Python, diferença na lista de computação

195

No Python, qual é a melhor maneira de calcular a diferença entre duas listas?

exemplo

A = [1,2,3,4]
B = [2,5]

A - B = [1,3,4]
B - A = [5]
Mike
fonte

Respostas:

206

Use setse você não se importa com ordem ou repetição de itens. Use a compreensão da lista se você:

>>> def diff(first, second):
        second = set(second)
        return [item for item in first if item not in second]

>>> diff(A, B)
[1, 3, 4]
>>> diff(B, A)
[5]
>>> 
Roman Bodnarchuk
fonte
31
Considere o uso set(b)para assegurar o algoritmo é O (nlogn) em vez de teta (n ^ 2)
Neil G
8
@Pencilcheck - não se você se preocupa em solicitar ou duplicar em A. A setinscrição em B é inofensiva, mas aplicá-la Ae usar o resultado em vez do original Anão é.
Re
1
@NeilG Você considera o tempo consumido para construir o cenário? No meu caso (ambas as listas têm cerca de 10 milhões de strings), o tempo para criar dois conjuntos e subtraí-los é consideravelmente maior do que construir um conjunto e iterar sobre a lista.
dimril
@ Dimril, se é isso que você quer fazer, talvez deva implementar algo mais sofisticado. Você pode, por exemplo, classificar as duas listas O (n log n + m log m) e repetir a segunda lista, mas usar a pesquisa binária para encontrar os itens na primeira lista. Ele sairia para operações O (n log n + m log m + m log n) (em vez de operações O (n * m)), o que não parece muito ruim. Apenas verifique se os vizinhos também eliminam duplicatas em suas implementações de pesquisa binária. Pode até haver um pacote que já implemente isso, mas eu não verifiquei.
jaaq
365

Se o pedido não importa, você pode simplesmente calcular a diferença definida:

>>> set([1,2,3,4]) - set([2,5])
set([1, 4, 3])
>>> set([2,5]) - set([1,2,3,4])
set([5])
phihag
fonte
9
Esta é de longe a melhor solução. Cada caso de teste em listas com ~ 6000 seqüências mostrou que esse método era quase 100x mais rápido que as compreensões de lista.
precisa saber é o seguinte
15
Depende da aplicação: se a preservação da ordem ou duplicação é importante, Roman Bodnarchuk pode ter uma abordagem melhor. Para velocidade e comportamento puro, este parece melhor.
Bryan P
7
Se você tiver vários elementos iguais na lista, esta solução não funcionará.
karantan
Muito mais do que compreensão de lista.
Dawei
4
Esta solução parece tão óbvia, mas está incorreta. Eu sinto Muito. É claro que queremos dizer que uma lista pode ter elementos iguais repetidos. Caso contrário, perguntamos sobre a diferença entre conjuntos, não sobre a diferença de lista.
Sergzach 01/06
67

Você pode fazer um

list(set(A)-set(B))

e

list(set(B)-set(A))
Senthil Kumaran
fonte
7
Mas se A = [1,1,1] e B = [0], então isso retorna [1]
Mark Bell
1
@ Mark Bell: Isso é porque um conjunto é uma lista distinta. (remove duplicatas)
nublado
1
@cloudy Então isso não responde à pergunta.
samm82
@ samm82 se A = [1,1,1] do que o conjunto (A) for [1] porque o conjunto é uma lista distinta e remove duplicatas. É por isso que, se A = [1,1,1] e B = [0], ele retorna [1].
nublado
29

Um forro:

diff = lambda l1,l2: [x for x in l1 if x not in l2]
diff(A,B)
diff(B,A)

Ou:

diff = lambda l1,l2: filter(lambda x: x not in l2, l1)
diff(A,B)
diff(B,A)
Artsiom Rudzenka
fonte
14

Os exemplos acima banalizaram o problema de calcular diferenças. Supondo que a classificação ou desduplicação definitivamente facilite a computação da diferença, mas se sua comparação não puder permitir essas suposições, você precisará de uma implementação não trivial de um algoritmo diff. Veja difflib na biblioteca padrão do python.

from difflib import SequenceMatcher 

squeeze=SequenceMatcher( None, A, B )

print "A - B = [%s]"%( reduce( lambda p,q: p+q, 
                               map( lambda t: squeeze.a[t[1]:t[2]], 
                                    filter(lambda x:x[0]!='equal', 
                                           squeeze.get_opcodes() ) ) ) )

A - B = [[1, 3, 4]]

Kevin
fonte
1
você recebe +1 por difflib, o que eu não tinha visto antes. no entanto, não concordo que as respostas acima trivializem o problema, conforme indicado .
rbp
Obrigado por usar o difflib - eu estava procurando uma solução usando a biblioteca padrão. No entanto, este não está a funcionar em Python 3, como printmudou a partir de um comando para uma função, e reduce, filtere mapforam declarados unpythonic. (E eu acho Guido pode estar certo - Eu não entendo o que reducefaz, também.)
Post169
Não é uma grande mudança para fazê-lo funcionar no py3. Eu li o debate sobre filtrar, mapear, reduzir e concordar com a opção de empurrar reduzir e alternar impl do filtro nas funções. A natureza mista funcional, OO e processual do python sempre foi, na IMO, um dos seus pontos fortes.
Kevin
14

Python 2.7.3 (padrão, 27 de fevereiro de 2014, 19:58:35) - IPython 1.1.0 - timeit: (github gist)

def diff(a, b):
  b = set(b)
  return [aa for aa in a if aa not in b]

def set_diff(a, b):
  return list(set(a) - set(b))

diff_lamb_hension = lambda l1,l2: [x for x in l1 if x not in l2]

diff_lamb_filter = lambda l1,l2: filter(lambda x: x not in l2, l1)

from difflib import SequenceMatcher
def squeezer(a, b):
  squeeze = SequenceMatcher(None, a, b)
  return reduce(lambda p,q: p+q, map(
    lambda t: squeeze.a[t[1]:t[2]],
      filter(lambda x:x[0]!='equal',
        squeeze.get_opcodes())))

Resultados:

# Small
a = range(10)
b = range(10/2)

timeit[diff(a, b)]
100000 loops, best of 3: 1.97 µs per loop

timeit[set_diff(a, b)]
100000 loops, best of 3: 2.71 µs per loop

timeit[diff_lamb_hension(a, b)]
100000 loops, best of 3: 2.1 µs per loop

timeit[diff_lamb_filter(a, b)]
100000 loops, best of 3: 3.58 µs per loop

timeit[squeezer(a, b)]
10000 loops, best of 3: 36 µs per loop

# Medium
a = range(10**4)
b = range(10**4/2)

timeit[diff(a, b)]
1000 loops, best of 3: 1.17 ms per loop

timeit[set_diff(a, b)]
1000 loops, best of 3: 1.27 ms per loop

timeit[diff_lamb_hension(a, b)]
1 loops, best of 3: 736 ms per loop

timeit[diff_lamb_filter(a, b)]
1 loops, best of 3: 732 ms per loop

timeit[squeezer(a, b)]
100 loops, best of 3: 12.8 ms per loop

# Big
a = xrange(10**7)
b = xrange(10**7/2)

timeit[diff(a, b)]
1 loops, best of 3: 1.74 s per loop

timeit[set_diff(a, b)]
1 loops, best of 3: 2.57 s per loop

timeit[diff_lamb_filter(a, b)]
# too long to wait for

timeit[diff_lamb_filter(a, b)]
# too long to wait for

timeit[diff_lamb_filter(a, b)]
# TypeError: sequence index must be integer, not 'slice'

A função de compreensão de lista roman-bodnarchuk def def diff (a, b) parece ser mais rápida.

Moreno
fonte
9
A = [1,2,3,4]
B = [2,5]

#A - B
x = list(set(A) - set(B))
#B - A 
y = list(set(B) - set(A))

print x
print y 
Saksham Varma
fonte
8

Você gostaria de usar um em setvez de um list.

O Pato Comunista
fonte
5

Caso você queira que a diferença entre nos itens da sua lista recursivamente, escrevi um pacote para python: https://github.com/erasmose/deepdiff

Instalação

Instale a partir do PyPi:

pip install deepdiff

Se você é Python3, também precisa instalar:

pip install future six

Exemplo de uso

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function

O mesmo objeto retorna vazio

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {}

O tipo de um item foi alterado

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {'type_changes': ["root[2]: 2=<type 'int'> vs. 2=<type 'str'>"]}

O valor de um item foi alterado

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {'values_changed': ['root[2]: 2 ====>> 4']}

Item adicionado e / ou removido

>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes)
    {'dic_item_added': ['root[5, 6]'],
     'dic_item_removed': ['root[4]'],
     'values_changed': ['root[2]: 2 ====>> 4']}

Diferença de cadeia

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'values_changed': [ 'root[2]: 2 ====>> 4',
                          "root[4]['b']:\n--- \n+++ \n@@ -1 +1 @@\n-world\n+world!"]}
>>>
>>> print (ddiff.changes['values_changed'][1])
    root[4]['b']:
    --- 
    +++ 
    @@ -1 +1 @@
    -world
    +world!

Diferença de string 2

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'values_changed': [ "root[4]['b']:\n--- \n+++ \n@@ -1,5 +1,4 @@\n-world!\n-Goodbye!\n+world\n 1\n 2\n End"]}
>>>
>>> print (ddiff.changes['values_changed'][0])
    root[4]['b']:
    --- 
    +++ 
    @@ -1,5 +1,4 @@
    -world!
    -Goodbye!
    +world
     1
     2
     End

Alteração de tipo

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'type_changes': [ "root[4]['b']: [1, 2, 3]=<type 'list'> vs. world\n\n\nEnd=<type 'str'>"]}

Diferença de lista

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'list_removed': ["root[4]['b']: [3]"]}

Diferença de lista 2: observe que NÃO leva em conta a ordem

>>> # Note that it DOES NOT take order into account
... t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { }

Lista que contém o dicionário:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'dic_item_removed': ["root[4]['b'][2][2]"],
      'values_changed': ["root[4]['b'][2][1]: 1 ====>> 3"]}
Seperman
fonte
5

maneira mais simples,

use set (). diferença (set ())

list_a = [1,2,3]
list_b = [2,3]
print set(list_a).difference(set(list_b))

a resposta é set([1])

Mohideen bin Mohammed
fonte
2

No caso de uma lista de dicionários , a solução completa de compreensão da lista funciona enquanto a setsolução gera

TypeError: unhashable type: 'dict'

Caso de teste

def diff(a, b):
    return [aa for aa in a if aa not in b]

d1 = {"a":1, "b":1}
d2 = {"a":2, "b":2}
d3 = {"a":3, "b":3}

>>> diff([d1, d2, d3], [d2, d3])
[{'a': 1, 'b': 1}]
>>> diff([d1, d2, d3], [d1])
[{'a': 2, 'b': 2}, {'a': 3, 'b': 3}]
joao
fonte
0

Código simples que fornece a diferença com vários itens, se você desejar:

a=[1,2,3,3,4]
b=[2,4]
tmp = copy.deepcopy(a)
for k in b:
    if k in tmp:
        tmp.remove(k)
print(tmp)
SOU
fonte
-1

Ao examinar o TimeComplexity do operador interno , na pior das hipóteses, ele funciona com O (n). Mesmo para conjuntos.

Portanto, ao comparar duas matrizes, teremos uma TimeComplexity de O (n) no melhor dos casos e O (n ^ 2) no pior dos casos.

Uma solução alternativa (mas infelizmente mais complexa), que funciona com O (n) na melhor e na pior das hipóteses, é esta:

# Compares the difference of list a and b
# uses a callback function to compare items
def diff(a, b, callback):
  a_missing_in_b = []
  ai = 0
  bi = 0

  a = sorted(a, callback)
  b = sorted(b, callback)

  while (ai < len(a)) and (bi < len(b)):

    cmp = callback(a[ai], b[bi])
    if cmp < 0:
      a_missing_in_b.append(a[ai])
      ai += 1
    elif cmp > 0:
      # Item b is missing in a
      bi += 1
    else:
      # a and b intersecting on this item
      ai += 1
      bi += 1

  # if a and b are not of same length, we need to add the remaining items
  for ai in xrange(ai, len(a)):
    a_missing_in_b.append(a[ai])


  return a_missing_in_b

por exemplo

>>> a=[1,2,3]
>>> b=[2,4,6]
>>> diff(a, b, cmp)
[1, 3]
DerKnorr
fonte