Operação em cada par de elementos em uma lista

94

Usando Python, gostaria de comparar todos os pares possíveis em uma lista.

Suponha que eu tenha

my_list = [1,2,3,4]

Eu gostaria de fazer uma operação (vamos chamá-la de foo) em cada combinação de 2 elementos da lista.

O resultado final deve ser o mesmo que

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

Meu primeiro pensamento foi iterar duas vezes pela lista manualmente, mas isso não parece muito python.

GuiSim
fonte

Respostas:

229

Confira product()no itertoolsmódulo. Faz exatamente o que você descreve.

import itertools

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

Isso é equivalente a:

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

Edit: Existem duas funções muito semelhantes também, permutations()e combinations(). Para ilustrar como eles diferem:

product() gera todos os pares de elementos possíveis, incluindo todas as duplicatas:

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()gera todas as ordens exclusivas de cada par exclusivo de elementos, eliminando as x,xduplicatas:

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

Por fim, combinations()gera apenas cada par único de elementos, em ordem lexicográfica:

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

Todas as três funções foram introduzidas no Python 2.6.

Ben Blank
fonte
1
Estranho, quando executo itertools.product (my_list, 2), ele reclama que int não pode ser chamado. Funciona assim que eu mudar para: itertools.product (my_list, repeat = 2)
ojrac
Observe que itertools.product () é novo no Python 2.6.
Mike Mazur
Apenas para a posteridade, vou apontar que itertools.combinations não geraria as linhas foo (1,1) ou foo (4,4) no exemplo original.
Kylotan
2
Também há combinações_com_reposição (). Combinações semelhantes (), mas incluindo a diagonal (de acordo com as ilustrações).
Ziegl
1
Para os preguiçosos: para obter os resultados acima com permutations()e combinations()usar r=2em vez de repeat=2usados ​​no exemplo paraproduct()
Rob
15

Tive um problema semelhante e encontrei a solução aqui . Funciona sem a necessidade de importar nenhum módulo.

Supondo uma lista como:

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

Uma solução simplificada de uma linha ficaria assim.

Todos os pares possíveis , incluindo duplicatas:

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

Todos os pares possíveis, excluindo duplicatas :

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

Pares únicos , onde a ordem é irrelevante:

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

Caso você não queira operar, mas apenas pegar os pares, foobastaria retirar a função e usar apenas uma tupla.

Todos os pares possíveis , incluindo duplicatas:

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 os pares possíveis, excluindo duplicatas :

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 , onde a ordem é 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')

Edit: Após o retrabalho para simplificar esta solução, percebi que é a mesma abordagem que Adam Rosenfield. Espero que a explicação mais ampla ajude alguns a entendê-la melhor.

J0ANMM
fonte
2
Eu prefiro muito isso a importar uma biblioteca, muito mais limpa!
sudo-nim
itertools faz parte do Python. Não é uma biblioteca externa.
GuiSim
2

Se você está apenas chamando uma função, você não pode fazer muito melhor do que:

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

Se quiser coletar uma lista dos resultados da chamada da função, você pode fazer:

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

que irá retornar uma lista do resultado da aplicação foo(i, j)para cada par possível (i, j).

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

pairs=[[x,y] for x in my_list for y in my_list]
print (pairs)
karan dave
fonte
Embora esse código possa resolver o problema, uma boa resposta também requer uma explicação do que o código faz e como ele resolve o problema.
BDL de