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

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]
>>> 
Roman Bodnarchuk
quelle
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])
Phihag
quelle
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))
Senthil Kumaran
quelle
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)
Artsiom Rudzenka
quelle
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]]

Kevin
quelle
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.

Moreno
quelle
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
quelle
8

Sie möchten a setanstelle von a verwenden list.

Die kommunistische Ente
quelle
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"]}
Seperman
quelle
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])

Mohideen bin Mohammed
quelle
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}]
Joao
quelle
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)
AM
quelle
-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]
DerKnorr
quelle