I have a list of things, and I need to apply some processing on successive (and overlapping) groups of 3 elements:
I can do it with:
for i in range(len(things)-2):
process(things[i:i+3])
So for instance:
things=[0, 1, 2, 3, 4, 5, 6, 7]
And I want to process:
[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]
But is there a clever (but readable) way to do it without explicitly using len(things)?
Yes, what you're looking for is called a sliding/moving window. There are different ways to achieve this but the easiest is to use tee() and islice() functions from itertools. Using this you can define a window() function like below with default window size of 2.
import itertools
def window(iterable, n=2):
iters = itertools.tee(iterable, n)
for i, it in enumerate(iters):
next(itertools.islice(it, i, i), None)
return zip(*iters)
Then you can use it as
>>> things=[0, 1, 2, 3, 4, 5, 6, 7]
>>> list(window(things, n = 3))
[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
>>> for elem in window(things, n = 3):
... print(elem)
...
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, 5)
(4, 5, 6)
(5, 6, 7)
Edit: For one time use a more simpler option may be
>>> list(zip(things, things[1:], things[2:]))
[(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
Another way of doing it could be:
for i in things[0:-2]:
a=things.index(i)
process(things[a:a+3])
Let's try using enumerate, Here len(things[i : i+len_]) == len_ is to drop uneven sized list that get's accumulated at the end iterations.
len_ = 3
[things[i : i+len_] for i, j in enumerate(things) if len(things[i : i+len_]) == len_]
[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]
len_ = 4
[things[i : i+len_] for i, j in enumerate(things) if len(things[i : i+len_]) == len_]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]
I have a list such as
x=[1,2,4,5,8,9]
What I wanted to implement is to get a list which contains combination of 4 digits out of the list. For example: Output should be
[1,2,4,5]
[2,3,4,8]
.....
[1,3,5,8]
....
Hence, I believe it will be 4^6 solutions. I have tried itertools combination with no success
Itertools is actually the answer, because what you want are permutations of length N.
import itertools as it
x=[1,2,4,5,8,9]
print(list(it.permutations(x, 4)))
If order does not matter you should use itertools.combinations:
from itertools import combinations
x=[1,2,4,5,8,9]
for c in combinations(x, 4):
print(c)
This outputs:
(1, 2, 4, 5)
(1, 2, 4, 8)
(1, 2, 4, 9)
(1, 2, 5, 8)
(1, 2, 5, 9)
(1, 2, 8, 9)
(1, 4, 5, 8)
(1, 4, 5, 9)
(1, 4, 8, 9)
(1, 5, 8, 9)
(2, 4, 5, 8)
(2, 4, 5, 9)
(2, 4, 8, 9)
(2, 5, 8, 9)
(4, 5, 8, 9)
You can try this:
from itertools import combinations
x=[1,2,4,5,8,9]
comb = list(map(list, itertools.combinations(x, 4)))
print(comb)
[[1, 2, 4, 5], [1, 2, 4, 8], [1, 2, 4, 9], [1, 2, 5, 8], [1, 2, 5, 9], [1, 2, 8, 9], [1, 4, 5, 8], [1, 4, 5, 9], [1, 4, 8, 9], [1, 5, 8, 9], [2, 4, 5, 8], [2, 4, 5, 9], [2, 4, 8, 9], [2, 5, 8, 9], [4, 5, 8, 9]]
I have a fairly last list of data like this:
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I'm trying to zip it so that that I get something like this:
zipped_data = [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
I know I could do that with
l = [(data[0]), (data[1]), (data[2])]
zipped_data = zip(*l)
But I would like to write a list comprehension to do that for any number of items in data. I tried this, but it didn't work.
s = [zip(i) for i in data]
s
[[(1,), (2,), (3,)], [(4,), (5,), (6,)], [(7,), (8,), (9,)]]
Can anyone identify where I've gone wrong here? Thanks.
Try the *:
In [2]: lis=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [3]: zip(*lis)
Out[3]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
If you really want to rewrite zip as a list comprehension, then this is how I would do it:
In [25]: data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
In [26]: [tuple(lis[j] for lis in data) for j in range(min(len(l) for l in data))]
Out[26]: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
In [27]: data = [[1, 2, 3], [4, 5, 6], [7, 8]]
In [28]: [tuple(lis[j] for lis in data) for j in range(min(len(l) for l in data))]
Out[28]: [(1, 4, 7), (2, 5, 8)]
Though, zip(*data) is definitely a better way to go about this
I would do it with zip but here is done with list comprehension
def zip_lists(lists):
"""
Assuming all lists have the same length
>>> zip_lists([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
>>> zip_lists([[1, 2], [3, 4], [5, 6], [7, 8]])
[[1, 3, 5, 7], [2, 4, 6, 8]]
"""
return [[l[x] for l in lists] for x in range(len(lists[0]))]
This question already has answers here:
How to get the cartesian product of multiple lists
(17 answers)
Closed 8 months ago.
I'm basically looking for a python version of Combination of List<List<int>>
Given a list of lists, I need a new list that gives all the possible combinations of items between the lists.
[[1,2,3],[4,5,6],[7,8,9,10]] -> [[1,4,7],[1,4,8],...,[3,6,10]]
The number of lists is unknown, so I need something that works for all cases. Bonus points for elegance!
you need itertools.product:
>>> import itertools
>>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> list(itertools.product(*a))
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
The most elegant solution is to use itertools.product in python 2.6.
If you aren't using Python 2.6, the docs for itertools.product actually show an equivalent function to do the product the "manual" way:
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = map(tuple, args) * kwds.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
Simply use itertools.product:
listOLists = [[1,2,3],[4,5,6],[7,8,9,10]]
for l in itertools.product(*listOLists):
print(l)
Nothing wrong with straight up recursion for this task, no need for external dependencies, and if you need a version that works with strings, this might fit your needs:
combinations = []
def combine(terms, accum):
last = (len(terms) == 1)
n = len(terms[0])
for i in range(n):
item = accum + terms[0][i]
if last:
combinations.append(item)
else:
combine(terms[1:], item)
>>> a = [['ab','cd','ef'],['12','34','56']]
>>> combine(a, '')
>>> print(combinations)
['ab12', 'ab34', 'ab56', 'cd12', 'cd34', 'cd56', 'ef12', 'ef34', 'ef56']
Numpy can do it:
>>> import numpy
>>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> [list(x) for x in numpy.array(numpy.meshgrid(*a)).T.reshape(-1,len(a))]
[[ 1, 4, 7], [1, 5, 7], [1, 6, 7], ....]
One can use base python for this. The code needs a function to flatten lists of lists:
def flatten(B): # function needed for code below;
A = []
for i in B:
if type(i) == list: A.extend(i)
else: A.append(i)
return A
Then one can run:
L = [[1,2,3],[4,5,6],[7,8,9,10]]
outlist =[]; templist =[[]]
for sublist in L:
outlist = templist; templist = [[]]
for sitem in sublist:
for oitem in outlist:
newitem = [oitem]
if newitem == [[]]: newitem = [sitem]
else: newitem = [newitem[0], sitem]
templist.append(flatten(newitem))
outlist = list(filter(lambda x: len(x)==len(L), templist)) # remove some partial lists that also creep in;
print(outlist)
Output:
[[1, 4, 7], [2, 4, 7], [3, 4, 7],
[1, 5, 7], [2, 5, 7], [3, 5, 7],
[1, 6, 7], [2, 6, 7], [3, 6, 7],
[1, 4, 8], [2, 4, 8], [3, 4, 8],
[1, 5, 8], [2, 5, 8], [3, 5, 8],
[1, 6, 8], [2, 6, 8], [3, 6, 8],
[1, 4, 9], [2, 4, 9], [3, 4, 9],
[1, 5, 9], [2, 5, 9], [3, 5, 9],
[1, 6, 9], [2, 6, 9], [3, 6, 9],
[1, 4, 10], [2, 4, 10], [3, 4, 10],
[1, 5, 10], [2, 5, 10], [3, 5, 10],
[1, 6, 10], [2, 6, 10], [3, 6, 10]]
This mostly mimics solutions like Answer by Jarret Hardie using itertools.product, but has these distinctions:
this passes parameters to itertools.product in-line, instead of via variable a - so no *args syntax needed on the inline parameters
if your mypy type-linter acts like mine, and you can get your code to otherwise "work" with the *args syntax with inline product parameters (like product(*[[1,2,3],[4,5,6],[7,8,9,10]])), mypy might still fail it (with something like error: No overload variant of "product" matches argument type "List[object]")
So solution to that mypy, is to not use *args syntax, like this:
>>> import itertools
>>> list(itertools.product([1,2,3],[4,5,6],[7,8,9,10]))
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
This answer isn't as clean as using itertools but the ideas could be useful.
Drawing inspiration from the construction of zip() here, we could do the following.
>>> a = iter([[1,2,3],[4,5,6],[7,8,9,10]])
>>> sentinel = object()
>>> result = [[]]
>>> while True:
>>> l = next(a,sentinel)
>>> if l == sentinel:
>>> break
>>> result = [ r + [digit] for r in result for digit in l]
>>> print(result)
[[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 5, 10], [1, 6, 7], [1, 6, 8], [1, 6, 9], [1, 6, 10], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 4, 10], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 5, 10], [2, 6, 7], [2, 6, 8], [2, 6, 9], [2, 6, 10], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 4, 10], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 5, 10], [3, 6, 7], [3, 6, 8], [3, 6, 9], [3, 6, 10]]
We use a as an iterator in order to successively get the next item of it without needing to know how many there are a priori. The next command will output sentinel (which is an object created solely to make this comparison, see here for some explanation) when we run out of lists in a, causing the if statement to trigger so we break out of the loop.
from itertools import product
list_vals = [['Brand Acronym:CBIQ', 'Brand Acronym :KMEFIC'],['Brand Country:DXB','Brand Country:BH']]
list(product(*list_vals))
Output:
[('Brand Acronym:CBIQ', 'Brand Country :DXB'),
('Brand Acronym:CBIQ', 'Brand Country:BH'),
('Brand Acronym :KMEFIC', 'Brand Country :DXB'),
('Brand Acronym :KMEFIC', 'Brand Country:BH')]