Python merging list of lists with varying length - python

I want to write a function that merges a list of lists. The lists can be of varying lengths.
selected_cells = [[[1],[2],[3]], [[4],[5],[6]], [[7],[8]]]
My expected output would be [[1,4,7], [2,5,8], [3,6]]
I've written the following function
def merge(lst1, lst2):
print('Merging', lst1, 'and', lst2)
return [(sub + [lst2[i][-1]]) for i, sub in enumerate(lst1) if i < len(lst2)]
for idx, cell in enumerate(selected_cells):
if idx < (len(selected_cells)-1):
print(idx)
selected_cells[0] = merge(selected_cells[0], selected_cells[idx+1])
print('Output', selected_cells[0])
However this outputs [[1, 4, 7], [2, 5, 8]]
I feel like I'm missing something simple here and it's driving me nuts. Any help would be appreciated

As written in comments, you can use zip_longest from itertools module.
>>> selected_cells = [[[1],[2],[3]], [[4],[5],[6]], [[7],[8]]]
>>> from itertools import zip_longest
>>> L = list(zip_longest(*selected_cells))
>>> L
[([1], [4], [7]), ([2], [5], [8]), ([3], [6], None)]
And then flatten the tuples and remove the None values:
>>> [[x[0] for x in t if x] for t in L]
[[1, 4, 7], [2, 5, 8], [3, 6]]
Another option is to use a fold (functools.reduce):
>>> selected_cells = [[[1],[2],[3]], [[4],[5],[6]], [[7],[8]]]
>>> import functools
>>> functools.reduce(lambda acc, x: [acc[i] + (x[i] if i < len(x) else []) for i in range(len(acc))], selected_cells)
[[1, 4, 7], [2, 5, 8], [3, 6]]
That's perhaps less intuitive, though.

You can use zip_longest as stated in the comments, because zip will stop at the shortest iterable, for example:
from itertools import zip_longest
selected_cells = [[[1],[2],[3]], [[4],[5],[6]], [[7],[8]]]
output = [
[cell[0] for cell in row if cell is not None]
for row in zip_longest(*selected_cells)
]
print(output)
>>> [[1, 4, 7], [2, 5, 8], [3, 6]]

Related

How to sort items in nested list in ascending order in Python?

I have a list of numbers in nested lists. I need to sort those in ascending order, while keeping it in nested lists. I guess regrouping them in new nested lists.
I have following function:
def sorting(n):
for i in n:
for e in range(1, len(i)):
key = i[1]
j = e - 1
while j >= 0 and key < i[j]:
i[j + 1] = i[j]
j -= 1
i[j + 1] = key
for i in range(len(n)):
return(n)
n = [[6, 2], [9, 5], [3, 7], [1, 8]]
print(sorting(n))
But it does not sort the list correctly. I get this:
[[2, 6], [5, 9], [3, 7], [1, 8]]
But it should regroup the nested lists in the following way:
[[1, 2], [3, 5], [6, 7], [8, 9]]
How can I edit my code to make this work?
Thanks for helping
One way to solve this would be to flatten the list, sort it, and then reassemble into a list of pairs:
n = [[6, 2], [9, 5], [3, 7], [1, 8]]
# flatten list and sort
f = sorted(i for l in n for i in l)
# put it back together into nested pairs
n = [[v1, v2] for v1, v2 in zip(f[0::2], f[1::2])]
print(n)
Output:
[[1, 2], [3, 5], [6, 7], [8, 9]]
Indeed, you should first flatten the list, then sort, then re-pair:
n = [[6, 2], [9, 5], [2, 7], [1, 8]]
flatlist = [ x for pair in n for x in pair ]
flatlist.sort()
result = [ list(t) for t in zip (flatlist[0::2], flatlist[1::2]) ]
The other solutions offer good ways to do this problem.
Here is an itertools solution that flattens the list with itertools.chain.from_iterable using flatten(), sort the numbers with sorted(), then uses grouper() to chunk the result into length 2 pairs, and finally converts the result to a list of lists with map().
The flatten() and grouper() methods are from Itertools Recipes.
from itertools import chain, zip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
def flatten(list_of_lists):
"Flatten one level of nesting"
return chain.from_iterable(list_of_lists)
lst = [[6, 2], [9, 5], [3, 7], [1, 8]]
result = list(map(list, grouper(sorted(flatten(lst)), 2)))
print(result)
Another thing to point out is you don't need to copy these methods into your code. You can simply install the more-itertools package with pip install more-itertools, import it, then start using the methods.
Here is what it would look like:
from more_itertools import grouper, flatten
lst = [[6, 2], [9, 5], [3, 7], [1, 8]]
result = list(map(list, grouper(sorted(flatten(lst)), 2)))
print(result)
Output:
[[1, 2], [3, 5], [6, 7], [8, 9]]

Is there a way to combine a list like this?

assume that a and b are list.
a = [[1], [2]]
b = [[5, 6, 7], [3, 4, 5]]
I want to get a list which is
[[1,5,6,7], [2,3,4,5]]
Is there any way to do that effectively? Either lists or numpy array is OK.
zip is your friend:
>>> a = [[1], [2]]
>>> b = [[5, 6, 7], [3, 4, 5]]
>>> [x+y for x, y in zip(a, b)]
[[1, 5, 6, 7], [2, 3, 4, 5]]
You can also use map; the operator module provides a ready-made definition of lambda x,y: x + y for such uses.
>>> import operator
>>> list(map(operator.add, a, b))

How to zip two lists where one list contains just one item

I have two lists:
list1 = [1,2,3]
and
list2 = [4]
and I need to combine them to produce the following output:
list3 = [[1,4],[2,4],[3,4]]
itertools doesn't seem to have a method to accomplish this, the zip function ends when the second list does.
I'm sure there's a one-liner out there, but I'm finding too much stuff about similar but not the same problems on here and google.
You can iterate over the list and concatenate the list2 value and the element for the current iteration:
list1 = [1,2,3]
list2 = [4]
new_list = [[a]+list2 for a in list1]
Output:
[[1, 4], [2, 4], [3, 4]]
Or, an alternative, although lower solution using map:
final_list = map(lambda x:[x, list2[0]], list1)
Output:
[[1, 4], [2, 4], [3, 4]]
You can use itertools izip_longest (py2) or itertools zip longest (py3) too:
import itertools
list(map(list,itertools.izip_longest([], list1, fillvalue=list2[0])))
Returns:
[[4, 1], [4, 2], [4, 3]]
Do you need a third alternative?
>>> list(map(list,zip(list2 * len(list1), list1)))
[[4, 1], [4, 2], [4, 3]]
You can also try using itertools.cycle():
>>> import itertools
>>> list1 = [1,2,3]
>>> list2 = [4]
>>> print([list(x) for x in zip(list1, itertools.cycle(list2))])
[[1, 4], [2, 4], [3, 4]]
Are you looking for something like this?
Without any external module or heavy code:
print(list(map(lambda x:[x,list2[0]],list1)))
when data is :
list1 = [1,2,3]
list2 = [4]
output:
[[1, 4], [2, 4], [3, 4]]
As someone pointed out this is already given answer ,Here is another solution:
list1 = [1,2,3]
list2 = [4]
print(list(zip(list1,list2*len(list1))))
output:
[(1, 4), (2, 4), (3, 4)]

Splitting nested list of floats to columns [duplicate]

Let's take:
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
The result I'm looking for is
r = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
and not
r = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
Python 3:
# short circuits at shortest nested list if table is jagged:
list(map(list, zip(*l)))
# discards no data if jagged and fills short nested lists with None
list(map(list, itertools.zip_longest(*l, fillvalue=None)))
Python 2:
map(list, zip(*l))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Explanation:
There are two things we need to know to understand what's going on:
The signature of zip: zip(*iterables) This means zip expects an arbitrary number of arguments each of which must be iterable. E.g. zip([1, 2], [3, 4], [5, 6]).
Unpacked argument lists: Given a sequence of arguments args, f(*args) will call f such that each element in args is a separate positional argument of f.
itertools.zip_longest does not discard any data if the number of elements of the nested lists are not the same (homogenous), and instead fills in the shorter nested lists then zips them up.
Coming back to the input from the question l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], zip(*l) would be equivalent to zip([1, 2, 3], [4, 5, 6], [7, 8, 9]). The rest is just making sure the result is a list of lists instead of a list of tuples.
Equivalently to Jena's solution:
>>> l=[[1,2,3],[4,5,6],[7,8,9]]
>>> [list(i) for i in zip(*l)]
... [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
One way to do it is with NumPy transpose. For a list, a:
>>> import numpy as np
>>> np.array(l).T.tolist()
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Or another one without zip (python < 3):
>>> map(list, map(None, *l))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Or for python >= 3:
>>> list(map(lambda *x: list(x), *l))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
just for fun, valid rectangles and assuming that m[0] exists
>>> m = [[1,2,3],[4,5,6],[7,8,9]]
>>> [[row[i] for row in m] for i in range(len(m[0]))]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Methods 1 and 2 work in Python 2 or 3, and they work on ragged, rectangular 2D lists. That means the inner lists do not need to have the same lengths as each other (ragged) or as the outer lists (rectangular). The other methods, well, it's complicated.
the setup
import itertools
import six
list_list = [[1,2,3], [4,5,6, 6.1, 6.2, 6.3], [7,8,9]]
method 1 — map(), zip_longest()
>>> list(map(list, six.moves.zip_longest(*list_list, fillvalue='-')))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]
six.moves.zip_longest() becomes
itertools.izip_longest() in Python 2
itertools.zip_longest() in Python 3
The default fillvalue is None. Thanks to #jena's answer, where map() is changing the inner tuples to lists. Here it is turning iterators into lists. Thanks to #Oregano's and #badp's comments.
In Python 3, pass the result through list() to get the same 2D list as method 2.
method 2 — list comprehension, zip_longest()
>>> [list(row) for row in six.moves.zip_longest(*list_list, fillvalue='-')]
[[1, 4, 7], [2, 5, 8], [3, 6, 9], ['-', 6.1, '-'], ['-', 6.2, '-'], ['-', 6.3, '-']]
The #inspectorG4dget alternative.
method 3 — map() of map() — broken in Python 3.6
>>> map(list, map(None, *list_list))
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]]
This extraordinarily compact #SiggyF second alternative works with ragged 2D lists, unlike his first code which uses numpy to transpose and pass through ragged lists. But None has to be the fill value. (No, the None passed to the inner map() is not the fill value. It means there is no function to process each column. The columns are just passed through to the outer map() which converts them from tuples to lists.)
Somewhere in Python 3, map() stopped putting up with all this abuse: the first parameter cannot be None, and ragged iterators are just truncated to the shortest. The other methods still work because this only applies to the inner map().
method 4 — map() of map() revisited
>>> list(map(list, map(lambda *args: args, *list_list)))
[[1, 4, 7], [2, 5, 8], [3, 6, 9]] // Python 2.7
[[1, 4, 7], [2, 5, 8], [3, 6, 9], [None, 6.1, None], [None, 6.2, None], [None, 6.3, None]] // 3.6+
Alas the ragged rows do NOT become ragged columns in Python 3, they are just truncated. Boo hoo progress.
Three options to choose from:
1. Map with Zip
solution1 = map(list, zip(*l))
2. List Comprehension
solution2 = [list(i) for i in zip(*l)]
3. For Loop Appending
solution3 = []
for i in zip(*l):
solution3.append((list(i)))
And to view the results:
print(*solution1)
print(*solution2)
print(*solution3)
# [1, 4, 7], [2, 5, 8], [3, 6, 9]
import numpy as np
r = list(map(list, np.transpose(l)))
One more way for square matrix. No numpy, nor itertools, use (effective) in-place elements exchange.
def transpose(m):
for i in range(1, len(m)):
for j in range(i):
m[i][j], m[j][i] = m[j][i], m[i][j]
Maybe not the most elegant solution, but here's a solution using nested while loops:
def transpose(lst):
newlist = []
i = 0
while i < len(lst):
j = 0
colvec = []
while j < len(lst):
colvec.append(lst[j][i])
j = j + 1
newlist.append(colvec)
i = i + 1
return newlist
more_itertools.unzip() is easy to read, and it also works with generators.
import more_itertools
l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r)) # a list of lists
or equivalently
import more_itertools
l = more_itertools.chunked(range(1,10), 3)
r = more_itertools.unzip(l) # a tuple of generators.
r = list(map(list, r)) # a list of lists
matrix = [[1,2,3],
[1,2,3],
[1,2,3],
[1,2,3],
[1,2,3],
[1,2,3],
[1,2,3]]
rows = len(matrix)
cols = len(matrix[0])
transposed = []
while len(transposed) < cols:
transposed.append([])
while len(transposed[-1]) < rows:
transposed[-1].append(0)
for i in range(rows):
for j in range(cols):
transposed[j][i] = matrix[i][j]
for i in transposed:
print(i)
Just for fun: If you then want to make them all into dicts.
In [1]: l = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
...: fruits = ["Apple", "Pear", "Peach",]
...: [dict(zip(fruits, j)) for j in [list(i) for i in zip(*l)]]
Out[1]:
[{'Apple': 1, 'Pear': 4, 'Peach': 7},
{'Apple': 2, 'Pear': 5, 'Peach': 8},
{'Apple': 3, 'Pear': 6, 'Peach': 9}]
Here is a solution for transposing a list of lists that is not necessarily square:
maxCol = len(l[0])
for row in l:
rowLength = len(row)
if rowLength > maxCol:
maxCol = rowLength
lTrans = []
for colIndex in range(maxCol):
lTrans.append([])
for row in l:
if colIndex < len(row):
lTrans[colIndex].append(row[colIndex])
#Import functions from library
from numpy import size, array
#Transpose a 2D list
def transpose_list_2d(list_in_mat):
list_out_mat = []
array_in_mat = array(list_in_mat)
array_out_mat = array_in_mat.T
nb_lines = size(array_out_mat, 0)
for i_line_out in range(0, nb_lines):
array_out_line = array_out_mat[i_line_out]
list_out_line = list(array_out_line)
list_out_mat.append(list_out_line)
return list_out_mat

sort a matrix using its value in python

For example I have a matrix
a = [[6,8,9],[7,4,3],[1,2,5]]
now I want to sort matrix like as below
a = [[1,2,3],[4,5,6],[7,8,9]]
please help me I am new to python.
For list-of-list, you can clone the shape like this
>>> from itertools import chain
>>> a = [[6,8,9],[7,4,3],[1,2,5]]
>>> it = iter(sorted(chain.from_iterable(a)))
>>> [[next(it) for j in i] for i in a]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Use chain.from_iterable
>>> a = [[6,8,9],[7,4,3],[1,2,5]]
>>> from itertools import chain
>>> l = sorted(chain.from_iterable(a))
>>> [l[i:i+3] for i in range(0, len(l),3)]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Categories