Obtener diferencia entre dos listas


Tengo dos listas en Python, como estas:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']

Necesito crear una tercera lista con elementos de la primera lista que no están presentes en la segunda. Del ejemplo tengo que obtener:

temp3 = ['Three', 'Four']

¿Hay alguna forma rápida sin ciclos y comprobación?

Author: denfromufa, 0000-00-00

18 answers

In [5]: list(set(temp1) - set(temp2))
Out[5]: ['Four', 'Three']

Tenga cuidado de que

In [5]: set([1, 2]) - set([2, 3])
Out[5]: set([1]) 

Donde podrías esperar/querer que sea igual a set([1, 3]). Si quieres set([1, 3]) como respuesta, necesitarás usar set([1, 2]).symmetric_difference(set([2, 3])).

 845
Author: ars,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-19 11:50:55

Todas las soluciones existentes ofrecen una u otra de:

  • Más rápido que el rendimiento de O(n*m).
  • Preservar el orden de la lista de entrada.

Pero hasta ahora ninguna solución tiene ambas. Si quieres ambos, prueba esto:

s = set(temp2)
temp3 = [x for x in temp1 if x not in s]

Prueba de Rendimiento

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

El método que presenté, además de preservar el orden, también es (ligeramente) más rápido que la resta de conjuntos porque no requiere la construcción de un conjunto innecesario. El la diferencia de rendimiento sería más notoria si la primera lista es considerablemente más larga que la segunda y si el hash es costoso. Aquí hay una segunda prueba que demuestra esto:

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
 394
Author: Mark Byers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 20:35:25
temp3 = [item for item in temp1 if item not in temp2]
 52
Author: matt b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 19:40:27

En caso de que desee la diferencia recursivamente, he escrito un paquete para python: https://github.com/seperman/deepdiff

Instalación

Instalar desde PyPI:

pip install deepdiff

Ejemplo de uso

Importando

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2

El mismo objeto devuelve vacío

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

El tipo de elemento ha cambiado

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

El valor de un elemento ha cambiado

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

Elemento añadido y/o eliminado

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

Cadena diferencia

>>> 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'}}}

String difference 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

Cambio 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, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                      'newvalue': 'world\n\n\nEnd',
                                      'oldtype': <class 'list'>,
                                      'oldvalue': [1, 2, 3]}}}

List difference

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

Listar diferencia 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}}}

Listar diferencia ignorando orden o duplicados: (con los mismos diccionarios que arriba)

>>> 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 contiene diccionario:

>>> 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 con nombre:

>>> 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 de objeto añadido:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
 16
Author: Seperman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-10 21:57:35

La diferencia entre dos listas (digamos list1 y list2) se puede encontrar usando la siguiente función simple.

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)

O

def diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))  # or return list(set(list1) ^ set(list2))

Usando la función anterior, la diferencia se puede encontrar usando diff(temp2, temp1) o diff(temp1, temp2). Ambos darán el resultado ['Four', 'Three']. No tiene que preocuparse por el orden de la lista o qué lista se debe dar primero.

Referencia del documento de Python

 15
Author: arulmr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-05 12:07:17

Si realmente estás buscando rendimiento, entonces usa numpy!

Aquí está el cuaderno completo como un gist en github con comparación entre list, numpy y pandas.

Https://gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451

introduzca la descripción de la imagen aquí

 14
Author: denfromufa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-29 07:04:12

Voy a tirar en ya que ninguna de las soluciones actuales producen una 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))

Al igual que la otra no-tupla que da respuestas en esta dirección, conserva el orden

 12
Author: aaronasterling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 20:57:18

Se puede hacer usando el operador python XOR.

  • Esto eliminará los duplicados en cada lista
  • Esto mostrará la diferencia de temp1 de temp2 y temp2 de temp1.

set(temp1) ^ set(temp2)
 11
Author: Vikram S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-19 05:25:16

La forma más simple,

Use set().diferencia (conjunto())

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

La respuesta es set([1])

Puede imprimir como una lista,

print list(set(list_a).difference(set(list_b)))
 10
Author: Mohideen ibn Mohammed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-30 12:59:00

Esto podría ser incluso más rápido que la comprensión de la lista de Marcos:

list(itertools.filterfalse(set(temp2).__contains__, temp1))
 9
Author: Mohammed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-21 00:22:48

Prueba esto:

temp3 = set(temp1) - set(temp2)
 8
Author: Maciej Kucharz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-11 19:39:59

Quería algo que tomara dos listas y pudiera hacer lo que diff en bash hace. Dado que esta pregunta aparece primero cuando buscas "python diff two lists" y no es muy específica, publicaré lo que se me ocurrió.

Utilizando SequenceMather desde difflib puedes comparar dos listas como lo hace diff. Ninguna de las otras respuestas le dirá la posición donde ocurre la diferencia, pero ésta sí. Algunas respuestas dan la diferencia en una sola dirección. Algunos reordenan el elemento. Algunos no manejan duplicados. Pero esta solución le da una verdadera diferencia entre dos 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])

Esto produce:

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

Por supuesto, si su solicitud hace las mismas suposiciones que las otras respuestas, usted se beneficiará más de ellas. Pero si está buscando una verdadera funcionalidad diff, entonces este es el único camino a seguir.

Por ejemplo, ninguna de las otras respuestas podría manejar:

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

Pero este sí:

  2nd has [5, 4, 3, 2]
both have [1]
  1st has [2, 3, 4, 5]
 8
Author: arekolek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-07 22:35:27

Podría usar un método naive si los elementos de la lista difflist están ordenados y conjuntos.

list1=[1,2,3,4,5]
list2=[1,2,3]

print list1[len(list2):]

O con métodos set nativos:

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)

Solución naive: 0,0787101593292

Solución de conjunto nativa: 0.998837615564

 5
Author: soundcorner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-29 13:21:24

Aquí hay una Counter respuesta para el caso más simple.

Esto es más corto que el anterior que hace diffs bidireccionales porque solo hace exactamente lo que la pregunta pide: generar una lista de lo que está en la primera lista pero no en la segunda.

from collections import Counter

lst1 = ['One', 'Two', 'Three', 'Four']
lst2 = ['One', 'Two']

c1 = Counter(lst1)
c2 = Counter(lst2)
diff = list((c1 - c2).elements())

Alternativamente, dependiendo de sus preferencias de legibilidad, se convierte en un decente de una sola línea:

diff = list((Counter(lst1) - Counter(lst2)).elements())

Salida:

['Three', 'Four']

Tenga en cuenta que puede eliminar la llamada list(...) si solo está iterando sobre ella.

Porque esta solución utiliza contadores, maneja las cantidades correctamente frente a las muchas respuestas basadas en conjuntos. Por ejemplo en esta entrada:

lst1 = ['One', 'Two', 'Two', 'Two', 'Three', 'Three', 'Four']
lst2 = ['One', 'Two']

La salida es:

['Two', 'Two', 'Three', 'Three', 'Four']
 5
Author: Taylor Edmiston,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-07 03:26:27

Si se encuentra con TypeError: unhashable type: 'list' necesita convertir listas o conjuntos en tuplas, por ejemplo,

set(map(tuple, list_of_lists1)).symmetric_difference(set(map(tuple, list_of_lists2)))

Véase también ¿Cómo comparar una lista de listas/conjuntos en python?

 4
Author: guaka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:41

Esta es otra solución:

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
 3
Author: manhgd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-29 02:36:31

Versión de una sola línea de arulmr solución

def diff(listA, listB):
    return set(listA) - set(listB) | set(listA) -set(listB)
 3
Author: sreemanth pulagam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-26 12:02:44

Si quieres algo más como un conjunto de cambios... podría utilizar 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 
 3
Author: ,
Warning: date() expects parameter 2 to be long, string given in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61