448

How can I get the Cartesian product (every possible combination of values) from a group of lists?

Input:

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]

Desired output:

[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), (2, 'a', 5) ...]
Georgy
  • 9,972
  • 7
  • 57
  • 66
ʞɔıu
  • 45,214
  • 31
  • 101
  • 144
  • 34
    be aware that 'every possible combination' is not quite the same as 'Cartesian product', since in Cartesian products, duplicates are allowed. – Kenan Banks Feb 10 '09 at 20:08
  • 9
    Is there a non duplicate version of cartesian product? – KJW Nov 13 '13 at 05:32
  • 19
    @KJW Yes, `set(cartesian product)` – NoBugs Feb 12 '15 at 07:04
  • 11
    There should be no duplicates in a Cartesian product, unless the input lists contain duplicates themselves. If you want no duplicates in the Cartesian product, use `set(inputlist)` over all your input lists. Not on the result. – CamilB Aug 24 '17 at 08:39
  • 3
    @Triptych what? The standard definition of a Cartesian product is a set. Why do so many people upvote? – PascalIv Apr 17 '20 at 07:16
  • 8
    Mathematically, a Cartesian product is a set, so a Cartesian product does *not* contain duplicates. On the other hand, `itertools.product` will have duplicates in the output if the inputs have duplicates. So `itertools.product` is not strictly speaking the Cartesian product, unless you wrap the inputs in `set`, as mentioned by @CamilB. – Cameron Bieganek Dec 08 '20 at 22:56
  • @Pascallv the standard definition of a combination is an (unordered)element of a many times cartesian product of a set with itself, such that the *element* contains no duplicates. A Cartesian product of sets contains no duplicates, but the elements of that product may. Confounding the issue, mathematically speaking `itertools.product` is a Cartesian product of multisets. A product of multisets will contain duplicate tuples if the input multisets do. All of these issues are at play in the comments here, and people are perhaps talking across each other. – Tobias Hagge Apr 13 '21 at 01:04
  • 1
    The mathematical analogies are great for naming functions, but they only go so far. A python `set` is not exactly a mathematical set, and `itertools.product` is not exactly a mathematical Cartesian product. That's fine. Also, if the goal is to avoid duplicate, I believe `product(*(set(l) for l in somelists))` is more efficient than `set(product(somelists))` if the lists contain many duplicates. – Stef Jan 31 '22 at 10:02
  • As a signpost for others trying to close questions as duplicates: it is very common that people want to find the "cartesian exponent" of a list - the cartesian product of a list with itself, multiple times. This question is generally not the best possible duplicate - since it doesn't address the `repeat` parameter for `itertools.product`, and doesn't clearly draw the link between the cartesian product and typical questions (which are often incorrectly phrased in terms of "combinations"). Try this one: https://stackoverflow.com/questions/3099987/generating-permutations-with-repetitions – Karl Knechtel Mar 29 '22 at 09:53

17 Answers17

540

itertools.product

Available from Python 2.6.

import itertools

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]
for element in itertools.product(*somelists):
    print(element)

Which is the same as,

for element in itertools.product([1, 2, 3], ['a', 'b'], [4, 5]):
    print(element)
cs95
  • 330,695
  • 80
  • 606
  • 657
Kenan Banks
  • 198,060
  • 33
  • 151
  • 170
  • 29
    Just wanted to add the '*' character is required if you use the variable somelists as provided by the OP. – brian buck Jan 13 '11 at 22:51
  • any idea the efficiency of the `itertools.product()` code? I assume it's been optimized, but just curious (and I don't know how to calculate it from the docs page...) – galois Aug 12 '15 at 08:56
  • 1
    @jaska: `product()` generates `nitems_in_a_list ** nlists` elements in the result (`reduce(mul, map(len, somelists))`). There is no reason to believe that yielding a single element is not `O(nlists)` (amortized) i.e., the time complexity is the same as for [simple nested `for`-loops](http://stackoverflow.com/a/534085/4279) e.g., for the input in the question: `nlists=3`, total number of elements in the result: `3*2*2`, and each element has `nlists` items (`3` in this case). – jfs Aug 14 '15 at 22:08
  • 5
    What is the use of `*` before somelists? What does it do? – Vineet Kumar Doshi Aug 25 '15 at 09:04
  • 10
    @VineetKumarDoshi: Here it is used to unpcak a list into multiple arguments to the function call. Read more here: http://stackoverflow.com/questions/36901/what-does-double-star-and-star-do-for-python-parameters – Moberg Sep 15 '15 at 06:20
  • 5
    Note: This works only if each list contains at least one item – igo Sep 13 '17 at 12:35
  • 2
    Just a detail, but note that `itertools.product()` can also handle generators, and not just list-like objects. – normanius Dec 05 '18 at 23:18
  • 7
    @igo it also works when any list contains zero items--the cartesian product of at least one zero sized list and any other lists *is* an empty list, and that's exactly what this produces. – Ruzihm Oct 09 '19 at 20:42
  • Or via list comprehension: `lst = [i for i in itertools.product(*somelists)]` – Lucas Schwartz Dec 12 '21 at 01:24
109
import itertools
>>> for i in itertools.product([1,2,3],['a','b'],[4,5]):
...         print i
...
(1, 'a', 4)
(1, 'a', 5)
(1, 'b', 4)
(1, 'b', 5)
(2, 'a', 4)
(2, 'a', 5)
(2, 'b', 4)
(2, 'b', 5)
(3, 'a', 4)
(3, 'a', 5)
(3, 'b', 4)
(3, 'b', 5)
>>>
Jason Baker
  • 182,027
  • 130
  • 359
  • 509
44

For Python 2.5 and older:

>>> [(a, b, c) for a in [1,2,3] for b in ['a','b'] for c in [4,5]]
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), 
 (2, 'a', 5), (2, 'b', 4), (2, 'b', 5), (3, 'a', 4), (3, 'a', 5), 
 (3, 'b', 4), (3, 'b', 5)]

Here's a recursive version of product() (just an illustration):

def product(*args):
    if not args:
        return iter(((),)) # yield tuple()
    return (items + (item,) 
            for items in product(*args[:-1]) for item in args[-1])

Example:

>>> list(product([1,2,3], ['a','b'], [4,5])) 
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), 
 (2, 'a', 5), (2, 'b', 4), (2, 'b', 5), (3, 'a', 4), (3, 'a', 5), 
 (3, 'b', 4), (3, 'b', 5)]
>>> list(product([1,2,3]))
[(1,), (2,), (3,)]
>>> list(product([]))
[]
>>> list(product())
[()]
jfs
  • 374,366
  • 172
  • 933
  • 1,594
34

I would use list comprehension :

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]

cart_prod = [(a,b,c) for a in somelists[0] for b in somelists[1] for c in somelists[2]]
user1035648
  • 415
  • 4
  • 7
27

with itertools.product:

import itertools
result = list(itertools.product(*somelists))
SilentGhost
  • 287,765
  • 61
  • 300
  • 288
  • 7
    What is the use of `*` before somelists? – Vineet Kumar Doshi Aug 25 '15 at 09:04
  • 1
    @VineetKumarDoshi *"product(somelists)"* is a cartesian product between the sublists in a way that Python first get *"[1, 2, 3]"* as an element and then gets other element after next comman and that is linebreak so the first product term is ([1, 2, 3],), similary for the second ([4, 5],) and so *"[([1, 2, 3],), ([4, 5],), ([6, 7],)]"*. If you wanna get a cartesian product between elements inside the tuples, you need to tell Python with Asterisk about the tuple structure. For dictionary, you use **. More [here](http://stackoverflow.com/a/400753/164148). – hhh Feb 15 '16 at 23:13
15

Here is a recursive generator, which doesn't store any temporary lists

def product(ar_list):
    if not ar_list:
        yield ()
    else:
        for a in ar_list[0]:
            for prod in product(ar_list[1:]):
                yield (a,)+prod

print list(product([[1,2],[3,4],[5,6]]))

Output:

[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
Anurag Uniyal
  • 81,711
  • 39
  • 167
  • 215
10

In Python 2.6 and above you can use 'itertools.product`. In older versions of Python you can use the following (almost -- see documentation) equivalent code from the documentation, at least as a starting point:

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)

The result of both is an iterator, so if you really need a list for furthert processing, use list(result).

  • Per the documentation, the actual itertools.product implementation does NOT build intermediate results, which could be expensive. Using this technique could get out of hand quite quickly for moderately sized lists. – Kenan Banks Feb 10 '09 at 20:05
  • 5
    i can only point the OP to the documentation, not read it for him. –  Feb 10 '09 at 20:19
  • 1
    The code from the documentation is meant to demonstrate what the product function does, not as a workaround for earlier versions of Python. – Kenan Banks Mar 10 '09 at 21:07
9

Although there are many answers already, I would like to share some of my thoughts:

Iterative approach

def cartesian_iterative(pools):
  result = [[]]
  for pool in pools:
    result = [x+[y] for x in result for y in pool]
  return result

Recursive Approach

def cartesian_recursive(pools):
  if len(pools) > 2:
    pools[0] = product(pools[0], pools[1])
    del pools[1]
    return cartesian_recursive(pools)
  else:
    pools[0] = product(pools[0], pools[1])
    del pools[1]
    return pools
def product(x, y):
  return [xx + [yy] if isinstance(xx, list) else [xx] + [yy] for xx in x for yy in y]

Lambda Approach

def cartesian_reduct(pools):
  return reduce(lambda x,y: product(x,y) , pools)
Matthew Beckman
  • 1,644
  • 2
  • 15
  • 27
weiyixie
  • 505
  • 5
  • 6
  • In "Iterative Approach", why is result declared as result = [[]] I know that it is list_of_list but in general even if we have declare list_of_list we use [] and not [[]] – Sachin S Jul 16 '17 at 15:44
  • 1
    I'm a bit of a newby in terms of Pythonic solutions. Would you or some passerby please write the list comprehension in the "iterative approach" in separate loops? – Johnny Boy Dec 10 '18 at 17:03
  • @SachinS you use an inner list inside the outer list because you iterate over the outer list (for x in result), and the inner list means the outer list isn't empty. If it were empty, no iteration would occur since there would be no x in 'result'. And then you add items to that list. The example is pretty much taken from the official documentation, but I daresay it's more implicit than explicit. If you were to refactor it into code only based on loops and cut out the comprehensions, as Johny Boy is saying, then it would take a quite a lot more code. – Daniel Jul 16 '20 at 13:52
  • what is `pools`? Is it a list of the lists I want the product of? – blkpingu Dec 17 '20 at 22:18
7

Recursive Approach:

def rec_cart(start, array, partial, results):
  if len(partial) == len(array):
    results.append(partial)
    return 

  for element in array[start]:
    rec_cart(start+1, array, partial+[element], results)

rec_res = []
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]  
rec_cart(0, some_lists, [], rec_res)
print(rec_res)

Iterative Approach:

def itr_cart(array):
  results = [[]]
  for i in range(len(array)):
    temp = []
    for res in results:
      for element in array[i]:
        temp.append(res+[element])
    results = temp

  return results

some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]  
itr_res = itr_cart(some_lists)
print(itr_res)
Jai
  • 3,014
  • 2
  • 15
  • 24
3

A minor modification to the above recursive generator solution in variadic flavor:

def product_args(*args):
    if args:
        for a in args[0]:
            for prod in product_args(*args[1:]) if args[1:] else ((),):
                yield (a,) + prod

And of course a wrapper which makes it work exactly the same as that solution:

def product2(ar_list):
    """
    >>> list(product(()))
    [()]
    >>> list(product2(()))
    []
    """
    return product_args(*ar_list)

with one trade-off: it checks if recursion should break upon each outer loop, and one gain: no yield upon empty call, e.g.product(()), which I suppose would be semantically more correct (see the doctest).

Regarding list comprehension: the mathematical definition applies to an arbitrary number of arguments, while list comprehension could only deal with a known number of them.

Mike Lu
  • 31
  • 2
2

Just to add a bit to what has already been said: if you use sympy, you can use symbols rather than strings which makes them mathematically useful.

import itertools
import sympy

x, y = sympy.symbols('x y')

somelist = [[x,y], [1,2,3], [4,5]]
somelist2 = [[1,2], [1,2,3], [4,5]]

for element in itertools.product(*somelist):
  print element

About sympy.

FengYao
  • 11
  • 4
Tyler Heers
  • 124
  • 4
1

I believe this works:

def cartesian_product(L):  
   if L:
       return {(a,) + b for a in L[0] 
                        for b in cartesian_product(L[1:])}
   else:
       return {()}
0

The following code is a 95 % copy from Using numpy to build an array of all combinations of two arrays, all credits go there! This is said to be much faster since it is only in numpy.

import numpy as np

def cartesian(arrays, dtype=None, out=None):
    arrays = [np.asarray(x) for x in arrays]
    if dtype is None:
        dtype = arrays[0].dtype
    n = np.prod([x.size for x in arrays])
    if out is None:
        out = np.zeros([n, len(arrays)], dtype=dtype)

    m = int(n / arrays[0].size) 
    out[:,0] = np.repeat(arrays[0], m)
    if arrays[1:]:
        cartesian(arrays[1:], out=out[0:m, 1:])
        for j in range(1, arrays[0].size):
            out[j*m:(j+1)*m, 1:] = out[0:m, 1:]
    return out

You need to define the dtype as a parameter if you do not want to take the dtype from the first entry for all entries. Take dtype = 'object' if you have letters and numbers as items. Test:

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]

[tuple(x) for x in cartesian(somelists, 'object')]

Out:

[(1, 'a', 4),
 (1, 'a', 5),
 (1, 'b', 4),
 (1, 'b', 5),
 (2, 'a', 4),
 (2, 'a', 5),
 (2, 'b', 4),
 (2, 'b', 5),
 (3, 'a', 4),
 (3, 'a', 5),
 (3, 'b', 4),
 (3, 'b', 5)]
0

This can be done as

[(x, y) for x in range(10) for y in range(10)]

another variable? No problem:

[(x, y, z) for x in range(10) for y in range(10) for z in range(10)]
0

List comprehension is simple and clean:

import itertools

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]
lst = [i for i in itertools.product(*somelists)]
-1

You can use itertools.product in the standard library to get the cartesian product. Other cool, related utilities in itertools include permutations, combinations, and combinations_with_replacement. Here is a link to a python codepen for the snippet below:

from itertools import product

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]

result = list(product(*somelists))
print(result)
chriskoch
  • 9
  • 1
-1

With early rejection:

def my_product(pools: List[List[Any]], rules: Dict[Any, List[Any]], forbidden: List[Any]) -> Iterator[Tuple[Any]]:
    """
    Compute the cartesian product except it rejects some combinations based on provided rules
    
    :param pools: the values to calculate the Cartesian product on 
    :param rules: a dict specifying which values each value is incompatible with
    :param forbidden: values that are never authorized in the combinations
    :return: the cartesian product
    """
    if not pools:
        return

    included = set()

    # if an element has an entry of 0, it's acceptable, if greater than 0, it's rejected, cannot be negative
    incompatibles = defaultdict(int)
    for value in forbidden:
        incompatibles[value] += 1
    selections = [-1] * len(pools)
    pool_idx = 0

    def current_value():
        return pools[pool_idx][selections[pool_idx]]

    while True:
        # Discard incompatibilities from value from previous iteration on same pool
        if selections[pool_idx] >= 0:
            for value in rules[current_value()]:
                incompatibles[value] -= 1
            included.discard(current_value())

        # Try to get to next value of same pool
        if selections[pool_idx] != len(pools[pool_idx]) - 1:
            selections[pool_idx] += 1
        # Get to previous pool if current is exhausted
        elif pool_idx != 0:
            selections[pool_idx] = - 1
            pool_idx -= 1
            continue
        # Done if first pool is exhausted
        else:
            break

        # Add incompatibilities of newly added value
        for value in rules[current_value()]:
            incompatibles[value] += 1
        included.add(current_value())

        # Skip value if incompatible
        if incompatibles[current_value()] or \
                any(intersection in included for intersection in rules[current_value()]):
            continue

        # Submit combination if we're at last pool
        if pools[pool_idx] == pools[-1]:
            yield tuple(pool[selection] for pool, selection in zip(pools, selections))
        # Else get to next pool
        else:
            pool_idx += 1

I had a case where I had to fetch the first result of a very big Cartesian product. And it would take ages despite I only wanted one item. The problem was that it had to iterate through many unwanted results before finding a correct one because of the order of the results. So if I had 10 lists of 50 elements and the first element of the two first lists were incompatible, it had to iterate through the Cartesian product of the last 8 lists despite that they would all get rejected.

This implementation enables to test a result before it includes one item from each list. So when I check that an element is incompatible with the already included elements from the previous lists, I immediately go to the next element of the current list rather than iterating through all products of the following lists.

Adrien H
  • 559
  • 5
  • 18