Todas las combinaciones de una lista de listas.

240

Básicamente estoy buscando una versión python de Combination ofList<List<int>>

Dada una lista de listas, necesito una nueva lista que proporcione todas las combinaciones posibles de elementos entre las listas.

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

El número de listas es desconocido, por lo que necesito algo que funcione para todos los casos. ¡Puntos de bonificación por elegancia!

Lin
fuente

Respuestas:

429

necesitas 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
fuente
20
¿Alguien podría explicar el significado del asterisco en *a?
Serrano
52
*asignifica que estos son argumentos que se pasan a la función o método. def fn(a,b,c):respondería a la fn(*[1,2,3]) referencia
mjallday
1
@mjallday, ¿sería posible agregar también estas combinaciones: (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 No está del todo claro lo que desea obtener, pero si es, por ejemplo, también el reverso de cada tupla, puede usar una función de contenedor que tome acomo entrada, itere itertools.product(*a)y yields la tupla producida por itertoolsy una versión inversa ( Por ejemplo, crear una lista reverse()y convertirla de nuevo en tupla). Mejor hacer una nueva pregunta.
Joachim Wagner
24

La solución más elegante es usar itertools.product en python 2.6.

Si no está utilizando Python 2.6, los documentos para itertools.product en realidad muestran una función equivalente para hacer el producto de forma "manual":

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

Espero que lo encuentres tan elegante como yo cuando lo encontré por primera vez.

Matthew Flaschen
fuente
55
¿Qué pasa con ese punto y coma? :)
Paolo Bergantino
3
Fuerza de la costumbre. Me encanta cómo Python te permite poner un punto y coma, solo para ayudarnos a los viejos programadores de C / Java. Pero está claro; no es realmente un terminador de declaraciones cuando haces algo como imprimir ("foo") ;; que es perfectamente legal en C o Java (aunque no tiene sentido) pero está prohibido en Python.
Matthew Flaschen
5

Numpy puede hacerlo:

 >>> 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
fuente
¿Alguien podría explicar esto?
ashishv
5

No hay nada malo en la recursividad directa para esta tarea, y si necesita una versión que funcione con cadenas, esto podría satisfacer sus necesidades:

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
fuente
3

Uno puede usar Python base para esto. El código necesita una función para aplanar listas de listas:

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

Entonces uno puede ejecutar:

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)

Salida:

[[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
fuente
-1
from itertools import product 
list_vals = [['Brand Acronym:CBIQ', 'Brand Acronym :KMEFIC'],['Brand Country:DXB','Brand Country:BH']]
list(product(*list_vals))

Salida:

[('Acrónimo de marca: CBIQ', 'País de marca: DXB'),
('Acrónimo de marca: CBIQ', 'País de marca: BH'),
('Acrónimo de marca: KMEFIC', 'País de marca: DXB'),
( 'Acrónimo de marca: KMEFIC', 'País de marca: BH')]

Kez
fuente
Esta respuesta debe aceptarse, ya que es la única que utiliza una función incorporada, al tiempo que destaca que también funciona para cualquier tipo y también para tipos heterogéneos.
pedjjj
¿En qué se diferencia esta respuesta de la que se proporcionó hace muchos años?
Dawid Laszuk