Obtenga la diferencia entre dos listas con entradas únicas

9 minutos de lectura

Avatar de usuario de Max Frai
Max Fray

Tengo dos listas en Python:

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

Suponiendo que los elementos de cada lista son únicos, quiero crear una tercera lista con elementos de la primera lista que no están en la segunda lista:

temp3 = ['Three', 'Four']

¿Existen formas rápidas sin ciclos y comprobaciones?

  • ¿Los elementos son únicos garantizados? Si usted tiene temp1 = ['One', 'One', 'One'] y temp2 = ['One']Quieres ['One', 'One'] atrás, o []?

    – Michael Mrozek

    11 de agosto de 2010 a las 19:43

  • @michael-mrozek son únicos.

    – Max Fray

    11 de agosto de 2010 a las 19:45

  • ¿Quieres conservar el orden de los elementos?

    –Mark Byers

    11 de agosto de 2010 a las 19:49

  • ¿Responde esto a tu pregunta? Encontrar elementos que no están en una lista

    – Gonçalo Peres

    28 de abril de 2021 a las 8:48

Avatar de usuario de Mark Byers
marca byers

Todas las soluciones existentes ofrecen uno u otro de:

  • Rendimiento más rápido que O(n*m).
  • Conservar el orden de la lista de entrada.

Pero hasta ahora ninguna solución tiene ambos. 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. La diferencia de rendimiento sería más notable si la primera lista es considerablemente más larga que la segunda y si el hashing 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

  • Soporte adicional para esta respuesta: se encontró con un caso de uso en el que preservar el orden de la lista era importante para el rendimiento. Cuando trabajaba con objetos tarinfo o zipinfo, estaba usando la resta de conjuntos. Para excluir ciertos objetos tarinfo de ser extraídos del archivo. La creación de la nueva lista fue rápida pero muy lenta durante la extracción. La razón me evadió al principio. Resulta que reordenar la lista de objetos tarinfo causó una gran penalización en el rendimiento. Cambiar al método de comprensión de listas salvó el día.

    – Ray Thompson

    13 de diciembre de 2011 a las 0:26


  • @MarkByers: tal vez debería escribir una pregunta completamente nueva para esto. Pero, ¿cómo funcionaría esto en un forloop? Por ejemplo, si mi temp1 y temp2 siguen cambiando… ¿y quiero agregar la nueva información a temp3?

    – Un hijo

    9 de agosto de 2012 a las 17:57

  • @MarkByers: suena bien. Seguiré pensando en ello un rato. pero +1 para una gran solución.

    – Un hijo

    9 de agosto de 2012 a las 18:39

  • ¿Podría explicar por qué su código lleva menos tiempo que el de la respuesta de Matt? @MarkByers

    – trucos

    5 de noviembre de 2015 a las 16:36

  • @haccks Porque verificar la membresía de una lista es una operación O (n) (iterar sobre toda la lista), pero verificar la membresía de un conjunto es O (1).

    –Mark Byers

    5 de noviembre de 2015 a las 16:57

  • ¡Buen descubrimiento! Siempre pasé por alto esta sección de la documentación: docs.python.org/3/library/….

    usuario3521099

    28 de diciembre de 2020 a las 16:09

  • Este es el mejor para una diferencia de 2 lados.

    – EuberDeveloper

    17/03/2021 a las 22:35

  • Definitivamente la mejor respuesta que aborda la pregunta del OP directamente “Obtener la diferencia entre dos listas”. Los otros son demasiado complicados con casos secundarios. Y no hay conversión de tipo de datos.

    – Rich Lysakowski Doctorado

    7 de abril de 2021 a las 3:25

  • ¿Funciona mejor que cualquier otra solución a la vez?

    – Gangula

    13 de septiembre de 2021 a las 9:11

  • @Gangula Para ver la diferencia entre los dos métodos, agregue un valor a temp2 que no está presente en temp1 e intenta de nuevo.

    – urig

    19 oct 2021 a las 5:41

avatar de usuario de matt b
mate b

Podrías usar la lista de comprensión:

temp3 = [item for item in temp1 if item not in temp2]

  • Torneado temp2 en un conjunto antes haría esto un poco más eficiente.

    usuario355252

    11 de agosto de 2010 a las 19:47

  • Cierto, depende de si a Ockonal le importan los duplicados o no (la pregunta original no lo dice)

    – mate b

    11 de agosto de 2010 a las 19:47

  • El comentario dice que las (listas|tuplas) no tienen duplicados.

    usuario395760

    11 de agosto de 2010 a las 19:52

  • Voté a favor de su respuesta porque pensé que tenía razón sobre los duplicados al principio. Pero item not in temp2 y item not in set(temp2) siempre devolverá los mismos resultados, independientemente de si hay duplicados o no en temp2.

    – arekolek

    07/03/2016 a las 22:42


  • Vote a favor por no exigir que los elementos de la lista sean hashable.

    – Brent

    11 de septiembre de 2017 a las 15:19

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

Importador

>>> 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 un 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 artículo 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 agregado 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}}}

diferencia de cadena

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

diferencia de cadena 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]}}}

Diferencia de lista

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

Enumere la diferencia ignorando el orden o los 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 agregado:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

  • Torneado temp2 en un conjunto antes haría esto un poco más eficiente.

    usuario355252

    11 de agosto de 2010 a las 19:47

  • Cierto, depende de si a Ockonal le importan los duplicados o no (la pregunta original no lo dice)

    – mate b

    11 de agosto de 2010 a las 19:47

  • El comentario dice que las (listas|tuplas) no tienen duplicados.

    usuario395760

    11 de agosto de 2010 a las 19:52

  • Voté a favor de su respuesta porque pensé que tenía razón sobre los duplicados al principio. Pero item not in temp2 y item not in set(temp2) siempre devolverá los mismos resultados, independientemente de si hay duplicados o no en temp2.

    – arekolek

    07/03/2016 a las 22:42


  • Vote a favor por no exigir que los elementos de la lista sean hashable.

    – Brent

    11 de septiembre de 2017 a las 15:19

Avatar de usuario de Maciej Kucharz
maciej kucharz

Prueba esto:

temp3 = set(temp1) - set(temp2)

¿Ha sido útil esta solución?