Replacing an element in a list with multiple elements - python

I am trying to modify a list of two lists. For each of the two inside lists, I perform some operation and 'split' them into new lists.
Here is a simple example of what I'm trying to do:
[['a', 'b'], ['c', 'd']] --> [['a'], ['b'], ['c', 'd']]
Currently my algorithm passes ['a', 'b'] to a function that determines whether or not it should be split into [['a'], ['b']] (e.g. based on their correlations). The function returns [['a'], ['b']] which tells me that ['a', 'b'] should be split, or returns ['a', 'b'] (the original list) which indicates that it should not be split.
Currently I have something like this:
blist = [['a', 'b'], ['c', 'd']] #big list
slist = [['a'], ['b']] #small list returned by function
nlist = [items for i in xrange(len(blist)) for items in (slist if i==0 else blist[i])]
This produces [['a'], ['b'], 'c', 'd'] as opposed to the desired output [['a'], ['b'], ['c', 'd']] which does not alter the second list in the original blist. I understand why this is happening--my second loop is also applied to blist[1] in this case, but I am not sure how to fix it as I do not understand list comprehension completely.
A 'pythonic' solution is preferred.
Any feedback would be appreciated, thank you!
EDIT: Like the title suggests, I am trying to 'replace' ['a', 'b'] with ['a'], ['b']. So I would like the 'position' to be the same, having ['a'], ['b'] appear in the original list before ['c', 'd']
RESULTS
Thank you Christian, Paul and schwobaseggl for your solutions! They all work :)

Try
... else [blist[i]])]
to create a list of lists.

You can use slice assignment:
>> l1 = [[1, 2], [3, 4]]
>>> l2 = [[1], [2]]
>>> l1[0:1] = l2
>>> l1
[[1], [2], [3, 4]]
This changes l1, so if you want to keep it make a copy before.
Another way that doesn't change l1 is addition:
>> l1 = [[1, 2], [3, 4]]
>>> l3 = l2 + l1[1:]
>>> l3
[[1], [2], [3, 4]]

You could alter your split function to return structurally adequate lists. Then you can use a comprehension:
def split_or_not(l):
if condition: # split
return [l[:1], l[1:]]
return [l] # wrap in extra list
# using map
nlist = [x for sub_l in map(split_or_not, blist) for x in sub_l]
# or nested comprehension
nlist = [x for sub_l in (split_or_not(l) for l in blist) for x in sub_l]

Assuming you have the mentioned funtion that decides whether to split an item:
def munch(item):
if item[0] == 'a': # split
return [[item[0]], [item[1]]]
return [item] # don't split
You can use it in s simple for-loop.
nlist = []
for item in blist:
nlist.extend(munch(item))
"Pythonic" is whatever is easy to read and understand. Don't use list comprehensions just because you can.

Related

combining list of list with addition of value when list items are missing

I need to combine list of lists and add items when items are not present in other lists.
I need a way to represent when a value is not present in a list across three list of lists. In the example below ['e','f'] is only in list one (l1), so a placeholder would be added to list two (l2) and three (l3) to represent that it is in list one, but not list two and three. The placeholder would be something like ['e','-'].
l1 = [['a', 'b'],['e','f']]
l2 = [['a', 'b'],['c', 'd']]
l3 = [['a', 'b'],['c', 'd']]
So in the end, every list would have an entry for any list item that had a unique value in the first position of a list item.
l1 = [['a', 'b'],['c','-'],['e','f']
l2 = [['a', 'b'],['c','d'],['e','-']
l3 = [['a', 'b'],['c','d'],['e','-']
I tried converting the list of lists to sets and could find common objects, for example
l1_set = set(tuple(row) for row in l1)
l2_set = set(tuple(row) for row in l2)
l3_set = set(tuple(row) for row in l3)
print (ipl_set & vgda_set & odm_set)
set([('a', 'b')])
I am not sure how to manage the sets so I can find different values and modify the lists to include those different values, while keeping position in the list.
The order does matter. I don't want to simply append missing list items like this:
l1 = [['a', 'b'],['e','f'],['c','-']
l2 = [['a', 'b'],['c','d'],['e','-']
l3 = [['a', 'b'],['c','d'],['e','-']
The function add_missing_items() accepts any number (>0) of lists and add to them items that are missing from other lists. Assumes that items inside the lists can be sorted alphabetically:
l1 = [['a', 'b'],['e','f']]
l2 = [['a', 'b'],['c', 'd']]
l3 = [['a', 'b'],['c', 'd']]
def add_missing_items(*lists):
l_sets = [set(tuple(row) for row in l) for l in lists]
u = l_sets[0].union(*l_sets[1:])
for lst, slst in zip(lists, l_sets):
l = [list(v) for v in u.difference(slst)]
for missing_value in l:
missing_value[1::1] = '-' * (len(missing_value)-1)
lst[:] = sorted(lst + l)
add_missing_items(l1, l2, l3)
print(l1)
print(l2)
print(l3)
Prints:
[['a', 'b'], ['c', '-'], ['e', 'f']]
[['a', 'b'], ['c', 'd'], ['e', '-']]
[['a', 'b'], ['c', 'd'], ['e', '-']]
You can test if a couple in a list is in another list like this:
l1 = [['a', 'b'],['e','f']]
l2 = [['a', 'b'],['c', 'd']]
l3 = [['a', 'b'],['c', 'd']]
for couple in l1:
if couple not in l2:
print(couple)
In the case above, it's just an example to show how you can find elements that are different from l1. However, you can image modify an array instead of just make a print.
Hence, if you want to modify your arrays, I advise you to copy them and not modify directly original arrays.

Insert elements of a list as last elements into lists in list in python

A list of lists
list1 = [[1,'a'],[2,'b'],[3,'c']]
should be extended by
list2 = ['I','II','III']
so it becomes
list1 = [[1,'a','I'],[2,'b','II'],[3,'c','III']]
My attempt with
[i+j for i,j in zip(list1,list2)]
seems to be wrong. My current way is
list1 = [[1, 'a'], [2, 'b'], [3, 'c']]
list2 = ['I', 'II', 'III']
for x in range(0,len(list1)):
list1[x].append(list2[x])
This is not an elegant way. What is a recommendable pythonic way, maybe with zip?
Perhaps with the help of zip i.e
[i+[j] for i,j in zip(list1,list2)]
[[1, 'a', 'I'], [2, 'b', 'II'], [3, 'c', 'III']]
A variation of #Dark's solution:
list1 = [[1,'a'],[2,'b'],[3,'c']]
list2 = ['I','II','III']
list3 = [sublist + [list2[i]] for i, sublist in enumerate(list1)]
print(list3) # -> [[1, 'a', 'I'], [2, 'b', 'II'], [3, 'c', 'III']]
If you cannot guarantee that the 2 lists will have the same length, my approach would be slightly easier to tweak to keep it working.
list1 bigger than list2:
list3 = [sublist + [list2[i] if i < len(list2) else None] for i, sublist in enumerate(list1)]
list2 bigger than list1:
# no problem here
You do this and it's perfectly fine:
for i in range(len(list1)):
list1[i].append(list2[i])
Otherwise you could use that one liner:
[i+[list2[i[0] -1]] for i in list1]
But it's one of these things where anyone who will ever read your code won't understand in one read. I'd recommend the clearer first option that says what it does at first glance.

restructure lists of lists by iteration in Python

I have a 2D list or list of lists.
Input file is
A 58.76-65.9
B 58.76-65.9
C 58.76-65.9
A 24.8-62.8
I then created a list of lists:
with open("Input.txt", "r") as file:
raw = [[str(x) for x in line.split()] for line in file]
print (raw)
which returns
[['A', '58.76-65.9'], ['B', '58.76-65.9'], ['C', '58.76-65.9'], ['A', '24.8-62.8']]
My aim is to now create a new list of lists with a new structure. How can I obtain a new list of lits like this?
[['58.76-65.9', 'A', 'B', 'C'], ['A', '24.8-62.8']]
I first tried unioning sets, but that creates one large list and I need lists of lists. Therefore my plan is to (1) Create a new empty list of lists,
(2) iterate through the original list of lists,
(3) check if the 2nd element (i.e. 58.76-65.9) exists in the new list lists of lists. If it does not, extend both elements. If it does, just the first element (ie A)
# Defining empty list
matches=[]
# Accesing each row in the 2d list
for r in raw:
if r[1] not in matches[0][]:
matches.append([r[1], r[0]])
I realize that matches[0][] is not correct, what is the correct way to access it?
Use the grouping idiom:
>>> data = [['A', '58.76-65.9'], ['B', '58.76-65.9'], ['C', '58.76-65.9'], ['A', '24.8-62.8']]
>>> from collections import defaultdict
>>> grouper = defaultdict(list)
>>> for x, y in data:
... grouper[y].append(x)
...
>>> grouper
defaultdict(<class 'list'>, {'24.8-62.8': ['A'], '58.76-65.9': ['A', 'B', 'C']})
Now, I honestly think the above data-structure is much more practical, but you can easily convert into a list-of-lists if you really want:
>>> [[k] + v for k, v in grouper.items()]
[['24.8-62.8', 'A'], ['58.76-65.9', 'A', 'B', 'C']]
Or even nicer:
>>> [[k, *v] for k, v in grouper.items()]
[['24.8-62.8', 'A'], ['58.76-65.9', 'A', 'B', 'C']]
Just use the dictionary data structure. It does, what you want:
# Load data:
my_array = [[1 , 10], [2, 10], [3, 20]]
# Result as a dictionary:
result = {}
# Loop over data:
for value, key in my_array:
if key not in result:
# Create new list
result[key]=[]
result[key].append(value)
# If you really need a list of lists as output, do something like:
result_l = [list(elem) for elem in result.items()]
# (in python3)

How to reorganize sublists and exclude specific indexes in those sublists?

How can I reorganize sublists and exclude certain items from sublists to create a new list of sublists?
By reorganize I mean that I want to change the order of the items within each sublists across each sublist. For example moving every element at index 0 to index 1, and moving every element in index 2 to index 0 across every sublist. At the same time, I don't want to include index 1 in the original list of sublists.
Original_List = [['a','b','c'],['a','b','c'],['a','b','c']]
Desired_List = [['c','a'],['c','a'],['c','a']]
I currently have this function, which rearranges and pulls out different indexes from a sublist.
def Function(li):
return map(lambda x: (x[2] + "|" + x[0]).split("|"),li)
However, there are situations in which the sublists are much longer and there are more indexes that I want to pull out.
Rather than making this same function for 3 or 4 indexes like this for example:
def Function(li):
return map(lambda x: (x[2] + "|" + x[1] + "|" + x[0]).split("|"),li)
I'd like to use the *args, so that I can specify different amounts of indexes of the sublists to pull out. This is what I have so far, but I get a TypeError.
def Function(self,li,*args):
return map(lambda x: ([int(arg) + "|" for arg in args]).split("|"))
I get a TypeError, which I can understand but can't get around:
TypeError: string indices must be integers, not str
Perhaps there is a better and faster method entirely to rearrange sublists and exclude certain items within those sublists?
Also, it would be amazing if the function could deal with sub-sub-lists like this.
Original_List = [['a','b','c',['1','2','3']],['a','b','c',['1','2','3']],['a','b','c',['1','2','3']]]
Inputs that I'd like to achieve this:
[2] for c
[0] for a
[3][1] for '2'
Desired_List = [['c','a','2'],['c','a','2'],['c','a','2']]
I think what you are describing is this:
def sublist_indices(lst, *args):
return [[l[i] for i in args] for l in lst]
>>> sublist_indices([[1, 2, 3], [4, 5, 6]], 2, 0)
[[3, 1], [6, 4]]
If your sublists and sub-sublists contain all iterable items (e.g. strings, lists), you can use itertools.chain.from_iterable to flatten the sub-sublists, and then index in:
from itertools import chain
def sublists(lst, *args):
return [[list(chain.from_iterable(l))[i] for i in args] for l in lst]
e.g.
>>> lst = [['a', 'b', 'c', ['1', '2', '3']],
['a', 'b', 'c', ['1', '2', '3']],
['a', 'b', 'c', ['1', '2', '3']]]
>>> sublists(lst, 2, 0, 4)
[['c', 'a', '2'], ['c', 'a', '2'], ['c', 'a', '2']]
original = [['a','b','c'],['a','b','c'],['a','b','c']]
desired = [['c','a'],['c','a'],['c','a']]
def filter_indices(xs, indices):
return [[x[i] for i in indices if i < len(x)] for x in xs]
filter_indices(original, [2, 0])
# [['c', 'a'], ['c', 'a'], ['c', 'a']]
filter_indices(original, [2, 1, 0])
# [['c', 'b', 'a'], ['c', 'b', 'a'], ['c', 'b', 'a']]
I'm not sure what exactly you mean by "reorganize", but this nested list comprehension will take in a list of lists li and return a new list which contains the lists in li, but with the indices in args excluded.
def exclude_indices(li, *args):
return [[subli[i] for i in range(len(subli)) if i not in args] for subli in li]

Using append() on transient anonymous lists inside of a list comprehension in Python

I have a nested list that looks like this:
mylist = [['A;B', 'C'], ['D;E', 'F']]
I'd like to have it in the following form:
[['A', 'B', 'C'], ['D', 'E', 'F']]
Figured I'd write a simple list comprehension to do the task:
>>> newlist = [item[0].split(';').append(item[1]) for item in mylist]
>>> newlist
[None, None]
After some experimenting, I found that the error was in trying to use append() on anonymous lists:
>>> type(['A', 'B'])
<class 'list'>
>>> type(['A', 'B'].append('C'))
<class 'NoneType'>
Which seems like a gotcha, considering that you can do things like this:
>>> 'abc'.upper()
'ABC'
Obviously in most cases you could get around this by binding ['A', 'B'] to a variable before calling append(), but how would I make this work inside of a list comprehension? Furthermore, can anyone explain this unintuitive behavior?
[a.split(';') + [b] for a, b in mylist]
The problem is that you are storing the return value of the append() method, which is None.
One solution is to use itertools.chain() and store it in a list like so:
import itertools
mylist = [['A;B', 'C'], ['D;E', 'F']]
newlist = [list(itertools.chain(item[0].split(';'),item[1])) for item in mylist]
print newlist
prints:
[['A', 'B', 'C'], ['D', 'E', 'F']]
As you found out, mutating methods aren't of much use inside a list comprehension because the transient objects disappear immediately.
What works instead is to build-up a list through concatenation:
>>> mylist = [['A;B', 'C'], ['D;E', 'F']]
>>> [first.split(';') + [second] for first, second in mylist]
[['A', 'B', 'C'], ['D', 'E', 'F']]
As Will mentioned, the result of append() will be None, and you actually want the resulting list. Here is one option:
>>> mylist = [['A;B', 'C'], ['D;E', 'F']]
>>> mylist = [item[0].split(';') + [item[1]] for item in mylist]
>>> mylist
[['A', 'B', 'C'], ['D', 'E', 'F']]

Categories

Resources