Toutes les combinaisons d'une liste de listes

240

Je cherche essentiellement une version python de Combination ofList<List<int>>

Étant donné une liste de listes, j'ai besoin d'une nouvelle liste qui donne toutes les combinaisons possibles d'éléments entre les listes.

[[1,2,3],[4,5,6],[7,8,9,10]] -> [[1,4,7],[1,4,8],...,[3,6,10]]

Le nombre de listes est inconnu, j'ai donc besoin de quelque chose qui fonctionne dans tous les cas. Points bonus pour l'élégance!

Lin
la source

Réponses:

429

vous avez besoin de itertools.product:

>>> import itertools
>>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
>>> list(itertools.product(*a))
[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
SilentGhost
la source
20
Quelqu'un pourrait-il expliquer la signification de l'astérisque *a?
Serrano
52
*asignifie que ce sont des arguments transmis à la fonction ou à la méthode. def fn(a,b,c):répondrait à la fn(*[1,2,3]) référence
mjallday
1
@mjallday, serait-il possible d'ajouter également ces combinaisons: (7,4,1), (8,4,1), (9,4,1), (10,4,1), (7,5, 1), (8,5,1), (9,5,1), (10,5,1), etc.?
Reman
1
@Reman Ce que vous voulez obtenir n'est pas tout à fait clair, mais s'il s'agit, par exemple, également de l'inverse de chaque tuple, vous pouvez utiliser une fonction wrapper qui prend aen entrée, itère sur itertools.product(*a)et yields à la fois le tuple produit par itertoolset une version inverse ( par exemple créer une liste reverse()et la reconvertir en tuple). Mieux vaut poser une nouvelle question.
Joachim Wagner
24

La solution la plus élégante consiste à utiliser itertools.product en python 2.6.

Si vous n'utilisez pas Python 2.6, les documents pour itertools.product montrent en fait une fonction équivalente pour faire le produit de la manière "manuelle":

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)
Jarret Hardie
la source
19
listOLists = [[1,2,3],[4,5,6],[7,8,9,10]]
for list in itertools.product(*listOLists):
  print list;

J'espère que vous trouverez cela aussi élégant que moi lors de ma première rencontre.

Matthew Flaschen
la source
5
Qu'est-ce qui se passe avec ce point-virgule? :)
Paolo Bergantino
3
Force d'habitude. J'adore la façon dont Python vous permet de mettre un point-virgule, juste pour nous aider les anciens programmeurs C / Java. Mais c'est clair; n'est pas vraiment un terminateur d'instructions lorsque vous faites quelque chose comme print ("foo") ;; qui est parfaitement légal en C ou Java (quoique inutile) mais interdit en Python.
Matthew Flaschen le
5

Numpy peut le faire:

 >>> import numpy
 >>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
 >>> [list(x) for x in numpy.array(numpy.meshgrid(*a)).T.reshape(-1,len(a))]
[[ 1, 4, 7], [1, 5, 7], [1, 6, 7], ....]
Diamantatos Paraskevas
la source
Quelqu'un pourrait-il expliquer cela?
ashishv
5

Rien de mal avec une récursivité directe pour cette tâche, et si vous avez besoin d'une version qui fonctionne avec des chaînes, cela pourrait répondre à vos besoins:

combinations = []

def combine(terms, accum):
    last = (len(terms) == 1)
    n = len(terms[0])
    for i in range(n):
        item = accum + terms[0][i]
        if last:
            combinations.append(item)
        else:
            combine(terms[1:], item)


>>> a = [['ab','cd','ef'],['12','34','56']]
>>> combine(a, '')
>>> print(combinations)
['ab12', 'ab34', 'ab56', 'cd12', 'cd34', 'cd56', 'ef12', 'ef34', 'ef56']
duanev
la source
3

On peut utiliser le python de base pour cela. Le code a besoin d'une fonction pour aplatir les listes de listes:

def flatten(B):    # function needed for code below;
    A = []
    for i in B:
        if type(i) == list: A.extend(i)
        else: A.append(i)
    return A

Ensuite, on peut exécuter:

L = [[1,2,3],[4,5,6],[7,8,9,10]]

outlist =[]; templist =[[]]
for sublist in L:
    outlist = templist; templist = [[]]
    for sitem in sublist:
        for oitem in outlist:
            newitem = [oitem]
            if newitem == [[]]: newitem = [sitem]
            else: newitem = [newitem[0], sitem]
            templist.append(flatten(newitem))

outlist = list(filter(lambda x: len(x)==len(L), templist))  # remove some partial lists that also creep in;
print(outlist)

Production:

[[1, 4, 7], [2, 4, 7], [3, 4, 7], 
[1, 5, 7], [2, 5, 7], [3, 5, 7], 
[1, 6, 7], [2, 6, 7], [3, 6, 7], 
[1, 4, 8], [2, 4, 8], [3, 4, 8], 
[1, 5, 8], [2, 5, 8], [3, 5, 8], 
[1, 6, 8], [2, 6, 8], [3, 6, 8], 
[1, 4, 9], [2, 4, 9], [3, 4, 9], 
[1, 5, 9], [2, 5, 9], [3, 5, 9], 
[1, 6, 9], [2, 6, 9], [3, 6, 9], 
[1, 4, 10], [2, 4, 10], [3, 4, 10], 
[1, 5, 10], [2, 5, 10], [3, 5, 10], 
[1, 6, 10], [2, 6, 10], [3, 6, 10]]
rnso
la source
-1
from itertools import product 
list_vals = [['Brand Acronym:CBIQ', 'Brand Acronym :KMEFIC'],['Brand Country:DXB','Brand Country:BH']]
list(product(*list_vals))

Production:

[('Acronyme de marque: CBIQ', 'Pays de marque: DXB'),
('' Acronyme de marque: CBIQ ',' Pays de marque: BH '),
(' 'Acronyme de marque: KMEFIC', 'Pays de marque: DXB'),
( 'Acronyme de la marque: KMEFIC', 'Pays de la marque: BH')]

Kez
la source
Cette réponse doit être acceptée, car elle est la seule à utiliser une fonction intégrée, tout en soulignant qu'elle fonctionne également pour tous les types et également hétérogènes.
pedjjj
En quoi cette réponse est-elle différente de celle fournie il y a de nombreuses années?
Dawid Laszuk