Related
I have two lists:
l1 = ['a', 'b', 'c']
l2 = ['e', 'f', 'g']
And I want to generate all possible combinations of their pairings
Desired Output:
What is the best way to do it?
Right now I have written this code which works but seems highly inefficient.
You only need to permute the second list:
from itertools import permutations
l1 = ['a', 'b', 'c']
l2 = ['e', 'f', 'g']
pairs = [tuple(zip(l1, p)) for p in permutations(l2)]
print(pairs)
Output:
[(('a', 'e'), ('b', 'f'), ('c', 'g')),
(('a', 'e'), ('b', 'g'), ('c', 'f')),
(('a', 'f'), ('b', 'e'), ('c', 'g')),
(('a', 'f'), ('b', 'g'), ('c', 'e')),
(('a', 'g'), ('b', 'e'), ('c', 'f')),
(('a', 'g'), ('b', 'f'), ('c', 'e'))]
The easiest way to think about this is that each set of output values is the result of pairing the elements of the first list with some permutation of the elements of the second list. So you just need to generate all possible permutations if n elements, then use that as a permutation of the elements of the second list, pairing them with the elements of the first list.
The most flexible way to implement it is by defining a simple generator function:
from itertools import permutations
def gen_perm_pairs(l1, l2):
cnt = len(l1)
assert len(l2) == cnt
for perm in permutations(range(cnt)):
yield tuple((l1[i], l2[perm[i]]) for i in range(cnt))
You can then call it to generate the desired results:
l1 = ['a', 'b', 'c']
l2 = ['e', 'f', 'g']
for pairs in gen_perm_pairs(l1, l2):
print(pairs)
This produces the following:
(('a', 'e'), ('b', 'f'), ('c', 'g'))
(('a', 'e'), ('b', 'g'), ('c', 'f'))
(('a', 'f'), ('b', 'e'), ('c', 'g'))
(('a', 'f'), ('b', 'g'), ('c', 'e'))
(('a', 'g'), ('b', 'e'), ('c', 'f'))
(('a', 'g'), ('b', 'f'), ('c', 'e'))
I need to reduce time of executing my python's script which generate a list of some combinations. A brief explanation of the problem:
There are two lists:
char_list = ['a','b','c','d','e','f','g','h']
n_list = [3,2,1,2]
The goal is to create one collection (list, tuple or whatever you want) of all possible combinations of characters from char_list with length and order according to pattern in n_list. One example out of 1680 possible:
(('a', 'd', 'e'), ('h', 'c'), ('b',), ('d', 'f'))
All combinations in collection must look like this one above, the only thing which will be changing is the place of particular characters. And this is where the difficulties begin because there are some rules which cannot be omitted:
there can't be duplicates of characters in each combination (each
character must occur only once in combination)
combinations with changed order of characters in tuples which are on the same
place as the previous ones also are treated as duplicates (this rule
is more complicated so let me show you example):
let's say we have thousands of combinations in our collection and suddenly we notice four that are looking almost same:
(('a', 'c', 'e'), ('b', 'd'), ('g',), ('f', 'h'))
(('a', 'c', 'e'), ('h', 'f'), ('g',), ('d', 'b'))
(('a', 'c', 'e'), ('f', 'h'), ('g',), ('b', 'd'))
(('a', 'e', 'c'), ('h', 'f'), ('g',), ('d', 'b'))
only two of them are correct (can belong to our collection, btw this situation means that the entire collection is wrong because among these four combinations there are two wrong) Which ones? First one is fine (at least for the purpose of this example) but in the case of the next three only one of them is fine and this is the first one (first among these three, second if we count from the beginning of all 4 combinations) because it show up before the next two in whole collection. Why third and fourth combination is not unique? Because the place of tuples with particular order of characters within them hasn't changed; only characters switched places but only in particular tuples and this not what makes whole combination unique. Look once again at the tuples of the first and third combinations. They are same. But the order of these tuples is different. The one (order) of the first one is unique in regard to others.
My approach to this coding problem:
import itertools as iter
char_list = ['a','b','c','d','e','f','g','h']
n_list = [3,2,1,2]
###this line creates a list of all possible combinations of characters within tuples###
char_comb_in_tuples = list(iter.chain(*[list(iter.combinations(char_list,n)) for n in n_list]))
### this is list in which the appropriate combinations will be appended###
list_of_good_combinations = []
###for loop for looping over all possible combinations of tuples from 'char_comb_in_tuples'###
for combination in iter.combinations(char_comb_in_tuples,4):
###filtering only these combinations with appropriate pattern from n_list (3,2,1,2)###
if len([tuple for n_list_number, tuple in zip(n_list, combination) if n_list_number ==len(tuple)])==4:
###filtering only these combinations with no character duplicates###
if len(list(iter.chain(*combination))) != len(set(list(iter.chain(*combination)))):
pass
else:
###appending right combination to final list###
list_of_good_combinations.append(combination)
else:
pass
You can use a recursive function that picks the number of items from the list specified in the first of the given partitions with itertools.combinations, subtract the picked items from the item pool, pass them to the next recursive call with the rest of the partitions, and merge the returning combinations with each of the currently picked combination for the first partition. For efficient subtraction of items from a pool, you can convert the given list to a set first:
from itertools import combinations
def partitioned_combinations(s, partitions):
if partitions:
for combination in combinations(s, r=partitions[0]):
for combinations in partitioned_combinations(s.difference(combination), partitions[1:]):
yield (combination, *combinations)
else:
yield ()
so that:
list(partitioned_combinations(set(char_list), n_list))
would return 1680 tuples in a list:
[(('a', 'e', 'f'), ('c', 'd'), ('g',), ('h', 'b')),
(('a', 'e', 'f'), ('c', 'd'), ('b',), ('h', 'g')),
(('a', 'e', 'f'), ('c', 'd'), ('h',), ('g', 'b')),
(('a', 'e', 'f'), ('c', 'g'), ('d',), ('b', 'h')),
(('a', 'e', 'f'), ('c', 'g'), ('b',), ('d', 'h')),
(('a', 'e', 'f'), ('c', 'g'), ('h',), ('d', 'b')),
(('a', 'e', 'f'), ('c', 'b'), ('d',), ('g', 'h')),
(('a', 'e', 'f'), ('c', 'b'), ('g',), ('d', 'h')),
(('a', 'e', 'f'), ('c', 'b'), ('h',), ('d', 'g')),
(('a', 'e', 'f'), ('c', 'h'), ('d',), ('g', 'b')),
...
Note that sets are unordered in Python so the result of this approach will not be in a definite order. If you do want them to be in order, however, you can install the ordered-set module, so that:
from ordered_set import OrderedSet
list(partitioned_combinations(OrderedSet(char_list), n_list))
returns:
[(('a', 'b', 'c'), ('d', 'e'), ('f',), ('g', 'h')),
(('a', 'b', 'c'), ('d', 'e'), ('g',), ('f', 'h')),
(('a', 'b', 'c'), ('d', 'e'), ('h',), ('f', 'g')),
(('a', 'b', 'c'), ('d', 'f'), ('e',), ('g', 'h')),
(('a', 'b', 'c'), ('d', 'f'), ('g',), ('e', 'h')),
(('a', 'b', 'c'), ('d', 'f'), ('h',), ('e', 'g')),
(('a', 'b', 'c'), ('d', 'g'), ('e',), ('f', 'h')),
(('a', 'b', 'c'), ('d', 'g'), ('f',), ('e', 'h')),
(('a', 'b', 'c'), ('d', 'g'), ('h',), ('e', 'f')),
(('a', 'b', 'c'), ('d', 'h'), ('e',), ('f', 'g')),
...
This question already has answers here:
How to calculate a Cartesian product of a list with itself [duplicate]
(2 answers)
Closed 2 years ago.
I would like to get all combinations of a list:
L = ["a","b","c"]
combinations(L,length=2)
# [("a","a"),("a","b"),("a","c"),("b","a"),("b","b"),("b","c"),("c","a"),("c","b"),("c","c")]
I've tried
itertools.combinations()
but this returned
[('a', 'b'), ('a', 'c'), ('b', 'c')]
When I use itertools.permutations(), it just returns the combinations with the length of the iteration, which is also not what I want.
Any librarys / function that I can use, without writing my own?
You can use itertools.product with repeat=2 like so:
from itertools import product
L = ["a","b","c"]
print(list(product(L, repeat=2)))
#[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'b'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('c', 'c')]
A simple list comprehesion can do the job too.
L = ["a","b","c"]
print([(a,b) for a in L for b in L])
#[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'b'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('c', 'c')]
The product function from itertools offers a solution.
In [17]: from itertools import product
In [18]: L = ["a","b","c"]
In [19]: list(product(L, L))
Out[19]:
[('a', 'a'),
('a', 'b'),
('a', 'c'),
('b', 'a'),
('b', 'b'),
('b', 'c'),
('c', 'a'),
('c', 'b'),
('c', 'c')]
itertools module has a function called product which is what you are looking for.
>>> L = ["a", "b", "c"]
>>> list(itertools.product(L, repeat=2))
[('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'b'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('c', 'c')]
You can use the second parameter of itertools.permutations():
from itertools import permutations
L = ["a","b","c"]
print([n for n in permutations(L,2)]+[(i,i) for i in L])
Output:
[('a', 'b'), ('a', 'c'), ('b', 'a'), ('b', 'c'), ('c', 'a'), ('c', 'b'), ('a', 'a'), ('b', 'b'), ('c', 'c')]
From the documentation:
itertools.permutations(iterable, r=None)
Return successive r length permutations of elements in the iterable.
If r is not specified or is None, then r defaults to the length of the iterable and all possible full-length permutations are generated.
This question already has answers here:
How do I make a flat list out of a list of lists?
(34 answers)
Closed 3 years ago.
Consider following code:
string = "ABCD"
variations = [list(itertools.combinations(string,x)) for x in range(1,5)]
variations
It produces following output:
[[('A',), ('B',), ('C',), ('D',)],
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D')],
[('A', 'B', 'C'), ('A', 'B', 'D'), ('A', 'C', 'D'), ('B', 'C', 'D')],
[('A', 'B', 'C', 'D')]]
But the output I would like is:
[('A',),
('B',),
('C',),
('D',),
('A', 'B'),
('A', 'C'),
('A', 'D'),
('B', 'C'),
('B', 'D'),
('C', 'D'),
('A', 'B', 'C'),
('A', 'B', 'D'),
('A', 'C', 'D'),
('B', 'C', 'D'),
('A', 'B', 'C', 'D')]
In other words, I want to unpack all the data in the sublists into one list (in the shortest way possible)
I've tried to use asterisk:
string = "ABCD"
variations = [*list(itertools.combinations(string,x)) for x in range(1,5)]
But it throws following error:
SyntaxError: iterable unpacking cannot be used in comprehension
What should I do? (Again, I would like to keep things concise)
Just add another for to your list comprehension that loops over the combinations:
variations = [y for x in range(1, 5) for y in itertools.combinations(string, x)]
print(variations)
Output:
[('A',), ('B',), ('C',), ('D',), ('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('A', 'B', 'C'), ('A', 'B', 'D'), ('A', 'C', 'D'), ('B', 'C', 'D'), ('A', 'B', 'C', 'D')]
You can perform a nested iteration inside the list comprehension. Something like this:
[y for x in range(1,5) for y in itertools.combinations(string, x)]
variations = [e for x in range(1,5) for e in (itertools.combinations(string,x)) ]
I have a list of tuples that looks like this:
[('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]
I want to turn it into this:
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]
What is the most Pythonic way to do this?
one-line, using list comprehension:
l = [('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]
result = [z for y in (x if isinstance(x[0],tuple) else [x] for x in l) for z in y]
print(result)
yields:
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]
this is artificially creating a list if the element is not a tuple of tuples, then flattening all does the job. To avoid creating a single element list [x], (x for _ in range(1)) can also do the job (although it appears clunky)
Limitation: doesn't handle more than 1 level of nesting. In which case, a more complex/recursive solution must be coded (check Martijn's answer).
Adjust the canonical un-flatten recipe to only unflatten when there are tuples in the value:
def flatten(l):
for el in l:
if isinstance(el, tuple) and any(isinstance(sub, tuple) for sub in el):
for sub in flatten(el):
yield sub
else:
yield el
This will only unwrap tuples, and only if there are other tuples in it:
>>> sample = [('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]
>>> list(flatten(sample))
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]
A one-line solution would be using itertools.chain:
>>> l = [('a', 'b'), ('c', 'd'), (('e', 'f'), ('h', 'i'))]
>>> from itertools import chain
>>> [*chain.from_iterable(x if isinstance(x[0], tuple) else [x] for x in l)]
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('h', 'i')]