6

I have two lists:

a = ['a', 'b']
b = [1, 2, 3]

I want to get the combinations produced between the elements of list b and the elements of list a but treating elements of a as pairs (or triples etc. etc.) as the example below which gives len(b) ** len(a) number of combinations.

c = ["a_1 b_1", "a_1 b_2", "a_1 b_3", "a_2 b_1", "a_2 b_2", "a_2 b_3", "a_3 b_1", "a_3 b_2" "a_3 b_3"]

I have tried to use itertools.product (as described here) but this will give only the 6 possible combinations.

Giorgos Myrianthous
  • 36,235
  • 20
  • 134
  • 156
  • "I have tried to use itertools.product (as described here) but this will give only the 6 possible combinations. " That's because you tried to take the product of the A list with the B list. The actual problem is to take the product of the B list **with itself**, and then **use** the A list to **format** those results. – Karl Knechtel Mar 02 '23 at 00:56

2 Answers2

14

You can use itertools.product(..) but specify repeat to be repeat=len(a). So you can use:

from itertools import product

def mul_product(a,b):
    for tup in product(b,repeat=len(a)):
        yield ' '.join('%s_%s'%t for t in zip(a,tup))

The product(..) will generate tuples like:

>>> list(product(b,repeat=len(a)))
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]

So here the first element of the tuple is the one that is attached to a_, the second one to b_. Now we zip(..) them together with the a list, producing:

>>> list(map(lambda bi:list(zip(a,bi)),product(b,repeat=len(a))))
[[('a', 1), ('b', 1)], [('a', 1), ('b', 2)], [('a', 1), ('b', 3)], [('a', 2), ('b', 1)], [('a', 2), ('b', 2)], [('a', 2), ('b', 3)], [('a', 3), ('b', 1)], [('a', 3), ('b', 2)], [('a', 3), ('b', 3)]]

Now it is only a matter of formatting ('%s_%s'%t), and ' '.join(..)ining them together and yield them (or you can use list comprehension to produce a list).

The result for your sample input is:

>>> list(mul_product(a,b))
['a_1 b_1', 'a_1 b_2', 'a_1 b_3', 'a_2 b_1', 'a_2 b_2', 'a_2 b_3', 'a_3 b_1', 'a_3 b_2', 'a_3 b_3']

Note that the elements here are generated lazily. This can be useful if you are for instance only interested in the first k ones, or when you do not want to generate all of them at once.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
4

You could explicitly create your pairwise items using itertools.product, then operate on those pairs again with itertools.product

import itertools
a = ['a', 'b']
b = [1, 2, 3]
pairs = [list(itertools.product([ai], b)) for ai in a]

pairs will contain the two lists that can fed into itertools.product again.

list(itertools.product(*pairs))

The result is:

[(('a', 1), ('b', 1)),
 (('a', 1), ('b', 2)),
 (('a', 1), ('b', 3)),
 (('a', 2), ('b', 1)),
 (('a', 2), ('b', 2)),
 (('a', 2), ('b', 3)),
 (('a', 3), ('b', 1)),
 (('a', 3), ('b', 2)),
 (('a', 3), ('b', 3))]
Brad Campbell
  • 2,969
  • 2
  • 23
  • 21
  • While you could indeed do this, the first step defeats the purpose of using `itertools.product`. It could just as easily be `pairs = [[(ai, bi) for bi in b] for ai in a]`. Yes, `itertools.product` would generate all of those 6 combinations, but without the required list structure; and once we have used a comprehension to get the required structure, `itertools.product` is no longer doing meaningful work. – Karl Knechtel Mar 02 '23 at 01:02