Operación en cada par de elementos de una lista

95

Usando Python, me gustaría comparar todos los pares posibles en una lista.

Supongamos que tengo

my_list = [1,2,3,4]

Me gustaría hacer una operación (llamémoslo foo) en cada combinación de 2 elementos de la lista.

El resultado final debe ser el mismo que

foo(1,1)
foo(1,2)
...
foo(4,3)
foo(4,4)

Mi primer pensamiento fue iterar dos veces a través de la lista manualmente, pero eso no parece muy pitónico.

GuiSim
fuente

Respuestas:

231

Consulte product()el itertoolsmódulo. Hace exactamente lo que describe.

import itertools

my_list = [1,2,3,4]
for pair in itertools.product(my_list, repeat=2):
    foo(*pair)

Esto es equivalente a:

my_list = [1,2,3,4]
for x in my_list:
    for y in my_list:
        foo(x, y)

Editar: también hay dos funciones muy similares, permutations()y combinations(). Para ilustrar en qué se diferencian:

product() genera todos los emparejamientos posibles de elementos, incluidos todos los duplicados:

1,1  1,2  1,3  1,4
2,1  2,2  2,3  2,4
3,1  3,2  3,3  3,4
4,1  4,2  4,3  4,4

permutations()genera todos los ordenamientos únicos de cada par único de elementos, eliminando los x,xduplicados:

 .   1,2  1,3  1,4
2,1   .   2,3  2,4
3,1  3,2   .   3,4
4,1  4,2  4,3   .

Finalmente, combinations()solo genera cada par único de elementos, en orden lexicográfico:

 .   1,2  1,3  1,4
 .    .   2,3  2,4
 .    .    .   3,4
 .    .    .    .

Las tres funciones se introdujeron en Python 2.6.

Ben Blank
fuente
1
Curioso, cuando ejecuto itertools.product (my_list, 2), se queja de que int no se puede llamar. Funciona una vez que lo cambio a: itertools.product (my_list, repeat = 2)
ojrac
Tenga en cuenta que itertools.product () es nuevo en Python 2.6.
Mike Mazur
Solo para la posteridad, señalaré que itertools.combinations no generaría las líneas foo (1,1) o foo (4,4) en el ejemplo original.
Kylotan
2
También hay combinaciones_con_reposición (). Como combinaciones (), pero incluyendo la diagonal (de acuerdo con las ilustraciones).
Ziegl
1
Para los perezosos: para obtener los resultados anteriores permutations()y combinations()usarlos r=2en lugar de los repeat=2usados ​​en el ejemplo paraproduct()
Rob
15

Tuve un problema similar y encontré la solución aquí . Funciona sin tener que importar ningún módulo.

Suponiendo una lista como:

people = ["Lisa","Pam","Phil","John"]

Una solución simplificada de una línea se vería así.

Todos los pares posibles , incluidos los duplicados:

result = [foo(p1, p2) for p1 in people for p2 in people]

Todos los pares posibles, excluidos los duplicados :

result = [foo(p1, p2) for p1 in people for p2 in people if p1 != p2]

Pares únicos , donde el orden es irrelevante:

result = [foo(people[p1], people[p2]) for p1 in range(len(people)) for p2 in range(p1+1,len(people))]

En caso de que no desee operar sino solo obtener los pares, eliminar la función fooy usar solo una tupla sería suficiente.

Todos los pares posibles , incluidos los duplicados:

list_of_pairs = [(p1, p2) for p1 in people for p2 in people]

Resultado:

('Lisa', 'Lisa')
('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Lisa')
('Pam', 'Pam')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'Lisa')
('Phil', 'Pam')
('Phil', 'Phil')
('Phil', 'John')
('John', 'Lisa')
('John', 'Pam')
('John', 'Phil')
('John', 'John')

Todos los pares posibles, excluidos los duplicados :

list_of_pairs = [(p1, p2) for p1 in people for p2 in people if p1 != p2]

Resultado:

('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Lisa')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'Lisa')
('Phil', 'Pam')
('Phil', 'John')
('John', 'Lisa')
('John', 'Pam')
('John', 'Phil')

Pares únicos , donde el orden es irrelevante:

list_of_pairs = [(people[p1], people[p2]) for p1 in range(len(people)) for p2 in range(p1+1,len(people))]

Resultado:

('Lisa', 'Pam')
('Lisa', 'Phil')
('Lisa', 'John')
('Pam', 'Phil')
('Pam', 'John')
('Phil', 'John')

Editar: Después de la reelaboración para simplificar esta solución, me di cuenta de que es el mismo enfoque que Adam Rosenfield. Espero que la explicación más amplia ayude a algunos a entenderlo mejor.

J0ANMM
fuente
2
Prefiero enormemente esto a importar una biblioteca, ¡mucho más limpio!
sudo-nim
itertools es parte de Python. No es una biblioteca externa.
GuiSim
2

Si solo está llamando a una función, no puede hacerlo mucho mejor que:

for i in my_list:
    for j in my_list:
        foo(i, j)

Si desea recopilar una lista de los resultados de llamar a la función, puede hacer lo siguiente:

[foo(i, j) for i in my_list for j in my_list]

que le devolverá una lista del resultado de aplicar foo(i, j)a cada posible par (i, j).

Adam Rosenfield
fuente
0
my_list = [1,2,3,4]

pairs=[[x,y] for x in my_list for y in my_list]
print (pairs)
karan dave
fuente
Aunque este código podría resolver el problema, una buena respuesta también requiere una explicación de lo que hace el código y cómo resuelve el problema.
BDL