Python listesinin çıkarılması işlemi


227

Buna benzer bir şey yapmak istiyorum:

>>> x = [1,2,3,4,5,6,7,8,9,0]  
>>> x  
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]  
>>> y = [1,3,5,7,9]  
>>> y  
[1, 3, 5, 7, 9]  
>>> y - x   # (should return [2,4,6,8,0])

Ama bu python listeleri tarafından desteklenmiyor Bunu yapmanın en iyi yolu nedir?


@ezdazuzena bu çıkarma değildir. İki liste arasındaki fark budur. Paylaşımınız bu sorunun bir yayını değildir.
Çelik

1
[2, 2] - [2] ne dönmeli? []? [2]?
McKay

@McKay [2,2] - [2] geri dönmelidir [2]. [2,2] - [1,2,2,3] döndürmelidir []
Robino

Bu soru liste çıkarma ile ilgilidir, ancak kabul edilen cevap çıkarma için daha yakındır.
Robino

2
[2, 1, 2, 3, 2, 4, 2] - [2, 3, 2] ne dönmeli ve neden? 232'yi ortada bulup 2142'ye dönmeli mi? ya da her seferinde ilkini bulup 1242'ye dönmeli mi? Veya başka bir şey? Söylediğim, bunların açık cevaplar olmaması ve ihtiyaca bağlı olması.
McKay

Yanıtlar:


330

Bir liste kavrayışı kullanın:

[item for item in x if item not in y]

-Infix sözdizimini kullanmak istiyorsanız , şunları yapabilirsiniz:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(args)

    def __sub__(self, other):
        return self.__class__(*[item for item in self if item not in other])

daha sonra şu şekilde kullanabilirsiniz:

x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y   

Ancak liste özelliklerine (örneğin, sipariş) kesinlikle ihtiyacınız yoksa, diğer yanıtların önerdiği gibi setleri kullanın.


10
@ admica, yapıcıyı gölgelediği listiçin değişken adları için kullanmayın list. 'List' kullanırsanız, lütfen önce alt çizgi kullanın. Ayrıca, bırakarak, *kodumu kırdın ...
aaronasterling

19
Bunu yaparsanız [1,1,2,2] - [1,2]boş liste alırsınız. [1,1,2,2] - [2]verir [1,1]Yani gerçekten liste çıkarma değil, daha çok " Y setinden elemanlar olmadan Liste X'den Liste " gibidir .
Alfred Zien

@AlfredZien ne dedi
RetroCode

Liste kavrama yöntemi (örneğimde) ayarlanan fark yönteminden çok daha yavaştır.
redfiloux

1
@BarnabasSzabolcs: Kullanıcıların dönüşüm Bunun sebebi, bir şeyi kurtarmak olmaz ybir karşı setönce her (özgün çalışma benzer maliyet olan) çek. yset = set(y)Listcomp dışında bir şeyler yapmanız, daha sonra test etmeniz ya da tek satırlık if item not in ysetbir [item for yset in [set(y)] for item in x if item not in yset]önbellek ysetolarak önbelleğe almak için iç içe listcomps kötüye kullanımı yapmanız gerekir . Yeterli performans gösteren biraz daha az çirkin tek katmanlı bir çözüm kullanmak olacaktır list(itertools.filterfalse(set(y).__contains__, x))çünkü çünkü argüman filterfalsesadece bir kez oluşturulmuştur.
ShadowRanger

259

Set farkını kullan

>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]

Ya da sadece x ve y setleriniz olabilir, böylece herhangi bir dönüşüm yapmak zorunda kalmazsınız.


50
bu herhangi bir sıralamayı kaybedecektir. Bağlama bağlı olarak önemli olabilir veya olmayabilir.
aaronasterling

63
Bu aynı zamanda bakım gerektirebilecek / korunmasını isteyebilecek olası kopyaları da kaybedecektir.
Opal

AnladımTypeError: unhashable type: 'dict'
Havnar

Listelerin karşılaştırıldığı büyük durumlarda bu çok daha hızlıdır
JqueryToAddNumbers

2
Listedeki öğelerin siparişi ve kopyaları bağlam için önemli değilse, bu harika bir cevaptır ve çok okunabilir.
Watt Iamsuri

37

Bu bir "çıkarma çıkarma" işlemidir. Bunun için ayarlanmış veri yapısını kullanın.

Python 2.7'de:

x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y

Çıktı:

>>> print x - y
set([0, 8, 2, 4, 6])

1
list (set ([1,2,3,4,5]) - set ([1,2,3])) = [4, 5] böylece her biri önce ayarlanacak, ardından çıkarılacak (veya tek yönlü fark) listelenecek ) ve listeye geri dönün.
gseattle

2
X setinin orijinal öğe sırasını korumak istiyorsanız iyi değil.
Zahran


20

Birçok kullanım durumunda, istediğiniz cevap:

ys = set(y)
[item for item in x if item not in ys]

Bu, aaronasterling'in yanıtı ve quantumSoup'un yanıtı arasında bir melez .

aaronasterling'in sürümü, len(y)içindeki her öğe için öğe karşılaştırmaları yapar x, bu nedenle ikinci dereceden zaman alır. quantumSoup versiyonu kullanır setleri, böylece her bir eleman için tek bir sabit zamanlı seti arama yapar xo dönüştürür çünkü -ama her ikisi x ve ykümeler halinde, bu öğelerin sırasını kaybeder.

Sadece ybir sete dönüştürerek ve xsırayla yineleyerek , her iki dünyanın en iyisini elde edersiniz - doğrusal zaman ve sipariş koruması. *


Ancak, bunun hala quantumSoup'un sürümünden bir sorunu var: Öğelerinizin yıkanabilir olmasını gerektirir. Bu, kümelerin doğası üzerine inşa edilmiştir. ** Örneğin, başka bir dikte listesinden bir dikte listesi çıkarmaya çalışıyorsanız, ancak çıkarılacak liste büyükse, ne yapıyorsunuz?

Değerlerinizi yıkanabilir olacak şekilde dekore edebiliyorsanız, bu sorunu çözer. Örneğin, değerleri kendiliğinden yıkanabilir düz bir sözlükle:

ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]

Türleriniz biraz daha karmaşıksa (örneğin, genellikle yıkanabilir olan JSON uyumlu değerlerle veya değerleri özyinelemeli olarak aynı tür olan listeleri veya diktelerle uğraşıyorsanız), bu çözümü kullanmaya devam edebilirsiniz. Ancak bazı türler yıkanabilir bir şeye dönüştürülemez.


Öğeleriniz değilse ve yapılamazsa, yıkanabilir, ancak karşılaştırılabilir durumdaysa, en azından log-lineer zaman alabilirsiniz ( O(N*log M)bu, O(N*M)liste çözümünün zamanından çok daha iyi , ancak o kadar iyi değil) O(N+M)ayırma ve kullanarak ayarlanan çözelti süresi) bisect:

ys = sorted(y)
def bisect_contains(seq, item):
    index = bisect.bisect(seq, item)
    return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]

Eşyalarınız ne yıkanabilir ne de karşılaştırılabilir ise, ikinci dereceden çözümle sıkışıp kalırsınız.


* Bunu OrderedSet, tarifleri ve üçüncü taraf modülleri bulabileceğiniz bir çift nesne kullanarak da yapabileceğinizi unutmayın . Ama bence bu daha basit.

** Set aramalarının sabit zaman olmasının nedeni, tek yapması gereken değerin hash olmasıdır ve bu karma için bir giriş olup olmadığına bakmaktır. Değeri hash edemezse, bu işe yaramaz.


7

Kümelerdeki değerleri aramak, onları listelerde aramaktan daha hızlıdır:

[item for item in x if item not in set(y)]

Bunun biraz daha iyi ölçekleneceğine inanıyorum:

[item for item in x if item not in y]

Her ikisi de listelerin sırasını korur.


Önbellek set(y)ve yher döngüde yeni bir kümeye dönüştürülmeyecek mi? Aksi takdirde, ihtiyaç abarnert en iyisisin: ys = set(y); [i for i in x if i not in ys].
Jacktose

2
Bazı kaba testler, if i not in set(y)% 25 daha uzun sürdüğünü göstermektedir if i not in y( ybir liste nerede ). Setin önceden dönüştürülmesi% 55 daha az zaman alır. Oldukça kısa olan test edilmiş xve yfakat eğer bir şey fark daha uzunluğunda ile telaffuz almalısınız.
Jacktose

1
@Jacktose: Evet, bu çözüm daha fazla iş yapar, çünkü her elementi yiçin her elementi tekrarlamalı ve hash etmelidir x; Eşitlik karşılaştırması karma hesaplamaya göre gerçekten pahalı değilse, bu her zaman ovaya kaybedecektir item not in y.
ShadowRanger

@ShadowRanger mantıklı. Ayarlanmış dönüşüm, bu kontrolü yapmanın güvenilir bir şekilde daha hızlı bir yoluysa, derleyicinin her zaman bu şekilde kontrolü yapacağını düşünürsünüz.
Jacktose

5

Listeler yinelenen öğelere izin veriyorsa koleksiyonlardaki Sayacı kullanabilirsiniz:

from collections import Counter
result = list((Counter(x)-Counter(y)).elements())

X öğelerinin sırasını korumanız gerekirse:

result = [ v for c in [Counter(y)] for v in x if not c[v] or c.subtract([v]) ]

Siparişi kaybetmesine rağmen bu iyidir; bu biraz daha karmaşıktır .
ShadowRanger

@ShadowRanger, gerçekten. ama sadece biraz.
Alain T.

Sakıncası yok, sadece önbellekleme ve yan etkilerle listcomps'ta titreyeceğim (her ikisinin kombinasyonunun harici olarak görünen yan etkileri kaldırdığını varsayalım?). :-)
ShadowRanger

Ayrıca, bu kod yazıldığı gibi çalışmaz; Counter.subtractsıfır değerli öğeleri kaldırmak (vermez -ve -=yapmak değil, subtractsen unsurları kaldırarak durdurmak asla bu yüzden,). Sen değiştirmek isterdim not v in cile not c[v](güvenli "zeroiness" üzerinden için dönüşünü test böylece döner, varolmayan elemanları sıfır olan not).
ShadowRanger

@ShadowRanger, İyi yakala! Şimdi düzeltildi.
Alain T.

3

Bunu başarmanın en kolay yolunun set () kullanmak olduğunu düşünüyorum.

>>> x = [1,2,3,4,5,6,7,8,9,0]  
>>> y = [1,3,5,7,9]  
>>> list(set(x)- set(y))
[0, 2, 4, 6, 8]

3

Diğer çözümlerin birkaç sorunu var:

  1. Düzeni korumazlar veya
  2. Onlar için örneğin elemanları, kesin bir sayımını çıkarmayın x = [1, 2, 2, 2]ve y = [2, 2]onlar dönüştürmek ybir etmek setve ya (bırakarak tüm eşleşen öğeleri kaldırmak [1](bırakarak her benzersiz elemanın birini sadece) veya kaldırmak [1, 2, 2]kaldırmak için uygun bir davranış olur), 2iki kez ayrılmak [1, 2], veya
  3. Onlar yapmak O(m * n)optimal çözüm yapabileceği işi, O(m + n)çalışmayı

Alain,Counter # 2 ve # 3'ü çözmek için doğru yoldaydı , ancak bu çözüm siparişini kaybedecek. Düzeni koruyan çözüm (kaldırılacak değerlerin yinelemeleri niçin her değerin ilk kopyalarının kaldırılması):nlist

from collections import Counter

x = [1,2,3,4,3,2,1]  
y = [1,2,2]  
remaining = Counter(y)

out = []
for val in x:
    if remaining[val]:
        remaining[val] -= 1
    else:
        out.append(val)
# out is now [3, 4, 3, 1], having removed the first 1 and both 2s.

Çevrimiçi deneyin!

Her öğenin son kopyalarını kaldırmasını sağlamak için fordöngüyü değiştirin ve döngüden çıktıktan hemen sonra for val in reversed(x):ekleyin .out.reverse()for

Oluşturma Counteredilir O(n)açısından ybireyin uzunluğu, yineleme xolduğu O(n)anlamında xuzunluğu s 've Counterüyelik test ve mutasyon vardır O(1)ederken, list.appendamorti edilir O(1)verilen bir ( appendolabilir O(n), ancak birçok yönelik appends, genel Büyük O ortalamaları O(1)daha da azalan beri bunların bir yeniden tahsis edilmesi gerekir), bu yüzden yapılan genel iş O(m + n).

Ayrıca, test yedilerek kaldırılmayan herhangi bir öğe olup olmadığını belirlemek için xtest edebilirsiniz:

remaining = +remaining  # Removes all keys with zero counts from Counter
if remaining:
    # remaining contained elements with non-zero counts

Not: Bu does hashable olmaya değer girilmesi zorunlu ama hashable nesneleri gerektirmez herhangi bir çözüm genel amaçlı değil ya (sayabilir örneğin intsabit uzunlukta diziye s) ya da daha fazlasını yapmak zorunda O(m + n)(işin örn sonraki İyi Big -O bir sıralanmış yapmak olacaktır listdeğişen, benzersiz bir değer / sayım çiftleri O(1) dictiçine aramalarını O(log n)ikili aramalarda; aksi takdirde ödeme olurdum çünkü onların sayıları ile benzersiz değerler gerekirdi sadece benzersiz olmayan değerler sıralanır O(n)maliyetler çıkarmak için öğeleri sıralanır list).
ShadowRanger

2

Bunu dene.

def subtract_lists(a, b):
    """ Subtracts two lists. Throws ValueError if b contains items not in a """
    # Terminate if b is empty, otherwise remove b[0] from a and recurse
    return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:]) 
                                  for i in [a.index(b[0])]][0]

>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0]
>>> x = [1,2,3,4,5,6,7,8,9,0,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0, 9]     #9 is only deleted once
>>>

1

Bakışlar iyi @aaronasterling tarafından sağlanan yanıt, ancak listenin varsayılan arayüzü ile uyumlu değildir: x = MyList(1, 2, 3, 4)vs x = MyList([1, 2, 3, 4]). Böylece, aşağıdaki kod daha python listesi dostu olarak kullanılabilir:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(*args)

    def __sub__(self, other):
        return self.__class__([item for item in self if item not in other])

Misal:

x = MyList([1, 2, 3, 4])
y = MyList([2, 5, 2])
z = x - y

0

Bunun daha hızlı olduğunu düşünüyorum:

In [1]: a = [1,2,3,4,5]

In [2]: b = [2,3,4,5]

In [3]: c = set(a) ^ set(b)

In [4]: c
Out[4]: {1}

Bu çıkarma değil. Aslında, bu iki liste arasındaki simetrik farktır.
Parth Chauhan

Dahası bu sadece listeler içindeki yıkanabilir nesneler için çalışır
zhukovgreen

-1

Bu örnek iki listeyi çıkarır:

# List of pairs of points
list = []
list.append([(602, 336), (624, 365)])
list.append([(635, 336), (654, 365)])
list.append([(642, 342), (648, 358)])
list.append([(644, 344), (646, 356)])
list.append([(653, 337), (671, 365)])
list.append([(728, 13), (739, 32)])
list.append([(756, 59), (767, 79)])

itens_to_remove = []
itens_to_remove.append([(642, 342), (648, 358)])
itens_to_remove.append([(644, 344), (646, 356)])

print("Initial List Size: ", len(list))

for a in itens_to_remove:
    for b in list:
        if a == b :
            list.remove(b)

print("Final List Size: ", len(list))

8
Bundan kaçının, o O (N ^ 2)
Alexander - Monica'yı
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.