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'))
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)) ]
If there's a dictionary:
test_dict = { 'a':1,'b':2,'c':3,'d':4}
I want to find pairs of keys in list of tuples like:
[('a','b'),('a','c'),('a','d'),('b','c'),('b','d'),('c','d')]
I tried with the following double iteration
test_dict = { 'a':1,'b':2,'c':3,'d':4}
result = []
for first_key in test_dict:
for second_key in test_dict:
if first_key != second_key:
pair = (first_key,second_key)
result.append(pair)
But it's generating the following result
[('a', 'c'), ('a', 'b'), ('a', 'd'), ('c', 'a'), ('c', 'b'), ('c', 'd'), ('b', 'a'), ('b', 'c'), ('b', 'd'), ('d', 'a'), ('d', 'c'), ('d', 'b')]
For my test case ('a','b') and ('b','a') are similar and I just want one of them in the list. I had to run one more loop for getting the unique pairs from the result.
So is there any efficient way to do it in Python (preferably in 2.x)? I want to remove nested loops.
Update:
I have checked with the possible flagged duplicate, but it's not solving the problem here. It's just providing different combination. I just need the pairs of 2. For that question a tuple of ('a','b','c') and ('a','b','c','d') are valid, but for me they are not. I hope, this explains the difference.
Sounds like a job for itertools.
from itertools import combinations
test_dict = {'a':1, 'b':2, 'c':3, 'd':4}
results = list(combinations(test_dict, 2))
[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
I should add that although the output above happens to be sorted, this is not guaranteed. If order is important, you can instead use:
results = sorted(combinations(test_dict, 2))
Since dictionary keys are unique, this problem becomes equivalent of finding all combinations of the keys of size 2. You can just use itertools for that:
>>> test_dict = { 'a':1,'b':2,'c':3,'d':4}
>>> import itertools
>>> list(itertools.combinations(test_dict, 2))
[('c', 'a'), ('c', 'd'), ('c', 'b'), ('a', 'd'), ('a', 'b'), ('d', 'b')]
Note, these will come in no particular order, since dict objects are inherently unordered. But you can sort before or after, if you want sorted order:
>>> list(itertools.combinations(sorted(test_dict), 2))
[('a', 'b'), ('a', 'c'), ('a', 'd'), ('b', 'c'), ('b', 'd'), ('c', 'd')]
>>>
Note, this algorithm is relatively simple if you are working with sequences like a list:
>>> ks = list(test_dict)
>>> for i, a in enumerate(ks):
... for b in ks[i+1:]: # this is the important bit
... print(a, b)
...
c a
c d
c b
a d
a b
d b
Or more succinctly:
>>> [(a,b) for i, a in enumerate(ks) for b in ks[i+1:]]
[('c', 'a'), ('c', 'd'), ('c', 'b'), ('a', 'd'), ('a', 'b'), ('d', 'b')]
>>>
itertools.combinations does just what you want:
from itertools import combinations
test_dict = { 'a':1,'b':2,'c':3,'d':4}
keys = tuple(test_dict)
combs = list(combinations(keys, 2))
print(combs)
# [('a', 'd'), ('a', 'b'), ('a', 'c'), ('d', 'b'), ('d', 'c'), ('b', 'c')]
combs = list(combinations(test_dict, 2)) would just do; iterating over a dictionary is just iterating over its keys...
I'm making a specialized utility similar to John the Ripper, and I'd like to use a loop that returns all strings up to x characters that can be formed from the string. For example, if the "seed" string is abcd, it should return:
a
b
c
d
aa
ab
ac
and so on. If the character limit is 10, it would generate aaaaaaaaaa, abcddcbaaa, and so on. Is there a simple for loop to do this, or is it more complicated than that?
I'll self-plagiarize from this answer and add a maximum length:
from itertools import product
def multiletters(seq, max_length):
for n in range(1, max_length+1):
for s in product(seq, repeat=n):
yield ''.join(s)
which gives
>>> list(multiletters("abc", 2))
['a', 'b', 'c', 'aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
>>> list(multiletters("abcd", 4))[:8]
['a', 'b', 'c', 'd', 'aa', 'ab', 'ac', 'ad']
and so on.
def all_strings(alphabet, length_limit=None):
n_letters = len(alphabet)
length = 0
n_strings = 1
buf = []
while True:
for i in xrange(0, n_strings):
k = i
for j in xrange(length - 1, -1, -1):
buf[j] = alphabet[k % n_letters]
k /= n_letters
yield ''.join(buf)
length += 1
if length == length_limit:
break
n_strings *= n_letters
buf.append(alphabet[0])
for s in all_strings('abcd', length_limit=4):
print s
As pointed out in the comment's use itertools.premutations or even better take a look #DSM's answer, as this one misses the doubles:
In [194]: from itertools import chain, permutations
In [195]: s = 'abcd'
In [196]: map(''.join,chain.from_iterable(permutations(s,x)
for x in range(1,len(s)+1)))
Out[196]:
['a',
'b',
'c',
'd',
'ab',
'ac',
'ad',
'ba',
'bc',
'bd',
...
'dbca',
'dcab',
'dcba']
Anyway, here's a version of #DSM's answer that returns a list:
from itertools import product
def ms(seq, max_length):
return [''.join(s) for n in range(1, max_length+1)
for s in product(seq,repeat=n)]
Use itertools.permuataions.
for i in range(2,4):
tuples = itertools.permutations('abca' , i)
print( list(tuples))
The example code sequence generates:
[('a', 'b'), ('a', 'c'), ('a', 'a'), ('b', 'a'), ('b', 'c'), ('b', 'a'), ('c', 'a'), ('c', 'b'), ('c', 'a'), ('a', 'a'), ('a', 'b'), ('a', 'c')]
[('a', 'b', 'c'), ('a', 'b', 'a'), ('a', 'c', 'b'), ('a', 'c', 'a'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('b', 'a', 'c'), ('b', 'a', 'a'), ('b', 'c', 'a'), ('b', 'c', 'a'), ('b', 'a', 'a'), ('b', 'a', 'c'), ('c', 'a', 'b'), ('c', 'a', 'a'), ('c', 'b', 'a'), ('c', 'b', 'a'), ('c', 'a', 'a'), ('c', 'a', 'b'), ('a', 'a', 'b'), ('a', 'a', 'c'), ('a', 'b', 'a'), ('a', 'b', 'c'), ('a', 'c', 'a'), ('a', 'c', 'b')]