All combinations of a list of lists [duplicate] - python

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')]

Related

Python zip list of tuples [duplicate]

This question already has answers here:
Transpose/Unzip Function (inverse of zip)?
(14 answers)
Closed 1 year ago.
How can I zip a list of tuples in Python?
# From
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
# To
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Just use zip and list comprehension.
>>> tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
>>> [list(i) for i in zip(*tuples)]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
Or,
>>> tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
>>> [[*i] for i in zip(*tuples)]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
If you are okay with using numpy then
import numpy as np
l=[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
[tuple(i) for i in (np.array(l).transpose())]

Iterating sublists without using the list length

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]]

Possible combination of a nested list in python

If I have a list of lists and want to find all the possible combination from each different indices, how could I do that?
For example:
list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
I want to find
all_possibility = [[1, 5, 9], [1, 8, 6], [4, 2, 9], [4, 8, 3], [7, 2, 6], [7, 5, 3]]
where
[1,5,9]: 1 is 1st element of [1, 2, 3], 5 is 2nd element of [4, 5, 6], 9 is 3rd element of [7, 8, 9].
[1,8,6]: 1 is 1st element of [1, 2, 3], 8 is 2nd element of [7, 8, 9], 6 is 3rd element of [4, 5, 6].
and so on.
(Edited) Note: I would like the result to be in the same order as the original element of the list. [1, 8, 6] but not [1, 6, 8] because 8 is the 2nd element of [7, 8, 9].
What you're looking for is the Cartesian product, in Python itertools.product:
>>> import itertools
>>> list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> all_possibility = list(itertools.product(*list_of_lists))
>>> print(all_possibility)
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 6, 7), (1, 6, 8),
(1, 6, 9), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 6, 7),
(2, 6, 8), (2, 6, 9), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 5, 7), (3, 5, 8), (3, 5, 9),
(3, 6, 7), (3, 6, 8), (3, 6, 9)]
If you want permutations based on the indices rather than the values, you can use itertools.combinations to get the possible indices, then use those indices to get the respective values from the sub-lists, like this:
>>> list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> length = 3
>>> all_indices = list(itertools.permutations(range(length), length))
>>> all_possibility = [[l[i] for l,i in zip(list_of_lists, indices)] for indices in all_indices]
>>> print(all_possibility)
[[1, 5, 9], [1, 6, 8], [2, 4, 9], [2, 6, 7], [3, 4, 8], [3, 5, 7]]
I have to consider the indices as well. For example, (1, 4, 7) is excluded because 1, and 4 both are the 1st element from the list of the lists (from [1, 2, 3] and [4, 5, 6]). And actually (1, 4, 7) all of them are from the first component of the nested list. I need the cases with all the different indices.
So you actually just want to get the possible permutations of a “list selector” for each index in the output, i.e. these are what you are trying to get:
>>> list(itertools.permutations(range(3), 3))
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
And once you have that, you just need to translate into your list_of_lists where you access each index from the specified sublist:
>>> [[list_of_lists[k][i] for i, k in enumerate(comb)] for comb in itertools.permutations(range(3), 3)]
[[1, 5, 9], [1, 8, 6], [4, 2, 9], [4, 8, 3], [7, 2, 6], [7, 5, 3]]
In a spirit of #poke's approach, here is the cases for number of elements differ than the number of the list in the lists. (Previously, there are 3 elements in individual list where 3 sub-lists in the list).
list_of_lists = [[1, 2], [3, 4], [5, 6], [7, 8]]
We expect every (0, 1) pairs from the list of lists, or
all_possibility = [[1, 4], [1, 6], [1, 8], [3, 2], [3, 6], [3, 8], \
[5, 2], [5, 4], [5, 8], [7, 2], [7, 4], [7, 6]]
The code:
permutation_cases = list(itertools.permutations(range(2), 2))
select_from = list(itertools.combinations(range(len(list_of_lists)), 2))
all_possibility = []
for selecting_index in select_from:
selected = [list_of_lists[i] for i in selecting_index ]
cases = list([selected[k][i] for i, k in enumerate(comb)] for comb in permutation_cases)
for item in cases:
all_possibility.append(item)
print(all_possibility)

What is the most elegant way of finding the n-length consecutive sub-lists of a list in Python?

Let's say I have some_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9] and I want to find all 3 element consecutive sub-lists: [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9].
What's the most elegant way of doing this?
>>> some_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l = [some_lst[i:i+3] for i in xrange(len(some_lst)-2)]
Another option is slices and zip:
>>> some_lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> zip(some_lst, some_lst[1:], some_lst[2:])
[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7), (6, 7, 8), (7, 8, 9)]
With itertools.islice and itertools.izip you can make it more memory-efficient:
from itertools import islice, izip
izip(islice(some_lst, 0, None),
islice(some_lst, 1, None),
islice(some_lst, 2, None))
or
izip(*[islice(some_lst, s, None) for s in range(3)])
yet another way:
subLists = map(lambda x: some_lst[x-1:x+2], some_lst[:-2])

Zip as a list comprehension

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]))]

Categories

Resources