it-swarm.com.de

Ermitteln Sie den Unterschied zwischen zwei Listen

Ich habe zwei Listen in Python, wie diese:

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

Ich muss eine dritte Liste mit Elementen aus der ersten Liste erstellen, die in der zweiten nicht vorhanden sind. Aus dem Beispiel muss ich bekommen:

temp3 = ['Three', 'Four']

Gibt es schnelle Wege ohne Zyklen und Kontrolle?

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

Passen Sie das auf

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

wo Sie vielleicht erwarten/wollen, dass es set([1, 3]) entspricht. Wenn Sie set([1, 3]) als Antwort wünschen, müssen Sie set([1, 2]).symmetric_difference(set([2, 3])) verwenden.

1062
ars

Die vorhandenen Lösungen bieten entweder die eine oder die andere der folgenden Möglichkeiten:

  • Schneller als O (n * m).
  • Reihenfolge der Eingabeliste beibehalten.

Aber bisher hat keine Lösung beides. Wenn Sie beides möchten, versuchen Sie Folgendes:

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

Leistungstest

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)

Ergebnisse:

4.34620224079 # ars' answer
4.2770634955  # This answer
30.7715615392 # matt b's answer

Die von mir vorgestellte Methode sowie das Beibehalten der Reihenfolge ist auch (geringfügig) schneller als die Mengen-Subtraktion, da keine unnötige Menge erstellt werden muss. Der Leistungsunterschied würde sich deutlicher bemerkbar machen, wenn die erste Liste erheblich länger als die zweite ist und wenn das Hashing teuer ist. Hier ist ein zweiter Test, der dies demonstriert:

init = '''
temp1 = [str(i) for i in range(100000)]
temp2 = [str(i * 2) for i in range(50)]
'''

Ergebnisse:

11.3836875916 # ars' answer
3.63890368748 # this answer (3 times faster!)
37.7445402279 # matt b's answer
438
Mark Byers
temp3 = [item for item in temp1 if item not in temp2]
64
matt b

Der Unterschied zwischen zwei Listen (z. B. Liste1 und Liste2) kann mit der folgenden einfachen Funktion ermittelt werden.

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)

oder

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

Mit der obigen Funktion kann der Unterschied mit diff(temp2, temp1) oder diff(temp1, temp2) ermittelt werden. Beide geben das Ergebnis ['Four', 'Three']. Sie müssen sich keine Gedanken über die Reihenfolge der Liste machen oder welche Liste zuerst angegeben werden soll.

Python-Doku-Referenz

19
arulmr

Falls Sie den Unterschied rekursiv wollen, habe ich ein Paket für Python geschrieben: https://github.com/seperman/deepdiff

Installation

Installation von PyPi:

pip install deepdiff

Anwendungsbeispiel

Importieren

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

Dasselbe Objekt wird leer zurückgegeben

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

Der Typ eines Artikels hat sich geändert

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

Der Wert eines Artikels hat sich geändert

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

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)
{'dic_item_added': ['root[5]', 'root[6]'],
 'dic_item_removed': ['root[4]'],
 'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

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, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                      "root[4]['b']": { 'newvalue': 'world!',
                                        'oldvalue': '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, 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

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

Listenunterschied

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

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

Listendifferenz ignoriert Reihenfolge oder Duplikate: (mit den gleichen Wörterbüchern wie oben)

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

Liste, die Wörterbuch enthält:

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

Sätze:

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

Benannte Tupel:

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

Benutzerdefinierte Objekte:

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

Objektattribut hinzugefügt:

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

Wenn Sie wirklich Leistung suchen, dann verwenden Sie numpy!

Hier ist das vollständige Notizbuch als Gist on Github mit einem Vergleich zwischen list, numpy und pandas.

https://Gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451

enter image description here

15
denfromufa

ich werfe hinein, da keine der vorliegenden Lösungen einen Tupel ergibt:

temp3 = Tuple(set(temp1) - set(temp2))

alternative:

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

Wie bei den anderen Nicht-Tupeln, die Antworten in diese Richtung liefern, bleibt die Ordnung erhalten

13
aaronasterling

am einfachsten,

benutze set (). difference (set ())

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

antwort ist set([1])

kann als Liste drucken,

print list(set(list_a).difference(set(list_b)))
13

Kann mit dem Operator python XOR ausgeführt werden.

  • Dadurch werden die Duplikate in jeder Liste entfernt
  • Dies zeigt den Unterschied von Temp1 zu Temp2 und Temp2 zu Temp1.

set(temp1) ^ set(temp2)
12
SuperNova

Ich wollte etwas, das zwei Listen benötigt und das tun kann, was diff in bash tut. Da diese Frage zuerst bei der Suche nach "Python Diff Two Lists" auftaucht und nicht sehr spezifisch ist, werde ich veröffentlichen, was ich mir ausgedacht habe.

Mit SequenceMather aus difflib können Sie zwei Listen wie diff vergleichen. Keine der anderen Antworten gibt Aufschluss über die Position, an der der Unterschied auftritt. Einige Antworten geben den Unterschied nur in eine Richtung. Einige ordnen die Elemente neu. Einige verarbeiten keine Duplikate. Diese Lösung bietet Ihnen jedoch einen echten Unterschied zwischen zwei Listen:

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

Dies gibt aus:

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

Wenn Ihre Anwendung dieselben Annahmen wie die anderen Antworten trifft, werden Sie natürlich am meisten davon profitieren. Wenn Sie jedoch nach einer echten diff -Funktionalität suchen, ist dies der einzige Weg.

Zum Beispiel könnte keine der anderen Antworten behandeln:

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

Aber dieser macht:

  2nd has [5, 4, 3, 2]
both have [1]
  1st has [2, 3, 4, 5]
11
arekolek

Versuche dies:

temp3 = set(temp1) - set(temp2)
10
Maciej Kucharz

dies könnte sogar schneller sein als Marks Listenverständnis:

list(itertools.filterfalse(set(temp2).__contains__, temp1))
9
Mohammed

Hier ist eine Counter Antwort für den einfachsten Fall.

Dies ist kürzer als das obige, das Zwei-Wege-Unterschiede ausführt, weil es nur genau das tut, was in der Frage gefragt wird: Erstellen Sie eine Liste der Elemente in der ersten Liste, jedoch nicht in der zweiten.

from collections import Counter

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

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

Abhängig von Ihren Lesbarkeitspräferenzen ergibt sich alternativ ein anständiger Einzeiler:

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

Ausgabe:

['Three', 'Four']

Beachten Sie, dass Sie den list(...) -Aufruf entfernen können, wenn Sie nur darüber iterieren.

Da diese Lösung Zähler verwendet, werden die Mengen im Vergleich zu den vielen satzbasierten Antworten ordnungsgemäß verarbeitet. Zum Beispiel für diesen Eingang:

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

Die Ausgabe ist:

['Two', 'Two', 'Three', 'Three', 'Four']
6
Taylor Edmiston

Sie können eine naive Methode verwenden, wenn die Elemente der Diffliste sortiert und festgelegt sind.

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

print list1[len(list2):]

oder mit nativen Set-Methoden:

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)

Naive Lösung: 0,0787101593292

Native Set-Lösung: 0.998837615564

5
soundcorner

Dafür bin ich etwas zu spät im Spiel, aber Sie können die Leistung einiger der oben genannten Codes damit vergleichen. Zwei der schnellsten Konkurrenten sind:

list(set(x).symmetric_difference(set(y)))
list(set(x) ^ set(y))

Ich entschuldige mich für die elementare Ebene der Codierung.

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])
5
Alex Jacob

Wenn Sie auf TypeError: unhashable type: 'list' stoßen, müssen Sie Listen oder Mengen in Tupel umwandeln, z.

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

Siehe auch Wie vergleiche ich eine Liste von Listen/Sets in Python?

4
guaka

Dies ist eine andere Lösung:

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
4
manhgd

Hier sind einige einfache ordnungserhaltende Möglichkeiten, zwei Listen von Zeichenfolgen zu unterscheiden.

Code

Ein ungewöhnlicher Ansatz mit 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']
_

Dies setzt voraus, dass beide Listen Zeichenfolgen mit gleichem Anfang enthalten. Weitere Informationen finden Sie in docs . Beachten Sie, dass es im Vergleich zu festgelegten Vorgängen nicht besonders schnell ist.


Eine einfache Implementierung mit itertools.Zip_longest :

_import itertools as it


[x for x, y in it.Zip_longest(temp1, temp2) if x != y]
# ['Three', 'Four']
_
4
pylang

einzeilige Version von arulmr solution

def diff(listA, listB):
    return set(listA) - set(listB) | set(listA) -set(listB)
3

wenn du etwas mehr wie ein changeset willst ... könntest du Counter benutzen

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"
3

Dies kann mit einer Zeile gelöst werden. Die Frage ist gegeben, zwei Listen (Temp1 und Temp2) geben ihren Unterschied in einer dritten Liste (Temp3) zurück.

temp3 = list(set(temp1).difference(set(temp2)))
2
fgaim

Wir können Schnittmenge minus Vereinigung von Listen berechnen:

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

set(temp1+temp2)-(set(temp1)&set(temp2))

Out: set(['Four', 'Five', 'Three']) 
2

Nehmen wir an, wir haben zwei Listen

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

aus den beiden obigen Listen können wir ersehen, dass die Elemente 1, 3, 5 in Liste 2 vorhanden sind und die Elemente 7, 9 nicht. Auf der anderen Seite existieren die Punkte 1, 3, 5 in Liste1 und die Punkte 2, 4 nicht.

Was ist die beste Lösung, um eine neue Liste mit den Elementen 7, 9 und 2, 4 zurückzugeben?

Alle Antworten oben finden die Lösung, was ist nun die optimalste?

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

versus

def sym_diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))

Mit timeit können wir die Ergebnisse sehen

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

kehrt zurück

[7, 9, 2, 4]
Using two for loops 0.11572412995155901 Milliseconds
Using symmetric_difference 0.11285737506113946 Milliseconds

Process finished with exit code 0
2
Carson

Hier ist eine einfache Möglichkeit, zwei Listen (unabhängig vom Inhalt) zu unterscheiden. Sie können das folgende Ergebnis erhalten:

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

Hoffe das wird hilfreich.

0
S.K. Venkat