Python, hesaplama listesi farkı


195

Python'da iki liste arasındaki farkı hesaplamanın en iyi yolu nedir?

misal

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

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

Yanıtlar:


206

setÖğelerin siparişini veya tekrarını umursamıyorsanız kullanın . Aşağıdakileri yaparsanız liste kavramalarını kullanın :

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

32
set(b)Algoritmanın Theta (n ^ 2) yerine O (nlogn) olduğundan emin olmak için kullanmayı düşünün
Neil G

8
@ Pencilcheck - A'daki sipariş veya çoğaltmayı önemsiyorsanız set, B'ye başvurmak zararsızdır, ancak Aorijinal yerine sonuca uygulamak ve sonucu kullanmak Adeğildir.
Mark Reed

1
@NeilG Seti kurmak için harcanan zamanı düşünüyor musunuz? Benim durumumda (her iki listenin de yaklaşık 10M dizeleri vardır) iki set oluşturmak ve çıkarmak için bir set oluşturmak ve liste üzerinde tekrarlamaktan çok daha büyüktür.
dimril

@dimril yapmak istediğiniz şeyse belki daha karmaşık bir şey uygulamalısınız. Örneğin, her iki listeyi de O (n log n + m log m) olarak sıralayabilir ve sonra ikinci liste üzerinde yineleyebilirsiniz, ancak ilk listedeki öğeleri bulmak için ikili aramayı kullanabilirsiniz. Çok kötü görünmeyen O (n log n + m log m + m log n) operasyonlarına (O (n * m) operasyonlar yerine) çıkacaktır. Sadece ikili arama uygulamalarınızdaki yinelemeleri ortadan kaldırmak için komşuları kontrol ettiğinizden emin olun. Bunu zaten uygulayan bir paket bile olabilir, ama kontrol etmedim.
jaaq

366

Sipariş önemli değilse, ayarlanan farkı hesaplayabilirsiniz:

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

9
Bu açık ara en iyi çözümdür. ~ 6000 dizeli listelerdeki test senaryosu, bu yöntemin liste kavrayışlarından neredeyse 100 kat daha hızlı olduğunu göstermiştir.
perrygeo

15
Uygulamaya bağlıdır: sipariş veya çoğalmanın korunması önemliyse Roman Bodnarchuk daha iyi bir yaklaşıma sahip olabilir. Hız ve saf set benzeri davranış için bu daha iyi görünüyor.
Bryan P

7
Listede birden fazla eşit öğeniz varsa bu çözüm çalışmaz.
karantan

Liste kavrayışından çok daha iyi.
Dawei

4
Bu çözüm çok açık görünüyor ama yanlış. Üzgünüm. Tabii ki bir listenin tekrarlanan eşit elemanlara sahip olabileceğini kastediyoruz. Aksi takdirde, setler arasındaki farkı değil, setler arasındaki farkı soruyoruz.
sergzach

67

Yapabilirsin

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

ve

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

7
Ancak A = [1,1,1] ve B = [0] ise bu [1] değerini döndürür
Mark Bell

1
@Mark Bell: Bu bir setin ayrı bir liste olduğu için. (yinelemeleri kaldırır)
bulutlu

1
@cloudy O zaman bu soruya cevap vermiyor.
samm82

@ samm82, A = [1,1,1], set (A) 'dan daha [1] ise, set ayrı bir listedir ve kopyaları kaldırır. Bu nedenle, A = [1,1,1] ve B = [0] ise [1] değerini döndürür.
bulutlu

29

Bir astar:

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

Veya:

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

14

Python 2.7.3 (varsayılan, 27 Şub 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())))

Sonuçlar:

# 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 liste anlama fonksiyonu def diff (a, b) daha hızlı görünüyor.


14

Yukarıdaki örnekler farklılıkları hesaplama problemini önemsizleştirmiştir. Sıralama veya tekilleştirme işleminin kesinlikle farkını hesaplamayı kolaylaştırdığı varsayılır, ancak karşılaştırmanız bu varsayımları karşılayamıyorsa, fark algoritmasının önemsiz bir uygulamasına ihtiyacınız olacaktır. Bkz. Python standart kütüphanesindeki difflib.

#! /usr/bin/python2
from difflib import SequenceMatcher

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

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

Çıktı:

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

1
daha önce görmediğim difflib için +1 alırsınız. Bununla birlikte, yukarıdaki cevapların sorunu belirtildiği gibi önemsizleştirdiğini kabul etmiyorum .
rbp

Difflib kullandığınız için teşekkürler - standart kütüphaneyi kullanarak bir çözüm arıyordum. Ancak, bu Python 3 çalışan olarak değil printbir işleve bir komutundan değişti ve reduce, filterve mapunpythonic ilan edilmiştir. (Ve Guido'nun haklı olabileceğini düşünüyorum - Ne yaptığını da anlamıyorum reduce.)
Post169

Py3 için çalışmasını sağlamak için büyük bir değişiklik değil. Filtre üzerindeki tartışmayı okudum, haritaladım, azalttım ve filtrenin functoollere alternatif impl'yi itme seçeneğini kabul ediyorum. Python'un karışık fonksiyonel, OO ve prosedürel doğası her zaman güçlü yanlarından biri olan IMO olmuştur.
Kevin

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

Farkın tekrar tekrar listenizdeki öğelere girmesini istiyorsanız, python için bir paket yazdım: https://github.com/erasmose/deepdiff

Kurulum

PyPi'den yükle:

pip install deepdiff

Python3 iseniz ayrıca yüklemeniz gerekir:

pip install future six

Örnek kullanım

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

Aynı nesne boş döner

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

Bir öğenin türü değişti

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

Bir öğenin değeri değişti

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

Öğe eklendi ve / veya kaldırıldı

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

Dize farkı

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

Dize farkı 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

Tür değişikliği

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

Liste farkı

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

Liste farkı 2: Siparişi dikkate almadığını unutmayın

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

Sözlük içeren liste:

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

en basit yol,

set () kullanın . fark (set ())

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

cevap set([1])


2

Bir sözlük listesi olması durumunda , tam liste anlama çözümü setçözüm yükselirken çalışır

TypeError: unhashable type: 'dict'

Test durumu

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

İsterseniz birden çok öğe ile size fark veren basit kod:

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

TimeComplexity'ye bakarkenOperatörün en kötü durumda O (n) ile çalışır. Setler için bile.

İki diziyi karşılaştırdığımızda, en iyi durumda O (n) TimeComplexity ve en kötü durumda O (n ^ 2) olacaktır.

O (n) ile en iyi ve en kötü durumda çalışan alternatif (ama ne yazık ki daha karmaşık) bir çözüm şudur:

# 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

Örneğin

>>> a=[1,2,3]
>>> b=[2,4,6]
>>> diff(a, b, cmp)
[1, 3]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.