Obtenha diferença entre duas listas
-
27-09-2019 - |
Pergunta
Eu tenho duas listas em Python, como estas:
temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']
Preciso criar uma terceira lista com itens da primeira lista que não estão presentes no segundo. Do exemplo que tenho que obter:
temp3 = ['Three', 'Four']
Existem maneiras rápidas sem ciclos e verificação?
Solução
In [5]: list(set(temp1) - set(temp2))
Out[5]: ['Four', 'Three']
Cuidado com isso
In [5]: set([1, 2]) - set([2, 3])
Out[5]: set([1])
onde você pode esperar/deseja que seja igual set([1, 3])
. Se você quiser set([1, 3])
Como sua resposta, você precisará usar set([1, 2]).symmetric_difference(set([2, 3]))
.
Outras dicas
Todas as soluções existentes oferecem uma ou outra de:
- Desempenho mais rápido que o (n*m).
- Preservar a lista de entrada da lista de entrada.
Mas até agora nenhuma solução tem os dois. Se você quiser os dois, tente o seguinte:
s = set(temp2)
temp3 = [x for x in temp1 if x not in s]
Teste de performance
import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print timeit.timeit('list(set(temp1) - set(temp2))', init, number = 100000)
print timeit.timeit('s = set(temp2);[x for x in temp1 if x not in s]', init, number = 100000)
print timeit.timeit('[item for item in temp1 if item not in temp2]', init, number = 100000)
Resultados:
4.34620224079 # ars' answer
4.2770634955 # This answer
30.7715615392 # matt b's answer
O método que apresentei e a preservação da ordem também é (ligeiramente) mais rápido que a subtração definida, pois não requer construção de um conjunto desnecessário. A diferença de desempenho seria mais perceptível se a primeira lista for consideravelmente maior que a segunda e se a hash for cara. Aqui está um segundo teste demonstrando o seguinte:
init = '''
temp1 = [str(i) for i in range(100000)]
temp2 = [str(i * 2) for i in range(50)]
'''
Resultados:
11.3836875916 # ars' answer
3.63890368748 # this answer (3 times faster!)
37.7445402279 # matt b's answer
temp3 = [item for item in temp1 if item not in temp2]
A diferença entre duas listas (digamos List1 e List2) pode ser encontrada usando a seguinte função simples.
def diff(list1, list2):
c = set(list1).union(set(list2)) # or c = set(list1) | set(list2)
d = set(list1).intersection(set(list2)) # or d = set(list1) & set(list2)
return list(c - d)
ou
def diff(list1, list2):
return list(set(list1).symmetric_difference(set(list2))) # or return list(set(list1) ^ set(list2))
Usando a função acima, a diferença pode ser encontrada usando diff(temp2, temp1)
ou diff(temp1, temp2)
. Ambos darão o resultado ['Four', 'Three']
. Você não precisa se preocupar com a ordem da lista ou qual lista deve ser dada primeiro.
Caso você queira recursivamente a diferença, escrevi um pacote para o Python:https://github.com/seperman/deepdiff
Instalação
Instale a partir de Pypi:
pip install deepdiff
Exemplo de uso
Importação
>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2
O mesmo objeto retorna vazio
>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> print(DeepDiff(t1, t2))
{}
Tipo de item mudou
>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
'newvalue': '2',
'oldtype': <class 'int'>,
'oldvalue': 2}}}
O valor de um item mudou
>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}
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)
{'dic_item_added': ['root[5]', 'root[6]'],
'dic_item_removed': ['root[4]'],
'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}
Diferença de string
>>> 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, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
"root[4]['b']": { 'newvalue': 'world!',
'oldvalue': '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, indent = 2)
{ 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
'+++ \n'
'@@ -1,5 +1,4 @@\n'
'-world!\n'
'-Goodbye!\n'
'+world\n'
' 1\n'
' 2\n'
' End',
'newvalue': 'world\n1\n2\nEnd',
'oldvalue': 'world!\n'
'Goodbye!\n'
'1\n'
'2\n'
'End'}}}
>>>
>>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
---
+++
@@ -1,5 +1,4 @@
-world!
-Goodbye!
+world
1
2
End
Tipo de alteração
>>> 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, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
'newvalue': 'world\n\n\nEnd',
'oldtype': <class 'list'>,
'oldvalue': [1, 2, 3]}}}
Lista diferença
>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3, 4]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{'iterable_item_removed': {"root[4]['b'][2]": 3, "root[4]['b'][3]": 4}}
Lista diferença 2:
>>> 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, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'iterable_item_added': {"root[4]['b'][3]": 3},
'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
"root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}
Liste a diferença de ignorando ordem ou duplicata: (com os mesmos dicionários acima)
>>> 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, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}
Lista que contém 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, indent = 2)
{ 'dic_item_removed': ["root[4]['b'][2][2]"],
'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}
Conjuntos:
>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}
Tuplas nomeadas:
>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}
Objetos personalizados:
>>> class ClassA(object):
... a = 1
... def __init__(self, b):
... self.b = b
...
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>>
>>> pprint(DeepDiff(t1, t2))
{'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Atributo do objeto adicionado:
>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
Se você está realmente analisando o desempenho, use Numpy!
Aqui está o caderno completo como uma essência no Github com comparação entre List, Numpy e Pandas.
https://gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451
maneira mais simples,
usar 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])
pode imprimir como uma lista,
print list(set(list_a).difference(set(list_b)))
Vou jogar, já que nenhuma das soluções atuais produz uma tupla:
temp3 = tuple(set(temp1) - set(temp2))
alternativamente:
#edited using @Mark Byers idea. If you accept this one as answer, just accept his instead.
temp3 = tuple(x for x in temp1 if x not in set(temp2))
Como as outras respostas que não rendem a tuple nessa direção, preserva a ordem
Pode ser feito usando o operador Python XOR.
- Isso removerá as duplicatas em cada lista
- Isso mostrará a diferença de temp1 de temp2 e temp2 de temp1.
set(temp1) ^ set(temp2)
Eu queria algo que levasse duas listas e pudesse fazer o que diff
dentro bash
faz. Como essa pergunta aparece primeiro quando você procura "Python Diff duas listas" e não é muito específico, postarei o que criei.
Usando SequenceMather
a partir de difflib
Você pode comparar duas listas como diff
faz. Nenhuma das outras respostas dirá a posição em que a diferença ocorre, mas essa acontece. Algumas respostas dão a diferença apenas em uma direção. Alguns reordenam os elementos. Alguns não lidam com duplicatas. Mas esta solução oferece uma diferença verdadeira entre duas listas:
a = 'A quick fox jumps the lazy dog'.split()
b = 'A quick brown mouse jumps over the dog'.split()
from difflib import SequenceMatcher
for tag, i, j, k, l in SequenceMatcher(None, a, b).get_opcodes():
if tag == 'equal': print('both have', a[i:j])
if tag in ('delete', 'replace'): print(' 1st has', a[i:j])
if tag in ('insert', 'replace'): print(' 2nd has', b[k:l])
Isso sai:
both have ['A', 'quick']
1st has ['fox']
2nd has ['brown', 'mouse']
both have ['jumps']
2nd has ['over']
both have ['the']
1st has ['lazy']
both have ['dog']
Obviamente, se o seu aplicativo fizer as mesmas suposições que as outras respostas fazem, você se beneficiará mais com elas. Mas se você está procurando um verdadeiro diff
funcionalidade, então este é o único caminho a percorrer.
Por exemplo, nenhuma das outras respostas poderia lidar:
a = [1,2,3,4,5]
b = [5,4,3,2,1]
Mas este faz:
2nd has [5, 4, 3, 2]
both have [1]
1st has [2, 3, 4, 5]
Experimente isso:
temp3 = set(temp1) - set(temp2)
Isso pode ser ainda mais rápido que a compreensão da lista de Mark:
list(itertools.filterfalse(set(temp2).__contains__, temp1))
Aqui está um Counter
Resposta para o caso mais simples.
Isso é mais curto que o acima que faz diferenças de mão dupla porque faz exatamente o que a pergunta faz: gerar uma lista do que está na primeira lista, mas não a segunda.
from collections import Counter
lst1 = ['One', 'Two', 'Three', 'Four']
lst2 = ['One', 'Two']
c1 = Counter(lst1)
c2 = Counter(lst2)
diff = list((c1 - c2).elements())
Como alternativa, dependendo das suas preferências de legibilidade, isso contribui para uma linha decente:
diff = list((Counter(lst1) - Counter(lst2)).elements())
Resultado:
['Three', 'Four']
Observe que você pode remover o list(...)
Ligue se você está apenas iterando sobre isso.
Como essa solução usa contadores, ele lida com quantidades corretamente versus as muitas respostas baseadas em conjuntos. Por exemplo, nesta entrada:
lst1 = ['One', 'Two', 'Two', 'Two', 'Three', 'Three', 'Four']
lst2 = ['One', 'Two']
A saída é:
['Two', 'Two', 'Three', 'Three', 'Four']
Você pode usar um método ingênuo se os elementos do diffist forem classificados e conjuntos.
list1=[1,2,3,4,5]
list2=[1,2,3]
print list1[len(list2):]
ou com métodos de conjunto nativo:
subset=set(list1).difference(list2)
print subset
import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print "Naive solution: ", timeit.timeit('temp1[len(temp2):]', init, number = 100000)
print "Native set solution: ", timeit.timeit('set(temp1).difference(temp2)', init, number = 100000)
Solução ingênua: 0,0787101593292
Solução de conjunto nativo: 0,998837615564
Estou muito tarde no jogo para isso, mas você pode fazer uma comparação do desempenho de alguns dos código acima mencionados com isso, dois dos candidatos mais rápidos são,
list(set(x).symmetric_difference(set(y)))
list(set(x) ^ set(y))
Peço desculpas pelo nível elementar de codificação.
import time
import random
from itertools import filterfalse
# 1 - performance (time taken)
# 2 - correctness (answer - 1,4,5,6)
# set performance
performance = 1
numberoftests = 7
def answer(x,y,z):
if z == 0:
start = time.clock()
lists = (str(list(set(x)-set(y))+list(set(y)-set(y))))
times = ("1 = " + str(time.clock() - start))
return (lists,times)
elif z == 1:
start = time.clock()
lists = (str(list(set(x).symmetric_difference(set(y)))))
times = ("2 = " + str(time.clock() - start))
return (lists,times)
elif z == 2:
start = time.clock()
lists = (str(list(set(x) ^ set(y))))
times = ("3 = " + str(time.clock() - start))
return (lists,times)
elif z == 3:
start = time.clock()
lists = (filterfalse(set(y).__contains__, x))
times = ("4 = " + str(time.clock() - start))
return (lists,times)
elif z == 4:
start = time.clock()
lists = (tuple(set(x) - set(y)))
times = ("5 = " + str(time.clock() - start))
return (lists,times)
elif z == 5:
start = time.clock()
lists = ([tt for tt in x if tt not in y])
times = ("6 = " + str(time.clock() - start))
return (lists,times)
else:
start = time.clock()
Xarray = [iDa for iDa in x if iDa not in y]
Yarray = [iDb for iDb in y if iDb not in x]
lists = (str(Xarray + Yarray))
times = ("7 = " + str(time.clock() - start))
return (lists,times)
n = numberoftests
if performance == 2:
a = [1,2,3,4,5]
b = [3,2,6]
for c in range(0,n):
d = answer(a,b,c)
print(d[0])
elif performance == 1:
for tests in range(0,10):
print("Test Number" + str(tests + 1))
a = random.sample(range(1, 900000), 9999)
b = random.sample(range(1, 900000), 9999)
for c in range(0,n):
#if c not in (1,4,5,6):
d = answer(a,b,c)
print(d[1])
Esta é outra solução:
def diff(a, b):
xa = [i for i in set(a) if i not in b]
xb = [i for i in set(b) if i not in a]
return xa + xb
Se você encontrar TypeError: unhashable type: 'list'
Você precisa transformar listas ou conjuntos em tuplas, por exemplo
set(map(tuple, list_of_lists1)).symmetric_difference(set(map(tuple, list_of_lists2)))
Veja também Como comparar uma lista de listas/conjuntos no Python?
Aqui estão alguns simples, Preservação de pedidos Maneiras de diferenciar duas listas de cordas.
Código
Uma abordagem incomum usando pathlib
:
import pathlib
temp1 = ["One", "Two", "Three", "Four"]
temp2 = ["One", "Two"]
p = pathlib.Path(*temp1)
r = p.relative_to(*temp2)
list(r.parts)
# ['Three', 'Four']
Isso pressupõe que ambas as listas contenham strings com começos equivalentes. Veja o documentos para mais detalhes. Observe que não é particularmente rápido em comparação às operações definidas.
Uma implementação direta usando itertools.zip_longest
:
import itertools as it
[x for x, y in it.zip_longest(temp1, temp2) if x != y]
# ['Three', 'Four']
versão de linha única de arulmr solução
def diff(listA, listB):
return set(listA) - set(listB) | set(listA) -set(listB)
Se você quiser algo mais parecido com uma mudança ... poderia usar o contador
from collections import Counter
def diff(a, b):
""" more verbose than needs to be, for clarity """
ca, cb = Counter(a), Counter(b)
to_add = cb - ca
to_remove = ca - cb
changes = Counter(to_add)
changes.subtract(to_remove)
return changes
lista = ['one', 'three', 'four', 'four', 'one']
listb = ['one', 'two', 'three']
In [127]: diff(lista, listb)
Out[127]: Counter({'two': 1, 'one': -1, 'four': -2})
# in order to go from lista to list b, you need to add a "two", remove a "one", and remove two "four"s
In [128]: diff(listb, lista)
Out[128]: Counter({'four': 2, 'one': 1, 'two': -1})
# in order to go from listb to lista, you must add two "four"s, add a "one", and remove a "two"
Podemos calcular a interseção menos a união de listas:
temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two', 'Five']
set(temp1+temp2)-(set(temp1)&set(temp2))
Out: set(['Four', 'Five', 'Three'])
Isso pode ser resolvido com uma linha. A pergunta recebe duas listas (temp1 e temp2) retornam sua diferença em uma terceira lista (TEMP3).
temp3 = list(set(temp1).difference(set(temp2)))
Digamos que temos duas listas
list1 = [1, 3, 5, 7, 9]
list2 = [1, 2, 3, 4, 5]
Podemos ver nas duas listas acima que os itens 1, 3, 5 existem na lista2 e os itens 7, 9 não. Por outro lado, os itens 1, 3, 5 existem na lista1 e os itens 2, 4 não.
Qual é a melhor solução para retornar uma nova lista que contém itens 7, 9 e 2, 4?
Todas as respostas acima encontram a solução, agora qual é o mais ideal?
def difference(list1, list2):
new_list = []
for i in list1:
if i not in list2:
new_list.append(i)
for j in list2:
if j not in list1:
new_list.append(j)
return new_list
contra
def sym_diff(list1, list2):
return list(set(list1).symmetric_difference(set(list2)))
Usando Timeit, podemos ver os resultados
t1 = timeit.Timer("difference(list1, list2)", "from __main__ import difference,
list1, list2")
t2 = timeit.Timer("sym_diff(list1, list2)", "from __main__ import sym_diff,
list1, list2")
print('Using two for loops', t1.timeit(number=100000), 'Milliseconds')
print('Using two for loops', t2.timeit(number=100000), 'Milliseconds')
retorna
[7, 9, 2, 4]
Using two for loops 0.11572412995155901 Milliseconds
Using symmetric_difference 0.11285737506113946 Milliseconds
Process finished with exit code 0
Aqui está uma maneira simples de distinguir duas listas (qualquer que seja o conteúdo), você pode obter o resultado como mostrado abaixo:
>>> from sets import Set
>>>
>>> l1 = ['xvda', False, 'xvdbb', 12, 'xvdbc']
>>> l2 = ['xvda', 'xvdbb', 'xvdbc', 'xvdbd', None]
>>>
>>> Set(l1).symmetric_difference(Set(l2))
Set([False, 'xvdbd', None, 12])
Espero que isso seja útil.
(list(set(a)-set(b))+list(set(b)-set(a)))