Bir listede meydana gelen tüm elemanları diğerinden kaldır


365

Diyelim ki iki listem var l1ve l2. Gerçekleştirmek istemiyorum l1 - l2, ki içinde l1olmayan tüm unsurları döndürür l2.

Bunu yapmak için saf bir döngü yaklaşımı düşünebilirim, ama bu gerçekten verimsiz olacak. Bunu yapmanın pitonik ve etkili bir yolu nedir?

Örnek olarak, eğer varsa l1 = [1,2,6,8] and l2 = [2,3,5,8], l1 - l2geri dönmeliyim[1,6]


12
Sadece bir ipucu: PEP8 , küçük harf "L" nin 1'e çok benzediğinden kullanılmaması gerektiğini
belirtir

2
Katılıyorum. Bu soruyu ve insanların neden on bir ve on iki kişiyi kullanmaya devam ettiğini merak eden cevapları okudum. Sadece @spelchekr'in yorumunu okuduğumda mantıklıydı.
robline


@JimG. Veri çerçevesi ve liste aynı şey değildir.
etkinliğini azaltma

Yanıtlar:


491

Python, bu tür şeyleri son derece kolay hale getirmek için mükemmel şekilde uygun olan List Comprehensions adlı bir dil özelliğine sahiptir . Aşağıdaki ifade tam olarak ne istediğinizi yapar ve sonucu şu şekilde saklar l3:

l3 = [x for x in l1 if x not in l2]

l3içerir [1, 6].


8
Çok pitonik; Bunu sevdim! Ne kadar verimli?
fandom

2
Oldukça verimli olduğuna inanıyorum ve ne yapmaya çalıştığınız konusunda son derece okunabilir ve net olmanın faydası var. Verimlilik ile ilgili ilginç bulabileceğiniz bir blog yazısı ile karşılaştım: blog.cdleary.com/2010/04/efficiency-of-list-comprehensions
Donut

6
@fandom: liste kavramanın kendisi oldukça verimlidir (ancak bir jeneratör kavraması bellekteki öğeleri çoğaltarak daha verimli olabilir), ancak inoperatör bir listede o kadar verimli değildir. inbir listede O (n), inbir kümede O (1) bulunur. Ancak, binlerce öğeye veya daha fazlasına ulaşıncaya kadar, farkı fark edemezsiniz.
Daniel Pryden

1
l3 = [x for x in l1 if x not in set(l2)]? Bir set(l2)kereden fazla çağrılıp çağrılmayacağından eminim .
Danosaure

5
Sadece ayarlayıp da l2s = set(l2)söyleyebilirsiniz l3 = [x for x in l1 if x not in l2s]. Biraz daha kolay.
spelchekr

149

Bunun bir yolu setleri kullanmaktır:

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

58
Bu, l1istenmeyen bir yan etki olabilecek kopyaları da kaldıracaktır .
tür

37
..ve eleman sırasını kaybet (sipariş önemliyse).
Danosaure

3
Sadece kabul cevap vs bu aşımına ve 3 hakkında kat daha fazla ölçülebilir olduğunu eklemek istiyorum: timeit.timeit('a = [1,2,3,4]; b = [1,3]; c = [i for i in a if a not in b]', number=100000) -> 0.12061533199999985 timeit.timeit('a = {1,2,3,4}; b = {1,3}; c = a - b', number=100000) -> 0.04106225999998969. Performans önemli bir
faktörse

37

Alternatif olarak, istenen sonucu elde etmek için lambda ifadesi ile de kullanabilirsinizfilter . Örneğin:

>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])

#     v  `filter` returns the a iterator object. Here I'm type-casting 
#     v  it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]

Performans karşılaştırması

Burada, burada bahsedilen tüm cevapların performansını karşılaştırıyorum. Beklendiği gibi, Arkku'nun set operasyonu en hızlı.

  • Arkku'nun Set Farkı - İlk (döngü başına 0.124 usec)

    mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
    10000000 loops, best of 3: 0.124 usec per loop
  • Daniel Pryden'in setArama ile Liste Anlayışı - İkinci (döngü başına 0.302 usec)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]"
    1000000 loops, best of 3: 0.302 usec per loop
  • Düz Listedeki Donut List Anlayışı - Üçüncü (döngü başına 0,552 usec)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
    1000000 loops, best of 3: 0.552 usec per loop
  • Moinuddin Quadri kullanımıfilter - Dördüncü (döngü başına 0,972 usec)

    mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)"
    1000000 loops, best of 3: 0.972 usec per loop
  • Akshay Hazari, reduce+filter - Beşinci (döngü başına 3,97 usec) kombinasyonunu kullanıyor

    mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
    100000 loops, best of 3: 3.97 usec per loop

PS: set sırası korumaz ve yinelenen öğeleri listeden kaldırır. Bu nedenle, bunlardan herhangi birine ihtiyacınız varsa set farkını kullanmayın .


32

Donut'un yanıtı ve diğer yanıtları genişleterek, liste kavraması yerine bir jeneratör kavrayışı kullanarak ve bir setveri yapısı (in operatör listede O (n), ancak O (1) olduğu bir sette).

İşte size uygun bir işlev:

def filter_list(full_list, excludes):
    s = set(excludes)
    return (x for x in full_list if x not in s)

Sonuç, filtrelenmiş listeyi tembel olarak getirecek bir yinelenebilir olacaktır. Gerçek bir liste nesnesine ihtiyacınız varsa (örneğin len(), sonuçta bir yapmanız gerekiyorsa ), aşağıdaki gibi kolayca bir liste oluşturabilirsiniz:

filtered_list = list(filter_list(full_list, excludes))

29

Python set türünü kullanın. Bu en Pythonic olurdu. :)

Ayrıca, yerel olduğundan, en iyi yöntem olmalıdır.

Görmek:

http://docs.python.org/library/stdtypes.html#set

http://docs.python.org/library/sets.htm (eski python için)

# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2

5
Setler kullanılırken, çıkışının sıralandığı, yani {1,3,2} {1,2,3} ve {"A", "C", "B"} {"A" olduğu, "B", "C"} ve buna sahip olmak istemeyebilirsiniz.
Pablo Reyes

2
Liste l1tekrarlanan öğeler içeriyorsa bu yöntem çalışmaz .
jdhao

10

ayarlamak için Anlamaları Ayarla {x için x için x kullanın } veya ayarlamak için (l2) kullanın, sonra listeyi almak için Anlama Listesi'ni kullanın

l2set = set(l2)
l3 = [x for x in l1 if x not in l2set]

Benchmark test kodu:

import time

l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))

l2set = {x for x in l2}

tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)

tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)

print("speedup %fx"%(difflist/diffset))

Benchmark test sonucu:

0.0015058517456054688
3.968189239501953
speedup 2635.179227x    

1
l2set = set( l2 )yerinel2set = { x for x in l2 }
cz

1
Güzel ruh! Ancak, yalnızca yıkanabilir nesnelerle çalıştığı unutulmamalıdır.
Eerik Sven Puudist

7

Alternatif Çözüm:

reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])

2
Bu yöntemi kullanmanın herhangi bir avantajı var mı? Görünüşe göre çok daha fazla yarar olmadan okumak daha karmaşık ve zor.
skrrgwasme

Bu karmaşık görünebilir. Azaltmak çok esnektir ve birçok amaç için kullanılabilir. Katlama olarak bilinir. azaltmak aslında foldl olduğunu. İçinde daha karmaşık şeyler eklemek istediğinizi varsayalım, o zaman bu fonksiyonda mümkün olacak, ancak seçilen en iyi cevap olan liste kavrama size sadece aynı türden bir çıktı elde edecek, yani liste ve muhtemelen aynı uzunlukta bir kıvrım elde edebilirsiniz. çıktı türünü de değiştirin. en.wikipedia.org/wiki/Fold_%28higher-order_function%29 . Bu çözelti n * m veya daha az karmaşıklıktır. Diğerleri daha iyi olabilir veya olmayabilir.
Akshay Hazari

1
azaltmak (fonksiyon, liste, ilk akümülatör (herhangi bir tipte olabilir))
Akshay Hazari
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.