I have a nested list
elements = [['A'],['B','C'],['D','E','F']]
and I have list of indexes
index= [1,2,3,4,5,6]
I want to group by the indexes meaning the output would look like this:
out = [[1],[2,3],[4,5,6]]
You can slice (with itertools.islice) relying on the length of each sublist:
from itertools import islice
vals = [['A'], ['B','C'], ['D','E','F']]
indices = iter([1,2,3,4,5,6])
slices = [list(islice(indices, len(sub_l))) for sub_l in vals]
print(slices)
[[1], [2, 3], [4, 5, 6]]
A simple approach would be to use iter to make index an iterable, then loop through the elements list and get the next element in the iterable.
iter_index = iter(index)
result = [[next(iter_index) for _ in group] for group in elements]
[[1], [2,3], [4,5,6]]
You could do it in a comprehension using an internal iterator:
result = [ [next(i) for _ in e] for i in [iter(index)] for e in elements ]
print(result)
[[1], [2, 3], [4, 5, 6]]
Alternatively, you could compute the starting position of each sub-list in the index list and use that in a comprehension to get the corresponding subset of indexes:
startPos = [0]
startPos.extend(len(e)+startPos[-1] for e in elements)
result = [ index[p:p+len(e)] for p,e in zip(startPos,elements) ]
print(result)
# [[1], [2, 3], [4, 5, 6]]
Using the same logic but based on the ending positions, you could use accumulate from itertools to compute the positions:
from itertools import accumulate
endPos = accumulate(map(len,elements))
result = [index[p-len(e):p] for p,e in zip(endPos,elements)]
print(result)
# [[1], [2, 3], [4, 5, 6]]
If it's ok to "destroy" the content of the index list in the process, you could write it like this:
result = [[index.pop(0) for _ in e] for e in elements]
print(result)
# [[1], [2, 3], [4, 5, 6]]
Related
How can I make the 1st element of list of lists to match the order of elements in another list? For example:
list1 = [3, 7, 1, 10, 4]
list2 = [[1,0],[3,2],[4,11],[7,9],[10,1]]
newlist = [[3,2],[7,9],[1,0],[10,1],[4,11]]
You can use list.index:
list1 = [3, 7, 1, 10, 4]
list2 = [[1, 0], [3, 2], [4, 11], [7, 9], [10, 1]]
newlist = sorted(list2, key=lambda l: list1.index(l[0]))
print(newlist)
Prints:
[[3, 2], [7, 9], [1, 0], [10, 1], [4, 11]]
If the lists are large, I'd suggest to create a mapping from list1 and use that in the sorting key function:
m = {v: i for i, v in enumerate(list1)}
newlist = sorted(list2, key=lambda l: m[l[0]])
print(newlist)
If the firsts in the second list are always a permutation of the first list (i.e., if that's not a misleading example):
firsts = next(zip(*list2))
first2full = dict(zip(firsts, list2))
newlist = [*map(first2full.get, list1)]
Try it online!
You can do that like this. But be careful when you have repititive first elements in list2 elements !
a = []
for i in range(len(list1)):
for j in range(len(list2)):
if list1[i] == list2[j][0]:
a.append(list2[j])
print(a)
For the sake of simplicity I will just use two lists.
So I have the following 2D lists:
> > a = [[1,2,3],
> > [1,2,3]]
> > b = [[4,5,6],
> > [4,5,6]]
And if I append list a and b, I'm looking to obtain the following:
masterlist = [[1,4], [2,5], [3,6], [1,4], [2,5], [3,6]]
The following code is what I have tried:
filenames = [a,b] #For the example, since I will have multiple arrays
masterlist = []
counter = 0
for file in filenames:
if counter == 0: #This if is to try to create lists within the list
for row in file: #This loops is to iterate throughout the whole list
for col in row:
c = [masterlist.append([col])]
[masterlist.append(c) for row, col in zip(masterlist, c)]
counter = 1
else: #This else is to append each element to their respective position
for row in file:
for col in row:
c = [masterlist.append(col)]
[masterlist.append([c]) for row, col in zip(masterlist, c)]
The output when printing masterlist is the following:
[[1], [2], [3], [1], [2], [3], [None], 4, 5, 6, 4, 5, 6, [[None]]]
I'm not sure where the [None]'s come from either. And as we can see '4,5,6...' aren't appended to the lists '[1], [2], [3]...' respectively.
You can iterate through the items of the lists and then add them to your masterlist:
a = [[1,2,3],
[1,2,3]]
b = [[4,5,6],
[4,5,6]]
masterlist = []
for aa,bb in zip(a,b): # loop over lists
for itema, itemb in zip(aa,bb): # loop over items in list
masterlist = masterlist + [[itema, itemb]]
output:
[[1, 4], [2, 5], [3, 6], [1, 4], [2, 5], [3, 6]]
If you use numpy,this is really easy
import numpy as np
a = np.array([[1,2,3],
[1,2,3]])
b = np.array([[4,5,6],
[4,5,6]])
fl = np.vstack(np.dstack((a,b)))
output
array([[1, 4],
[2, 5],
[3, 6],
[1, 4],
[2, 5],
[3, 6]])
A = [[1, 2, 4],
[3, 5, 6],
[7,8,9]]
def sumof(A,3):
We need to find the lowest sum of 3 elements
here 1 + 2 + 3 = 6 so output is (1,2,3)
Can we do by using zip
one basic solution, convert nested list into flat list, sort it,slice sorted list and sum:
A = [[1, 2, 4],
[3, 5, 6],
[7,8,9]]
def sumof(A, n):
# convert nested list into flat list
flat_list = [item for sublist in A for item in sublist]
return sum(sorted(flat_list)[:n])
print (sumof(A,3))
If you have a large array, you can do this without sorting the list,
which is a little faster like
from operator import add
from functools import reduce
A = [[1, 2, 4],
[3, 5, 6],
[7,8,9]]
addlists = lambda l: reduce(add, l)
list_A = addlists(A)
result = [list_A.pop(list_A.index(min(list_A))) for _ in range(3)]
It's a little more complicated, though the modules imported are really useful.
Suppose I have a numpy array like this:
arr = np.array([[1,1,1,1], [2,2,2,2], [3,3,3,3], [4,4,4,4])
I also have a list that determines the intended lengths:
lens = [1,2,3,4]
Is there an elegant and Pythonic way to return a new array, with each corresponding element selected using the lens variable?
The output should be:
[[1], [2,2],[3,3,3], [4,4,4,4]]
If each Numpy arr list element size is less than 5 this works.
Use zip:
[a[:i].tolist() for a, i in zip(arr, lens)]
Output:
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
This could be a possible solution:
res = []
for a,i in zip(arr, lens):
res.append(a[:i].tolist())
print(res)
Assuming arr is equal in length as lens:
[arr[i][:v].tolist() for i,v in enumerate(lens)]
Output: [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
Consider this list of lists:
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
I want to combine all the lists that have at least one number in common, that will be done iteratively until it finished and no dubletters will go with. The result will be:
combine(l) = [ [1,2,3,4], [5,6,7,8,9] ]
is there any neat way to do this, perhaps with itertools?
out = []
for l in lists:
for o in out:
if set(l).intersection(set(o)):
o[:] = list(set(l) + set(o)) # Mutate, don't reassign temp var
break
else:
out.append(l)
Not perfectly written, could be optimized and is not sorted, but should give you the idea of how to do it.
Maybe this?
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
a, b = [], map(set, l)
while len(a) != len(b):
a, b = b, []
for x in a:
for i, p in enumerate(b):
if p & x:
b[i] = p | x
break
else:
b.append(x)
print a
# [set([1, 2, 3, 4]), set([5, 6, 7, 8, 9])]
My naive attempt:
merge all tuples when their intersection is not empty
sort each tuple
remove duplicate tuples
repeat this until there are no more changes.
Example:
def combine_helper(l):
l = map(set, l)
for i, x in enumerate(l, 1):
x = set(x)
for y in l[i:]:
if x & y:
x = x | y
yield tuple(sorted(x))
def combine(l):
last_l = []
new_l = l
while last_l != new_l:
last_l = new_l
new_l = list(set(combine_helper(last_l)))
return map(list, last_l)
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
print combine(l)
Output:
$ python test.py
[[1, 2, 3, 4], [5, 6, 7, 8, 9]]
Its possible to introduce the following attempt:
Make a list of 1-element sets with individual values present in the list. This is the output list.
The l list is a recipe how items in the output list should be joined. e.g. If the output list is [{1}, {2}, {3}] and the first element in the l list is [1,3], then all sets containing 1 and 3 in the output list should be joined: output = [{1,3}, {2}].
Repeat step 2. for every item on the l list.
Code:
l = [ [1], [1], [1,2,3], [4,1], [5], [5], [6], [7,8,9], [7,6], [8,5] ]
def compose(l):
# the following will convert l into a list of 1-element sets:
# e.g. [ {1}, {2}, ... ]
r = sum(l, [])
r = map(lambda x: set([x]), set(r))
# for every item in l
# find matching sets in r and join them together
for item in map(set, l):
outside = [x for x in r if not x & item] # elements untouched
inside = [x for x in r if x & item] # elements to join
inside = set([]).union(*inside) # compose sets
r = outside + [inside]
return r
Example:
>>> compose(l)
[set([1, 2, 3, 4]), set([8, 9, 5, 6, 7])]
Recursion to the rescue! And don't forget reduce!
input_list = [ [1], [1], [1, 2, 3], [4, 1], [5], [5], [6], [7, 8, 9],
[7, 6], [8, 5] ]
def combine(input_list):
input_list = map(set, input_list) # working with sets has some advantages
reduced_list = reduce(combine_reduce, input_list, [])
if len(reduced_list) == len(input_list):
# return the whole thing in the original format (sorted lists)
return map(sorted, map(list, reduced_list))
else:
# recursion happens here
return combine(reduced_list)
def combine_reduce(reduced_list, numbers):
'''
find the set to add the numbers to or append as a new set.
'''
for sub_set in reduced_list:
if sub_set.intersection(numbers):
sub_set.update(numbers)
return reduced_list
reduced_list.append(numbers)
return reduced_list
print combine(input_list)
Prints out:
$ python combine.py
[[1, 2, 3, 4], [5, 6, 7, 8, 9]]
We have two things going on here. The first is reduce: I'm using it to cook the list down, by fitting each element into the resulting list somewhere or appending it, if that didn't work. This does not do the whole job, though, so we repeat this process (recursion!) until reducing does not provide a shorter list.
Also, use of set allows for the handy intersection method. You will notice the line with map(set, input_list) is redundant in recursion. Extracting a wrapper function combine from the inner function combine_inner and placing the formatting / unformatting (from list to set and back) in the outer function is left as an exercise.