Uzyskaj różnicę między dwiema listami

Mam dwie listy w Pythonie, takie jak te:

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

Muszę utworzyć trzecią listę z elementami z pierwszej listy, których nie ma w drugiej. Z przykładu muszę pobrać:

temp3 = ['Three', 'Four']

Czy są jakieś szybkie sposoby bez cykli i sprawdzania?

Author: denfromufa, 0000-00-00

18 answers

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

Beware that

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

Gdzie można oczekiwać / chcieć, aby było równe set([1, 3]). Jeśli chcesz set([1, 3]) jako swoją odpowiedź, musisz użyć 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-19 11:50:55

Wszystkie istniejące rozwiązania oferują jedno lub drugie z:

  • szybsza niż wydajność O (n*m).
  • Zachowaj kolejność wprowadzania listy.

Ale jak dotąd żadne rozwiązanie nie ma obu. Jeśli chcesz oba, spróbuj tego:

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

Test wydajności

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)

Wyniki:

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

Przedstawiona przeze mnie metoda, jak również zachowanie porządku, jest również (nieco) szybsza od odejmowania zbioru, ponieważ nie wymaga budowy niepotrzebnego zbioru. Na różnica w wydajności byłaby bardziej zauważalna, jeśli pierwsza lista jest znacznie dłuższa niż druga i jeśli hashowanie jest kosztowne. Oto drugi test pokazujący to:

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

Wyniki:

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/doraprojects.net/template/agent.layouts/content.php on line 54
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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 19:40:27

W przypadku, gdy chcesz rekurencyjnie różnicę, napisałem pakiet dla Pythona: https://github.com/seperman/deepdiff

Instalacja

Zainstaluj z PyPi:

pip install deepdiff

Przykładowe użycie

Importowanie

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

Ten sam obiekt zwraca pusty

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

Rodzaj elementu uległ zmianie

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

Wartość elementu uległa zmianie

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

Pozycja dodana i / lub usunięta

>>> 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 różnica

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

Zmiana typu

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

Lista różnic

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

Różnica listy 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}}}

Lista różnic ignorujących kolejność lub duplikaty: (z tymi samymi słownikami jak powyżej)

>>> 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 zawierająca słownik:

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

Zestawy:

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

Nazwane Krotki:

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

Obiekty własne:

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

Atrybut obiektu dodany:

>>> 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 21:57:35

Różnicę między dwoma listami (powiedzmy list1 i list2) można znaleźć za pomocą następującej prostej funkcji.

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)

Lub

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

Używając powyższej funkcji, różnicę można znaleźć za pomocą diff(temp2, temp1) lub diff(temp1, temp2). Oba dadzą wynik ['Four', 'Three']. Nie musisz się martwić o kolejność listy ani o to, która lista ma być podana jako pierwsza.

Python Doc reference

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-05 12:07:17

Jeśli naprawdę szukasz wydajności, użyj numpy!

Oto pełny notatnik jako gist na github z porównaniem list, numpy i pandy.

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

Tutaj wpisz opis obrazka

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-29 07:04:12

Dorzucę ponieważ żadne z obecnych rozwiązań nie daje krotki:

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

Alternatywnie:

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

Podobnie jak inne nieprostkowe odpowiedzi w tym kierunku, zachowuje porządek

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 20:57:18

Można wykonać za pomocą operatora XOR Pythona.

  • spowoduje to usunięcie duplikatów z każdej listy
  • to pokaże różnicę temp1 od temp2 i temp2 od 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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-19 05:25:16

Najprostszy sposób,

Użyj set ().różnica (zestaw())

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

Odpowiedź brzmi set([1])

Może drukować jako listę,

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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-30 12:59:00

To może być nawet szybsze niż zrozumienie listy Marka:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-21 00:22:48

Spróbuj tego:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-11 19:39:59

Chciałem czegoś, co zajmie dwie listy i może zrobić to, co diff w bash robi. Ponieważ to pytanie pojawia się najpierw podczas wyszukiwania "python diff dwie listy" i nie jest bardzo szczegółowe, opublikuję to, co wymyśliłem.

Za pomocą SequenceMather z difflib możesz porównać dwie listy, jak robi to diff. Żadna z pozostałych odpowiedzi nie powie Ci, gdzie występuje różnica, ale ta tak. Niektóre odpowiedzi dają różnicę tylko w jednym kierunku. Niektóre uporządkować żywioły. Niektórzy nie zajmują się duplikatami. Ale to rozwiązanie daje prawdziwą różnicę między dwiema listami:

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

To wyjście:

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

Oczywiście, jeśli Twoja aplikacja zakłada te same założenia, co inne odpowiedzi, skorzystasz z nich najbardziej. Ale jeśli szukasz prawdziwej funkcjonalności diff, to jest to jedyny sposób, aby przejść.

Na przykład żadna z innych odpowiedzi nie mogła sobie poradzić:

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

Ale ten Tak:

  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/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-07 22:35:27

Możesz użyć naiwnej metody, jeśli elementy listy difflist są sortowane i ustawiane.

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

print list1[len(list2):]

Lub z natywnymi metodami set:

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)

: 0.0787101593292

Natywne rozwiązanie zestawu: 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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-29 13:21:24

Oto Counter odpowiedź na najprostszy przypadek.

Jest krótszy niż ten powyżej, który robi dwukierunkowe różnice, ponieważ robi tylko dokładnie to, co zadaje pytanie: generuje listę tego, co jest na pierwszej liście, ale nie na drugiej.

from collections import Counter

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

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

Alternatywnie, w zależności od preferencji czytelności, to sprawia, że przyzwoity jednolinijkowy:

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

Wyjście:

['Three', 'Four']

zauważ, że możesz usunąć wywołanie list(...), Jeśli tylko je powtarzasz.

Ponieważ To rozwiązanie wykorzystuje liczniki, obsługuje ilości poprawnie w porównaniu z wieloma odpowiedziami na podstawie zestawów. Na przykład na tym wejściu:

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

Wyjście to:

['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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-07 03:26:27

Jeśli napotkasz TypeError: unhashable type: 'list' musisz zamienić listy lub zestawy w krotki, np.

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

Zobacz także Jak porównać listę list/zestawów w Pythonie?

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

To jest inne rozwiązanie:

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-29 02:36:31

Single line version of arulmr solution

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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-26 12:02:44

Jeśli chcesz coś bardziej jak zestaw zmian... could use Counter

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/doraprojects.net/template/agent.layouts/content.php on line 54