Interleave multiple lists of the same length in Python [duplicate] - python

This question already has answers here:
Pythonic way to combine (interleave, interlace, intertwine) two lists in an alternating fashion?
(26 answers)
Closed 7 months ago.
In Python, is there a good way to interleave two lists of the same length?
Say I'm given [1,2,3] and [10,20,30]. I'd like to transform those into [1,10,2,20,3,30].

Having posted the question, I've realised that I can simply do the following:
[val for pair in zip(l1, l2) for val in pair]
where l1 and l2 are the two lists.
If there are N lists to interleave, then
lists = [l1, l2, ...]
[val for tup in zip(*lists) for val in tup]

For Python>=2.3, there's extended slice syntax:
>>> a = [0, 2, 4, 6, 8]
>>> b = [1, 3, 5, 7, 9]
>>> c = a + b
>>> c[::2] = a
>>> c[1::2] = b
>>> c
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
The line c = a + b is used as a simple way to create a new list of exactly the right length (at this stage, its contents are not important). The next two lines do the actual work of interleaving a and b: the first one assigns the elements of a to all the even-numbered indexes of c; the second one assigns the elements of b to all the odd-numbered indexes of c.

Given
a = [1, 2, 3]
b = [10, 20, 30]
c = [100, 200, 300, 999]
Code
Assuming lists of equal length, you can get an interleaved list with itertools.chain and zip:
import itertools
list(itertools.chain(*zip(a, b)))
# [1, 10, 2, 20, 3, 30]
Alternatives
itertools.zip_longest
More generally with unequal lists, use zip_longest (recommended):
[x for x in itertools.chain(*itertools.zip_longest(a, c)) if x is not None]
# [1, 100, 2, 200, 3, 300, 999]
Many lists can safely be interleaved:
[x for x in itertools.chain(*itertools.zip_longest(a, b, c)) if x is not None]
# [1, 10, 100, 2, 20, 200, 3, 30, 300, 999]
more_itertools+
A library that ships with the roundrobin itertools recipe, interleave and interleave_longest.
import more_itertools
list(more_itertools.roundrobin(a, b))
# [1, 10, 2, 20, 3, 30]
list(more_itertools.interleave(a, b))
# [1, 10, 2, 20, 3, 30]
list(more_itertools.interleave_longest(a, c))
# [1, 100, 2, 200, 3, 300, 999]
yield from
Finally, for something interesting in Python 3 (though not recommended):
list(filter(None, ((yield from x) for x in zip(a, b))))
# [1, 10, 2, 20, 3, 30]
list([(yield from x) for x in zip(a, b)])
# [1, 10, 2, 20, 3, 30]
+Install using pip install more_itertools

I needed a way to do this with lists of different sizes which the accepted answer doesn't address.
My solution uses a generator and its usage looks a bit nicer because of it:
def interleave(l1, l2):
iter1 = iter(l1)
iter2 = iter(l2)
while True:
try:
if iter1 is not None:
yield next(iter1)
except StopIteration:
iter1 = None
try:
if iter2 is not None:
yield next(iter2)
except StopIteration:
iter2 = None
if iter1 is None and iter2 is None:
raise StopIteration()
And its usage:
>>> a = [1, 2, 3, 4, 5]
>>> b = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> list(interleave(a, b))
[1, 'a', 2, 'b', 3, 'c', 4, 'd', 5, 'e', 'f', 'g']
>>> list(interleave(b, a))
['a', 1, 'b', 2, 'c', 3, 'd', 4, 'e', 5, 'f', 'g']

Alternative:
>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> [y for x in map(None,l1,l2) for y in x if y is not None]
[1, 10, 2, 20, 3, 30]
This works because map works on lists in parallel. It works the same under 2.2. By itself, with None as the called functions, map produces a list of tuples:
>>> map(None,l1,l2,'abcd')
[(1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (None, None, 'd')]
Then just flatten the list of tuples.
The advantage, of course, is map will work for any number of lists and will work even if they are different lengths:
>>> l1=[1,2,3]
>>> l2=[10,20,30]
>>> l3=[101,102,103,104]
>>> [y for x in map(None,l1,l2,l3) for y in x if y in not None]
[1, 10, 101, 2, 20, 102, 3, 30, 103, 104]

I like aix's solution best. here is another way I think should work in 2.2:
>>> x=range(3)
>>> x
[0, 1, 2]
>>> y=range(7,10)
>>> y
[7, 8, 9]
>>> sum(zip(x,y),[])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "tuple") to list
>>> sum(map(list,zip(x,y)),[])
[0, 7, 1, 8, 2, 9]
and one more way:
>>> a=[x,y]
>>> [a[i][j] for j in range(3) for i in (0,1)]
[0, 7, 1, 8, 2, 9]
and:
>>> sum((list(i) for i in zip(x,y)),[])
[0, 7, 1, 8, 2, 9]

A funny approach is to use heapq.merge with the position in the final list as key:
from heapq import merge
from itertools import count
a = [1,2,3]
b = [10,20,30]
counter = count()
res = list(merge(a, b, key=lambda x: next(counter)))
print(res)
Output
[1, 10, 2, 20, 3, 30]
For multiple list, you can just unpack them:
from heapq import merge
from itertools import count
a = [1, 2, 3]
b = [10, 20, 30]
c = [11, 21, 31]
counter = count()
res = list(merge(*[a, b, c], key=lambda x: next(counter)))
print(res)
Output
[1, 10, 11, 2, 20, 21, 3, 30, 31]

This is also a way to do it:
list1 = [1, 2, 3]
list2 = [10, 20, 30]
list(sum(zip(list1, list2), ()))
The idea is similar.
zip the lists together. (using zip)
flatten to a tuple (using sum(..., ())
convert to a list

[el for el in itertools.chain(*itertools.izip_longest([1,2,3], [4,5])) if el is not None]
As long as you don't have None that you want to keep

To answer the question's title of "Interleave multiple lists of the same length in Python", we can generalize the 2-list answer of #ekhumoro. This explicitly requires that the lists are the same length, unlike the (elegant) solution by #NPE
import itertools
def interleave(lists):
"""Interleave a list of lists.
:param lists: List of lists; each inner length must be the same length.
:returns: interleaved single list
:rtype: list
"""
if len(set(len(_) for _ in lists)) > 1:
raise ValueError("Lists are not all the same length!")
joint = list(itertools.chain(*lists))
for l_idx, li in enumerate(lists):
joint[l_idx::len(lists)] = li
return joint
Examples:
>>> interleave([[0,2,4], [1, 3, 5]])
[0, 1, 2, 3, 4, 5]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12]])
[0, 1, 10, 2, 3, 11, 4, 5, 12]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14, 15]])
[0, 1, 10, 13, 2, 3, 11, 14, 4, 5, 12, 15]
>>> interleave([[0,2,4], [1, 3, 5], [10, 11, 12], [13, 14]])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in interleave
ValueError: Lists are not all the same length!
>>> interleave([[0,2,4]])
[0, 2, 4]

Too late to the party, and there is plenty of good answers but I would also like to provide a simple solution using extend() method:
list1 = [1, 2, 3]
list2 = [10, 20, 30]
new_list = []
for i in range(len(list1)):
new_list.extend([list1[i], list2[i]])
print(new_list)
Output:
[1, 10, 2, 20, 3, 30]

Related

How to multiply each element of two lists in python? [duplicate]

how do I multiply lists together in python using a function? This is what I have:
list = [1, 2, 3, 4]
def list_multiplication(list, value):
mylist = []
for item in list:
for place in value:
mylist.append(item*value)
return mylist
So I want to use this to multiply list*list (1*1, 2*2, 3*3, 4*4)
So the output would be 1, 4, 9, and 16. How would I do this in python where the 2nd list could be anything?
Thanks
My favorite way is mapping the mul operator over the two lists:
from operator import mul
mul(2, 5)
#>>> 10
mul(3, 6)
#>>> 18
map(mul, [1, 2, 3, 4, 5], [6, 7, 8, 9, 10])
#>>> <map object at 0x7fc424916f50>
map, at least in Python 3, returns a generator. Hence if you want a list you should cast it to one:
list(map(mul, [1, 2, 3, 4, 5], [6, 7, 8, 9, 10]))
#>>> [6, 14, 24, 36, 50]
But by then it might make more sense to use a list comprehension over the zip'd lists.
[a*b for a, b in zip([1, 2, 3, 4, 5], [6, 7, 8, 9, 10])]
#>>> [6, 14, 24, 36, 50]
To explain the last one, zip([a,b,c], [x,y,z]) gives (a generator that generates) [(a,x),(b,y),(c,z)].
The for a, b in "unpacks" each (m,n) pair into the variables a and b, and a*b multiplies them.
You can use a list comprehension:
>>> t = [1, 2, 3, 4]
>>> [i**2 for i in t]
[1, 4, 9, 16]
Note that 1*1, 2*2, etc is the same as squaring the number.
If you need to multiply two lists, consider zip():
>>> L1 = [1, 2, 3, 4]
>>> L2 = [1, 2, 3, 4]
>>> [i*j for i, j in zip(L1, L2)]
[1, 4, 9, 16]
If you have two lists A and B of the same length, easiest is to zip them:
>>> A = [1, 2, 3, 4]
>>> B = [5, 6, 7, 8]
>>> [a*b for a, b in zip(A, B)]
[5, 12, 21, 32]
Take a look at zip on its own to understand how that works:
>>> zip(A, B)
[(1, 5), (2, 6), (3, 7), (4, 8)]
zip() would do:
[a*b for a,b in zip(lista,listb)]
zip is probably the way to go, as suggested by the other answers. That said, here's an alternative beginner approach.
# create data
size = 20
a = [i+1 for i in range(size)]
b = [val*2 for val in a]
a
>> [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20]
b
>> [ 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40]
def multiply_list_elems(list_one, list_two):
""" non-efficient method """
res = [] # initialize empty list to append results
if len(a) == len(b): # check that both lists have same number of elems (and idxs)
print("\n list one: \n", list_one, "\n list two: \n", list_two, "\n")
for idx in range(len(a)): # for each chronological element of a
res.append(a[idx] * b[idx]) # multiply the ith a and ith b for all i
return res
def efficient_multiplier(list_one, list_two):
""" efficient method """
return [a[idx] * b[idx] for idx in range(len(a)) if len(a) == len(b)]
print(multiply_list_elems(a, b))
print(efficient_multiplier(a, b))
both give:
>> [2, 8, 18, 32, 50, 72, 98, 128, 162, 200, 242, 288, 338, 392, 450, 512, 578, 648, 722, 800]
Yet another approach is using numpy, as suggested here.
Use numpy for this.
>>> import numpy as np
>>> list = [1,2,3,4]
>>> np.multiply(list, list)
array([ 1, 4, 9, 16])
If you prefer python lists:
>>> np.multiply(list, list).tolist()
[1, 4, 9, 16]
additionally, this also works for element-wise multiplication with a scalar.
>>> np.multiply(list, 2)
array([2, 4, 6, 8])

How to merge two lists into a new list sequantially? [duplicate]

This question already has answers here:
Interleave multiple lists of the same length in Python [duplicate]
(11 answers)
Closed 3 years ago.
I'm working with lists in Python 3.x.
I want to merge two lists:
list1 = [1, 2, 3, 4]
list2 = [7, 8, 9, 19]
Expected output like this:
list3 = [1, 7, 2, 8, 3, 9, 4, 19]
I am not allowed to use any advanced data structures and need to write in a pythonic way.
Simply we can use list comprehension like this:
list1 = [1, 2, 3, 4]
list2 = [7, 8, 9, 19]
list3 = [v for v1_v2 in zip(list1, list2) for v in v1_v2]
assert list3 == [1, 7, 2, 8, 3, 9, 4, 19]
For example:
from itertools import chain
list(chain(*zip(v1, v2)))
zip() the two lists together then flatten with itertools.chain.from_iterable():
>>> from itertools import chain
>>> list1 = [1,2,3,4]
>>> list2 = [7,8,9,19]
>>> list(chain.from_iterable(zip(list1, list2)))
[1, 7, 2, 8, 3, 9, 4, 19]
You can simply use reduce from functools over a sum of the two lists using zip
from functools import reduce
from operator import add
list1 = [1,2,3,4]
list2 = [7,8,9,19]
x = list(reduce(add, zip(list1, list2)))
x
[1, 7, 2, 8, 3, 9, 4, 19]
Try the below code:
list1 = [1, 2, 3, 4]
list2 = [7, 8, 9, 19]
new_list = []
for i in range(len(list1)):
new_list.extend([list1[i], list2[i]])
print(new_list)
Output:
[1, 7, 2, 8, 3, 9, 4, 19]

Mapping two list without looping

I have two lists of equal length. The first list l1 contains data.
l1 = [2, 3, 5, 7, 8, 10, ... , 23]
The second list l2 contains the category the data in l1 belongs to:
l2 = [1, 1, 2, 1, 3, 4, ... , 3]
How can I partition the first list based on the positions defined by numbers such as 1, 2, 3, 4 in the second list, using a list comprehension or lambda function. For example, 2, 3, 7 from the first list belongs to the same partition as they have corresponding values in the second list.
The number of partitions is known at the beginning.
You can use a dictionary:
>>> l1 = [2, 3, 5, 7, 8, 10, 23]
>>> l2 = [1, 1, 2, 1, 3, 4, 3]
>>> d = {}
>>> for i, j in zip(l1, l2):
... d.setdefault(j, []).append(i)
...
>>>
>>> d
{1: [2, 3, 7], 2: [5], 3: [8, 23], 4: [10]}
If a dict is fine, I suggest using a defaultdict:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for number, category in zip(l1, l2):
... d[category].append(number)
...
>>> d
defaultdict(<type 'list'>, {1: [2, 3, 7], 2: [5], 3: [8, 23], 4: [10]})
Consider using itertools.izip for memory efficiency if you are using Python 2.
This is basically the same solution as Kasramvd's, but I think the defaultdict makes it a little easier to read.
This will give a list of partitions using list comprehension :
>>> l1 = [2, 3, 5, 7, 8, 10, 23]
>>> l2 = [1, 1, 2, 1, 3, 4, 3]
>>> [[value for i, value in enumerate(l1) if j == l2[i]] for j in set(l2)]
[[2, 3, 7], [5], [8, 23], [10]]
A nested list comprehension :
[ [ l1[j] for j in range(len(l1)) if l2[j] == i ] for i in range(1, max(l2)+1 )]
If it is reasonable to have your data stored in numpy's ndarrays you can use extended indexing
{i:l1[l2==i] for i in set(l2)}
to construct a dictionary of ndarrays indexed by category code.
There is an overhead associated with l2==i (i.e., building a new Boolean array for each category) that grows with the number of categories, so that you may want to check which alternative, either numpy or defaultdict, is faster with your data.
I tested with n=200000, nc=20 and numpy was faster than defaultdict + izip (124 vs 165 ms) but with nc=10000 numpy was (much) slower (11300 vs 251 ms)
Using some itertools and operator goodies and a sort you can do this in a one liner:
>>> l1 = [2, 3, 5, 7, 8, 10, 23]
>>> l2 = [1, 1, 2, 1, 3, 4, 3]
>>> itertools.groupby(sorted(zip(l2, l1)), operator.itemgetter(0))
The result of this is a itertools.groupby object that can be iterated over:
>>> for g, li in itertools.groupby(sorted(zip(l2, l1)), operator.itemgetter(0)):
>>> print(g, list(map(operator.itemgetter(1), li)))
1 [2, 3, 7]
2 [5]
3 [8, 23]
4 [10]
This is not a list comprehension but a dictionary comprehension. It resembles #cromod's solution but preserves the "categories" from l2:
{k:[val for i, val in enumerate(l1) if k == l2[i]] for k in set(l2)}
Output:
>>> l1
[2, 3, 5, 7, 8, 10, 23]
>>> l2
[1, 1, 2, 1, 3, 4, 3]
>>> {k:[val for i, val in enumerate(l1) if k == l2[i]] for k in set(l2)}
{1: [2, 3, 7], 2: [5], 3: [8, 23], 4: [10]}
>>>

Python combining items in a list of lists python

I have three items in a list of lists:
test = [[a,b,c],[d,e,f],[g,h,i]]
I want it to look like this:
test = [[a,b,c,d,e,f],[g,h,i]]
what is the best way to do this in python?
Thanks for the help
>>> test = [[1,2,3], [4,5,6], [7,8,9]]
>>> test[0].extend(test.pop(1)) # OR test[0] += test.pop(1)
>>> test
[[1, 2, 3, 4, 5, 6], [7, 8, 9]]
test = [test[0] + test[1], test[2]]
If you want to flatten of an arbitrary slice, use a slice assignment and a list comprehension on the part you want to flatten.
This flatten from position n to end of the list:
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> n=2
>>> test[n:]=[[i for s in test[n:] for i in s]]
>>> test
[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]]
This flattens up to n (but including n):
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> test[0:n]=[[i for s in test[0:n] for i in s]]
>>> test
[[1, 2, 3, 4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
This flattens in the middle (from and including n to include the additional groups specified):
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> test[n:n+2]=[[i for s in test[n:n+2] for i in s]]
>>> test
[[1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15]]
Flatten all the sublists:
>>> test = [[1,2,3],[4,5,6],[7,8,9],[10,11,12],[13,14,15]]
>>> n=len(test)
>>> test[0:n]=[[i for s in test[0:n] for i in s]]
>>> test
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]
Note that in each case the slice on the assignment side is the same as the slice on the list comprehension side. This allows the use of the same slice object for each.
For the last case of flattening the whole list, obviously, you can just do test=[[i for s in test for i in s]] but the fact the logic is consistent allows you to wrap this in a function use a slice object.
You could combine the first two items in the list of list with the + operator and you should use '' for your strings
test = [['a','b','c'],['e','f','g'],['h','i','j']]
result = [test[0] + test[1], test[2]]
print result
output:
[['a', 'b', 'c', 'e', 'f', 'g'], ['h', 'i', 'j']]
Using the more_itertools package:
import more_itertools as mit
list(mit.chunked(mit.flatten(test), 6))
# [[1, 2, 3, 4, 5, 6], [7, 8, 9]]

Python: Building a list comprehension that grows a list

I want to write some python that will take an existing list, and create a new list containing two entries for every entry in the original.
Example: Every entry should produce two new entries: x+1, and 3x.
a = [1]
a = [2, 3]
a = [3, 6, 4, 9]
a = [4, 9, 7, 18, 5, 12, 10, 27]
What code could be entered to produce the desired output:
a = [1]
for i in range(3):
a = ???
I have tried:
a = [(x+1, 3*x) for x in a]
... but this was incorrect because the first iteration gives a list containing a single tuple:
a = [(2, 3)]
... and a subsequent iteration does not work.
In addition to the answer, some explanation into the thought process that produces the answer would be most helpful.
EDIT: If anyone can give me some insight as to why my question is receiving close votes, I would appreciate that as well.
Here is a pretty efficient solution that uses itertools.chain.from_iterable and a generator expression:
>>> from itertools import chain
>>> a = [1]
>>> list(chain.from_iterable((x+1, x*3) for x in a))
[2, 3]
>>> a = [2, 3]
>>> list(chain.from_iterable((x+1, x*3) for x in a))
[3, 6, 4, 9]
>>> a = [3, 6, 4, 9]
>>> list(chain.from_iterable((x+1, x*3) for x in a))
[4, 9, 7, 18, 5, 12, 10, 27]
>>>
The links provided should explain everything except the list(...) part. I did that so the results were lists and not something like <itertools.chain object at 0x01815370>.
Edit in response to comment:
Yes, you can chain as many chain objects as you want and then convert the whole thing to a list in the end. See a demonstration below:
>>> a = [3, 6, 4, 9]
>>> list(chain.from_iterable((chain.from_iterable((x+1, x*3) for x in a), chain.from_iterable((x+1, x*3) for x in a))))
[4, 9, 7, 18, 5, 12, 10, 27, 4, 9, 7, 18, 5, 12, 10, 27]
>>>
def somefunc(n):
if not n:
return [1]
else:
return list(itertools.chain.from_iterable([(i+1, 3*i) for i in somefunc(n-1)]))
Output:
In [20]: somefunc(3)
Out[20]: [4, 9, 7, 18, 5, 12, 10, 27]
In [21]: somefunc(2)
Out[21]: [3, 6, 4, 9]
In [22]: somefunc(1)
Out[22]: [2, 3]

Categories

Resources