Python, Listenunterschied berechnen


194

Was ist in Python der beste Weg, um den Unterschied zwischen zwei Listen zu berechnen?

Beispiel

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

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

Antworten:


205

Verwenden setSie diese Option, wenn Sie sich nicht für die Reihenfolge oder Wiederholung von Artikeln interessieren. Verwenden Sie Listenverständnisse, wenn Sie Folgendes tun:

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

31
set(b)Erwägen Sie die Verwendung, um sicherzustellen, dass der Algorithmus O (nlogn) anstelle von Theta (n ^ 2) ist
Neil G

8
@Pencilcheck - nicht, wenn Sie sich für die Bestellung oder Vervielfältigung in A interessieren. Das Anwenden setauf B ist harmlos, das Anwenden auf Aund Verwenden des Ergebnisses anstelle des Originals Ajedoch nicht.
Mark Reed

1
@NeilG Betrachten Sie die Zeit, die für die Erstellung des Sets benötigt wird? In meinem Fall (beide Listen haben ungefähr 10 Millionen Zeichenfolgen) ist die Zeit zum Erstellen und Subtrahieren von zwei Sätzen erheblich länger als das Erstellen eines Satzes und das Durchlaufen der Liste.
Dimril

@dimril Wenn Sie das möchten, sollten Sie vielleicht etwas Anspruchsvolleres implementieren. Sie können beispielsweise beide Listen O sortieren (n log n + m log m) und dann die zweite Liste durchlaufen, aber die binäre Suche verwenden, um die Elemente in der ersten Liste zu finden. Es würde zu O (n log n + m log m + m log n) Operationen kommen (anstelle von O (n * m) Operationen), was nicht allzu schlecht zu sein scheint. Stellen Sie einfach sicher, dass Sie nach Nachbarn suchen, um auch Duplikate in Ihren binären Suchimplementierungen zu entfernen. Es könnte sogar ein Paket geben, das dies bereits implementiert, aber ich habe es nicht überprüft.
Jaaq

363

Wenn die Reihenfolge keine Rolle spielt, können Sie einfach die eingestellte Differenz berechnen:

>>> set([1,2,3,4]) - set([2,5])
set([1, 4, 3])
>>> set([2,5]) - set([1,2,3,4])
set([5])

9
Dies ist bei weitem die beste Lösung. Testfälle an Listen mit jeweils ~ 6000 Zeichenfolgen zeigten, dass diese Methode fast 100-mal schneller war als das Listenverständnis.
Perrygeo

15
Abhängig von der Anwendung: Wenn die Erhaltung von Ordnung oder Vervielfältigung wichtig ist, hat Roman Bodnarchuk möglicherweise einen besseren Ansatz. Für Geschwindigkeit und reines Set-ähnliches Verhalten scheint dieses besser zu sein.
Bryan P

7
Wenn Sie mehrere gleiche Elemente in der Liste haben, funktioniert diese Lösung nicht.
Karantan

Weitaus besser als das Listenverständnis.
Dawei

4
Diese Lösung scheint so offensichtlich, aber sie ist falsch. Es tut mir Leid. Natürlich meinen wir, dass eine Liste gleiche Elemente wiederholen kann. Ansonsten fragen wir nach dem Unterschied zwischen Sätzen, nicht nach dem Listenunterschied.
Sergzach

67

Sie können eine tun

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

und

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

7
Aber wenn A = [1,1,1] und B = [0], dann gibt dies [1] zurück
Mark Bell

1
@ Mark Bell: Das liegt daran, dass ein Set eine eindeutige Liste ist. (entfernt Duplikate)
bewölkt

1
@cloudy Dann beantwortet dies die Frage nicht.
samm82

@ samm82 wenn A = [1,1,1] als set (A) ist [1], weil set eine eindeutige Liste ist und Duplikate entfernt. Deshalb gibt es [A] zurück, wenn A = [1,1,1] und B = [0].
bewölkt

29

Einzeiler:

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

Oder:

diff = lambda l1,l2: filter(lambda x: x not in l2, l1)
diff(A,B)
diff(B,A)

14

Die obigen Beispiele trivialisierten das Problem der Berechnung von Differenzen. Angenommen, das Sortieren oder Deduplizieren erleichtert die Berechnung des Unterschieds auf jeden Fall. Wenn sich Ihr Vergleich diese Annahmen jedoch nicht leisten kann, benötigen Sie eine nicht triviale Implementierung eines Diff-Algorithmus. Siehe difflib in der Python-Standardbibliothek.

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


1
Sie erhalten +1 für Difflib, was ich vorher noch nicht gesehen hatte. Trotzdem stimme ich nicht zu, dass die obigen Antworten das Problem wie angegeben trivialisieren .
Rbp

Vielen Dank, dass Sie difflib verwenden. Ich habe nach einer Lösung mit der Standardbibliothek gesucht. Dies funktioniert jedoch nicht in Python 3, da printes von einem Befehl zu einer Funktion geändert reducewurde filterund mapals unpythonisch deklariert wurde. (Und ich denke, Guido kann Recht haben - ich verstehe auch nicht, was reducetut.)
Post169

Keine große Verschiebung, damit es für py3 funktioniert. Ich habe die Debatte über Filter, Map, Reduce gelesen und bin mit der Entscheidung einverstanden, Reduce und Alternate von Filter in Functools zu verschieben. Die gemischte funktionale, OO- und prozedurale Natur von Python war schon immer eine seiner Stärken, IMO.
Kevin

14

Python 2.7.3 (Standard, 27. Februar 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())))

Ergebnisse:

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

@ roman-bodnarchuk Listenverständnis Funktion def diff (a, b) scheint schneller zu sein.


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 


5

Für den Fall, dass der Unterschied rekursiv tief in Elemente Ihrer Liste einfließen soll, habe ich ein Paket für Python geschrieben: https://github.com/erasmose/deepdiff

Installation

Von PyPi installieren:

pip install deepdiff

Wenn Sie Python3 sind, müssen Sie auch Folgendes installieren:

pip install future six

Anwendungsbeispiel

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

Das gleiche Objekt wird leer zurückgegeben

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

Der Typ eines Elements hat sich geändert

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

Der Wert eines Artikels hat sich geändert

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

Artikel hinzugefügt und / oder entfernt

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

String Unterschied

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

Saitendifferenz 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

Typänderung

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

Listenunterschied

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

Listenunterschied 2: Beachten Sie, dass die Reihenfolge NICHT berücksichtigt wird

>>> # 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)
    { }

Liste mit Wörterbuch:

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

5

einfachster Weg,

benutze set (). different (set ())

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

Antwort ist set([1])


2

Bei einer Liste von Wörterbüchern funktioniert die vollständige Listenverständnislösung, während die setLösung ausgelöst wird

TypeError: unhashable type: 'dict'

Testfall

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

0

Einfacher Code, der Ihnen den Unterschied zu mehreren Elementen gibt, wenn Sie dies möchten:

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)

-1

Wenn Sie sich TimeComplexity of In-Operator ansehen, funktioniert es im schlimmsten Fall mit O (n). Auch für Sets.

Wenn wir also zwei Arrays vergleichen, haben wir im besten Fall eine Zeitkomplexität von O (n) und im schlechtesten Fall von O (n ^ 2).

Eine alternative (aber leider komplexere) Lösung, die im besten und im schlechtesten Fall mit O (n) funktioniert, ist diese:

# 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

z.B

>>> a=[1,2,3]
>>> b=[2,4,6]
>>> diff(a, b, cmp)
[1, 3]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.