I have these lists:
l1 = ["foo","bar","x","y","z","x","y","z","x","y","z"]
l2 = ["foo","bar","w","x","y","z","w","x","y","z","w","x","y","z"]
l3 = ["foo","bar","y","z","y","z","y","z"]
For each of the list above I'd like to get the indices of sequential chunks
from 3rd entry onwards. Yield:
l1_indices = [[2,3,4],[5,6,7],[8,9,10]]
l2_indices = [[2,3,4,5],[6,7,8,9],[10,11,12,13]]
l3_indices = [[2,3],[4,5],[6,7]]
To clarify further, I got l1_indices the following way:
["foo","bar", "x","y","z", "x","y","z", "x","y","z"]
0 1 2 3 4 5 6 7 8 9 10 <-- indices id
---> onwards
---> always in 3 chunks
What's the way to do it in Python?
I tried this but no avail:
In [8]: import itertools as IT
In [9]: import operator
In [11]: [list(zip(*g))[::-1] for k, g in IT.groupby(enumerate(l1[2:]), operator.itemgetter(1))]
Out[11]:
[[('x',), (0,)],
[('y',), (1,)],
[('z',), (2,)],
[('x',), (3,)],
[('y',), (4,)],
[('z',), (5,)],
[('x',), (6,)],
[('y',), (7,)],
[('z',), (8,)]]
If sequential elements are always in three chunks and always starts from third item then you can simply divide the remaining elements by three and generate indices list.
>>> def get_indices(l):
... last = len(l) - 2
... diff = last / 3
... return [range(i, i + diff) for i in range(2, last, diff)]
...
>>> get_indices(l1)
[[2, 3, 4], [5, 6, 7], [8, 9, 10]]
>>> get_indices(l2)
[[2, 3, 4, 5], [6, 7, 8, 9], [10, 11, 12, 13]]
>>> get_indices(l3)
[[2, 3], [4, 5]]
As a more general answer first of all you can find a sublist of your list that contain elements with length more than 1 , then based on its length and length of its set you can grub the desire indices :
>>> l =['foo', 'bar', 'w', 'x', 'y', 'z', 'w', 'x', 'y', 'z', 'w', 'x', 'y', 'z']
>>> s=[i for i in l if l.count(i)>2]
>>> len_part=len(l)-len(s)
>>> len_set=len(set(s))
>>> [range(i,i+l_s) for i in range(len_part,len(l),len_set)]
[[2, 3, 4, 5], [6, 7, 8, 9], [10, 11, 12, 13]]
Related
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])
I want to concatenate two matrices based on its matching string-values in a specific column. For example, I am trying to combine:
1 2 a
3 4 b
5 6 c
7 8 d
and
13 14 c
15 16 d
9 10 a
11 12 b
Such as:
1 2 9 10 a
3 4 11 12 b
5 6 13 14 c
7 8 15 16 d
Observe that the matrices aren't sorted in the same way order, but that I wish for the result to be sorted similar to the first one.
Thanks!
You don't have a matrix there, since a matrix or array (with NumPy) typically indicates numeric data only. Also, you are looking to merge data rather than concatenate. If you are happy to use a 3rd party library, this is possible with Pandas:
import pandas as pd
df1 = pd.DataFrame([[1, 2, 'a'], [3, 4, 'b'], [5, 6, 'c'], [7, 8, 'd']])
df2 = pd.DataFrame([[13, 14, 'c'], [15, 16, 'd'], [9, 10, 'a'], [11, 12, 'b']])
res = df1.merge(df2, on=2).values.tolist()
print(res)
[[1, 2, 'a', 9, 10],
[3, 4, 'b', 11, 12],
[5, 6, 'c', 13, 14],
[7, 8, 'd', 15, 16]]
l1 = [[1,2,'a'],[3,4,'b'],[5,6,'c'],[7,8,'d']]
l2 = [[13,14,'c'],[15,16,'d'],[9,10,'a'],[11,12,'b']]
l3 = sorted(l1, key=lambda x: x[2])
l4 = sorted(l2, key=lambda x: x[2])
l = list(zip(l3,l4))
z = [list(set(x + y)) for x, y in l]
[[1, 2, 9, 10, 'a'], [3, 4, 'b', 11, 12], [5, 6, 13, 14, 'c'], [7, 8, 15, 16, 'd']]
Not as elegant as Pandas (jpp answer), but another way using plain Lists and Dictionaries:
list_a=[[1,2,'a'],[3,4,'b'],[5,6,'c'],[7,8,'d']]
list_b=[[13,14,'c'],[15,16,'d'],[9,10,'a'],[11,12,'b']];
# ---------------------------------------
dict_result = {val[2]:val[0:2] for val in list_a}
for val in list_b:
dict_result[val[2]].extend(val[0:2])
# -----------------------------------------
result=[];
for key,val in dict_result.iteritems():
val.extend(key)
result.append([valout for valout in val]);
# ------------------------------------------
print result
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]]
Using nested lists like this:
N = [['D','C','A','B'],
[2,3,4,5],
[6,7,8,9]]
How could I swap two columns? for instance column C and column A.
With a for loop and a little help from this post:
Code:
N = [["D","C","A","B"],
[2,3,4,5],
[6,7,8,9]]
# Swap the last two columns
for item in N:
item[2], item[3] = item[3], item[2]
# Or as a function
def swap_columns(your_list, pos1, pos2):
for item in your_list:
item[pos1], item[pos2] = item[pos2], item[pos1]
Output:
swap_columns(N, 2, 3)
[['D', 'C', 'B', 'A'], [2, 3, 5, 4], [6, 7, 9, 8]]
Another possibility, using zip:
In [66]: N = [['D', 'C', 'A', 'B'], [2, 3, 4, 5], [6, 7, 8, 9]]
Transpose using zip:
In [67]: M = list(zip(*N))
Swap rows 1 and 2:
In [68]: M[1], M[2] = M[2], M[1]
Transpose again:
In [69]: N2 = list(zip(*M))
In [70]: N2
Out[70]: [('D', 'A', 'C', 'B'), (2, 4, 3, 5), (6, 8, 7, 9)]
The result is a list of tuples. If you need a list of lists:
In [71]: [list(t) for t in zip(*M)]
Out[71]: [['D', 'A', 'C', 'B'], [2, 4, 3, 5], [6, 8, 7, 9]]
This doesn't make the swap in-place. For that, see #DaveTucker's answer.
>>> N = [['D','C','A','B'],
... [2,3,4,5],
... [6,7,8,9]]
>>>
>>> lineorder = 0,2,1,3
>>>
>>> [[r[x] for x in lineorder] for r in N]
[['D', 'A', 'C', 'B'], [2, 4, 3, 5], [6, 8, 7, 9]]
If you don't want the order hardcoded, you can generate it easily like this
>>> lineorder = [N[0].index(x) for x in ['D','A','C','B']]
To create a copy of N with two columns swapped, S, in one line, You could do the following:
>>> N = [['D','C','A','B'],[2,3,4,5],[6,7,8,9]]
>>> S = [[n[0],n[2],n[1],n[3]] for n in N]
>>> S
[['D', 'A', 'C', 'B'], [2, 4, 3, 5], [6, 8, 7, 9]]
This assumes that each nested list of N are equal in size.
l = [1, 2]
emptl = []
for item in l:
empl.append([item[1], item[0]])
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]