Cómo obtener el producto cartesiano de una serie de listas

6 minutos de lectura

Avatar de usuario de ʞɔıu
ʞɔıu

¿Cómo puedo obtener el producto cartesiano (todas las combinaciones posibles de valores) de un grupo de listas?

Aporte:

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

Salida deseada:

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

Una aplicación común de esta técnica es evitar bucles profundamente anidados. Consulte Evitar bucles for anidados para obtener un duplicado más específico.

Si desea un producto cartesiano de lo mismo lista consigo mismo varias veces, itertools.product puede manejar eso con elegancia. Ver Operación en cada par de elementos en una lista o Generación de permutaciones con repeticiones.

  • tenga en cuenta que ‘todas las combinaciones posibles’ no es lo mismo que ‘producto cartesiano’, ya que en los productos cartesianos se permiten duplicados.

    – Kenan Banks

    10 de febrero de 2009 a las 20:08

  • ¿Existe una versión no duplicada del producto cartesiano?

    – KJW

    13 de noviembre de 2013 a las 5:32

  • @KJW Sí, set(cartesian product)

    – Sin errores

    12 de febrero de 2015 a las 7:04

  • No debe haber duplicados en un producto cartesiano, a menos que las listas de entrada contengan duplicados. Si no desea duplicados en el producto cartesiano, utilice set(inputlist) sobre todas sus listas de entrada. No en el resultado.

    – CamilB

    24 de agosto de 2017 a las 8:39

  • Matemáticamente, un producto cartesiano es un conjunto, por lo que un producto cartesiano no no contienen duplicados. Por otra parte, itertools.product tendrá duplicados en la salida si las entradas tienen duplicados. Asi que itertools.product no es estrictamente hablando el producto cartesiano, a menos que envuelva las entradas en setcomo lo menciona @CamilB.

    –Cameron Bieganek

    8 dic 2020 a las 22:56

Avatar de usuario de Kenan Banks
Bancos de Kenan

Usar itertools.productque ha estado disponible desde Python 2.6.

import itertools

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

Esto es lo mismo que:

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

  • Solo quería agregar el carácter ‘*’ si usa la variable somelists según lo proporcionado por el OP.

    – brian dólar

    13 de enero de 2011 a las 22:51

  • Cuál es el uso de * antes de algunas listas? ¿Qué hace?

    – Vineet Kumar Doshi

    25 de agosto de 2015 a las 9:04

  • @VineetKumarDoshi: Aquí se usa para descomponer una lista en múltiples argumentos para la llamada a la función. Lea más aquí: stackoverflow.com/questions/36901/…

    – Moberg

    15 de septiembre de 2015 a las 6:20

  • Nota: Esto funciona solo si cada lista contiene al menos un elemento

    – Yo voy

    13/09/2017 a las 12:35

  • @igo también funciona cuando cualquier lista contiene cero elementos: el producto cartesiano de al menos una lista de tamaño cero y cualquier otra lista es una lista vacía, y eso es exactamente lo que esto produce.

    – Ruzihm

    9 oct 2019 a las 20:42

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

avatar de usuario de jfs
jfs

Para Python 2.5 y anteriores:

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

Aquí hay una versión recursiva de product() (solo una ilustración):

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

Ejemplo:

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

  • La versión recursiva no funciona si algunos de args son iteradores.

    – jfs

    10 de febrero de 2009 a las 21:43

Yo usaría la lista de comprensión:

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

Avatar de usuario de SilentGhost
Fantasma silencioso

con itertools.producto:

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

  • Cuál es el uso de * antes de algunas listas?

    – Vineet Kumar Doshi

    25 de agosto de 2015 a las 9:04

  • @VineetKumarDoshi “producto(algunas listas)” es un producto cartesiano entre las sublistas de manera que Python obtiene primero “[1, 2, 3]” como un elemento y luego obtiene otro elemento después del siguiente comando y ese es el salto de línea, por lo que el primer término del producto es ([1, 2, 3],), similarmente para el segundo ([4, 5],) y entonces “[([1, 2, 3],), ([4, 5],), ([6, 7],)]”. Si desea obtener un producto cartesiano entre elementos dentro de las tuplas, debe informar a Python con Asterisk sobre la estructura de la tupla. Para el diccionario, usa **. Más aquí.

    – hhh

    15 de febrero de 2016 a las 23:13

Avatar de usuario de Anurag Uniyal
Anurag Uniyal

Aquí hay un generador recursivo, que no almacena ninguna lista temporal.

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

Producción:

[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]

  • Cuál es el uso de * antes de algunas listas?

    – Vineet Kumar Doshi

    25 de agosto de 2015 a las 9:04

  • @VineetKumarDoshi “producto(algunas listas)” es un producto cartesiano entre las sublistas de manera que Python obtiene primero “[1, 2, 3]” como un elemento y luego obtiene otro elemento después del siguiente comando y ese es el salto de línea, por lo que el primer término del producto es ([1, 2, 3],), similarmente para el segundo ([4, 5],) y entonces “[([1, 2, 3],), ([4, 5],), ([6, 7],)]”. Si desea obtener un producto cartesiano entre elementos dentro de las tuplas, debe informar a Python con Asterisk sobre la estructura de la tupla. Para el diccionario, usa **. Más aquí.

    – hhh

    15 de febrero de 2016 a las 23:13

En Python 2.6 y superior, puede usar ‘itertools.product`. En versiones anteriores de Python, puede usar el siguiente equivalente (casi, consulte la documentación) código de la documentaciónal menos como punto de partida:

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)

El resultado de ambos es un iterador, por lo que si realmente necesita una lista para un procesamiento posterior, use list(result).

  • Según la documentación, la implementación real de itertools.product NO genera resultados intermedios, lo que podría ser costoso. El uso de esta técnica podría salirse de control con bastante rapidez para las listas de tamaño moderado.

    – Kenan Banks

    10 de febrero de 2009 a las 20:05

  • Solo puedo señalar el OP a la documentación, no leerlo por él.

    usuario3850

    10 de febrero de 2009 a las 20:19

  • El código de la documentación está destinado a demostrar lo que hace la función del producto, no como una solución para versiones anteriores de Python.

    – Kenan Banks

    10 de marzo de 2009 a las 21:07

¿Ha sido útil esta solución?