web-dev-qa-db-fra.com

Obtenir le produit cartésien d'une série de listes?

Comment puis-je obtenir le produit cartésien (toutes les combinaisons de valeurs possibles) à partir d'un groupe de listes?

Contribution:

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

Sortie désirée:

[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), (2, 'a', 5) ...]
272
ʞɔıu

Dans Python 2.6+

import itertools
for element in itertools.product(*somelists):
    print(element)

Documentation: Python 3 - itertools.product

326
Triptych
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)
>>>
72
Jason Baker

Pour Python 2.5 et versions antérieures:

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

Voici une version récursive de product() (juste une 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])

Exemple:

>>> 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())
[()]
34
jfs

avec itertools.product :

import itertools
result = list(itertools.product(*somelists))
17
SilentGhost

Je voudrais utiliser la compréhension de la liste:

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

Dans Python2.6 et versions ultérieures, vous pouvez utiliser 'itertools.product`. Dans les anciennes versions de Python, vous pouvez utiliser l'équivalent suivant (presque - voir documentation) code de la documentation , au moins comme point de départ:

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)

Le résultat de ces deux opérations est un itérateur. Si vous avez vraiment besoin d'une liste pour un traitement ultérieur, utilisez list(result).

11
user3850

Voici un générateur récursif, qui ne stocke aucune liste temporaire

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

Sortie:

[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]
10
Anurag Uniyal

Bien qu'il y ait déjà de nombreuses réponses, j'aimerais partager certaines de mes réflexions:

Approche itérative

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

Approche récursive

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]

Approche Lambda

def cartesian_reduct(pools):
  return reduce(lambda x,y: product(x,y) , pools)
7
weiyixie

approche récursive:

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)

Approche Itérative:

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)
2
Jai

Pour ajouter un peu à ce qui a déjà été dit: si vous utilisez sympy, vous pouvez utiliser des symboles plutôt que des chaînes, ce qui les rend mathématiquement utiles.

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

À propos de sympy .

2
Tyler Heers

Une modification mineure à la solution de générateur récursive ci-dessus à saveur variadique:

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

Et bien sûr, un emballage qui le fait fonctionner exactement comme cette solution:

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

with n compromis: il vérifie si la récursivité doit s’interrompre à chaque boucle externe et n gain: aucun rendement sur appel vide, par exemple product(()), ce qui, je suppose, serait sémantiquement plus correct (voir le doctest).

En ce qui concerne la compréhension de liste: la définition mathématique s’applique à un nombre arbitraire d’arguments, alors que la compréhension de liste ne peut en traiter qu’un nombre connu.

2
Mike Lu