Python, bir listede diğerinde olmayan öğeleri bulur [yinelenen]


137

Bir listede bulunan ancak diğerinde bulunmayan belirli öğelerin yeni bir listesini oluşturmak için iki listeyi karşılaştırmam gerekiyor. Örneğin:

main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 

List_1 üzerinden döngü yapmak ve list_1'de bulunmayan list_2'deki tüm öğeleri main_list'e eklemek istiyorum.

Sonuç şöyle olmalıdır:

main_list=["f", "m"]

Bunu python ile nasıl yapabilirim?


2
İçinde list_2hiçbir yerde görünmeyen list_1öğeleri veya list_2uygulamasında aynı dizinde bulunmayan öğeleri list_1mi arıyorsunuz?
Patrick Haugh

Yanıtlar:


98

TL; DR:
ÇÖZÜM (1)

import numpy as np
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`

ÇÖZÜM (2) Sıralı bir liste istiyorsunuz

def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans
main_list = setdiff_sorted(list_2,list_1)




AÇIKLAMALAR:
(1) Sen NumPy en kullanabilirsiniz setdiff1d( array1, array2, assume_unique= False).

assume_uniquekullanıcıya dizilerin ZATEN EŞSİZ OLUP OLMADIĞINI sorar.
Eğer Falseöyleyse, önce benzersiz öğeler belirlenir.
Eğer Trueişlev, elemanların zaten benzersiz olduğunu varsayacak VE işlevi benzersiz öğeleri belirlemeyi atlayacaktır.

Bu verimleri benzersiz değerler array1o değil de array2. assume_uniqueolduğu Falsevarsayılan olarak.

Benzersiz öğelerle ilgileniyorsanız (Chinny84'ün yanıtına göre ), o zaman basitçe kullanın (burada assume_unique=False=> varsayılan değer):

import numpy as np
list_1 = ["a", "b", "c", "d", "e"]
list_2 = ["a", "f", "c", "m"] 
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`


(2) Cevapların sıralanmasını isteyenler için özel bir fonksiyon yaptım:

import numpy as np
def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans

Cevabı almak için şunu çalıştırın:

main_list = setdiff_sorted(list_2,list_1)

YAN NOTLAR:
(a) Çözüm 2 (özel işlev setdiff_sorted) bir liste döndürür ( çözüm 1'deki bir diziye kıyasla ).

(b) Öğelerin benzersiz olup olmadığından emin değilseniz, NumPy'nin varsayılan ayarını setdiff1dhem A hem de B çözümlerinde kullanın. Bir komplikasyona örnek ne olabilir? (C) notuna bakın.

İki listelerden birini ise (c) herşey daha farklı olacak değil benzersiz.
De ki list_2benzersiz değil: list2 = ["a", "f", "c", "m", "m"]. Olduğu list1gibi tutun : Verimlerin list_1 = ["a", "b", "c", "d", "e"]
varsayılan değerini ayarlama (her iki çözümde de). ANCAK, eğer ayarlarsanız , her iki çözüm de verir . Neden? Bunun nedeni, kullanıcının öğelerin benzersiz olduğunu varsaymasıdır). Dolayısıyla TUTMAK DAHA İYİassume_unique["f", "m"]assume_unique=True["f", "m", "m"]assume_uniquevarsayılan değerine. Her iki cevabın sıralandığına dikkat edin.


Listeleriniz zaten sıralıysa, bu da sıralı bir liste döndürecektir. Kümelere dönüştürmenin ve ardından farkı elde etmenin yerel çözümü (aşağıda gösterilen çözümler), sonuçlarınızı görsel olarak incelemeyi zorlaştırabilecek sırasız bir liste döndürür.
Doubledown

1
Merhaba @Doubledown! Endişeniz, düzenlenen gönderide ele alınmıştır. Bu yardımcı olur umarım!
jcoderepo

183

Setleri kullanabilirsiniz:

main_list = list(set(list_2) - set(list_1))

Çıktı:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> set(list_2) - set(list_1)
set(['m', 'f'])
>>> list(set(list_2) - set(list_1))
['m', 'f']

@JonClements'ın yorumuna göre, işte daha düzenli bir sürüm:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']

2
Bu, yalnızca uniqueöğeleri önemsiyorsak iyidir, ancak ya birden fazla m'sörneğimiz varsa, bu onu almaz.
Chinny84

Bu doğru. Posterin benzersiz öğeler aradığını varsaydım. Sanırım "özel" ile ne demek istediğine bağlı.
nrlakin

Gerçekten de ps, özellikle belirsiz orijinal bir soru için cevabınızı olumsuz oylamadım.
Chinny84

13
Bunu list(set(list_2).difference(list_1)), açıkça setdönüştürmeyi önleyen bir şekilde yazabilirsiniz ...
Jon Clements

Telaşa gerek yok! Biçimlendirme yardımı için teşekkürler @leaf.
nrlakin

61

Mevcut yerel yöntemleriniz varken yukarıdaki açıklamaların neden bu kadar karmaşık olduğundan emin değilim:

main_list = list(set(list_2)-set(list_1))

6
Bunun nedeni, düzeni korumak olabilir
Keith

58

Bunun gibi bir liste anlayışı kullanın :

main_list = [item for item in list_2 if item not in list_1]

Çıktı:

>>> list_1 = ["a", "b", "c", "d", "e"]
>>> list_2 = ["a", "f", "c", "m"] 
>>> 
>>> main_list = [item for item in list_2 if item not in list_1]
>>> main_list
['f', 'm']

Düzenle:

Aşağıdaki yorumlarda belirtildiği gibi, büyük listelerde yukarıdakiler ideal bir çözüm değildir. Eğer durum olduğunda, daha iyi bir seçenek dönüştürme olurdu list_1a setilk:

set_1 = set(list_1)  # this reduces the lookup time from O(n) to O(1)
main_list = [item for item in list_2 if item not in set_1]

3
Not: Daha büyük için list_1, a set/ ' ye önceden dönüştürmek istersiniz frozenset, örneğin set_1 = frozenset(list_1), daha sonra main_list = [item for item in list_2 if item not in set_1], kontrol süresini O(n)öğe başına (kabaca) düşürmek istersiniz O(1).
ShadowRanger

@ettanany Çözümü ettanany'nin belirttiği gibi denerseniz lütfen dikkatli olun. Ettanany'nin çözümünü olduğu gibi denedim ve daha büyük bir liste için gerçekten çok yavaş. Shadowranger'ın önerisini dahil etmek için yanıtı güncelleyebilir misiniz?
2019

Dize yerine dizini almak mümkün müdür?
JareBear

@JareBear Bunun için kullanabilirsiniz enumerate():[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany

@ ettanany çok teşekkür ederim !! Bunu en kısa zamanda uygulayacağım, yaptım. Ancak kodunuz çok daha temiz görünüyor.
JareBear

6

Yalnızca gerektiren bir tek satırlık bir çözüm (ithalat görmezden) istiyorsanız O(max(n, m))uzunluğunun girişler için çalışmalarını nve mdeğil O(n * m)işin, sen bunu yapabilirsiniz modülü :itertools

from itertools import filterfalse

main_list = list(filterfalse(set(list_1).__contains__, list_2))

Bu, yapım sırasında bir geri arama işlevi alan işlevsel işlevlerden yararlanarak geri aramayı bir kez oluşturmasına ve bir yerde saklamaya gerek kalmadan her öğe için yeniden kullanmasına izin verir (çünkü filterfalsedahili olarak depolar); liste anlayışları ve oluşturucu ifadeler bunu yapabilir, ancak bu çirkin. †

Bu, aşağıdaki gibi tek bir satırda aynı sonuçları alır:

main_list = [x for x in list_2 if x not in list_1]

hızıyla:

set_1 = set(list_1)
main_list = [x for x in list_2 if x not in set_1]

Elbette, karşılaştırmaların konumsal olması amaçlanıyorsa:

list_1 = [1, 2, 3]
list_2 = [2, 3, 4]

üretmeli:

main_list = [2, 3, 4]

(çünkü içindeki hiçbir değer list_2aynı indekste bir eşleşmeye sahip değildir list_1), kesinlikle Patrick'in cevabıyla devam etmelisiniz , bu cevap hiçbir geçici lists veya sets içermemektedir ( sets kabaca olsa bile, O(1)kontrol başına basit eşitlikten daha yüksek bir "sabit" faktöre sahiptirler. kontroller) ve O(min(n, m))diğer yanıtlardan daha az çalışmayı içerir ve sorununuz konuma duyarlıysa, eşleşen öğeler uyumsuz ofsetlerde göründüğünde tek doğru çözümdür.

†: Tek satırlık bir listeyle aynı şeyi yapmanın yolu, "en dıştaki" döngüdeki değerleri oluşturmak ve önbelleğe almak için yuvalanmış döngüyü kötüye kullanmaktır, örneğin:

main_list = [x for set_1 in (set(list_1),) for x in list_2 if x not in set_1]

Bu da Python 3'te küçük bir performans avantajı sağlar (çünkü artık set_1her kontrol için iç içe geçmiş kapsamdan aranmak yerine anlama kodunda yerel olarak kapsama alınmıştır; Python 2'de önemli değil, çünkü Python 2 Anlayışları listeleyin; kullanıldıkları kapsamda çalışırlar).


4
main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"]

for i in list_2:
    if i not in list_1:
        main_list.append(i)

print(main_list)

çıktı:

['f', 'm']

Gibi eşdeğer liste anlama tabanlı bir çözüm ise, bu yavaş olacak list_1büyük ve list_2önemsiz olmayan boyutta olduğu bulaştığı çünkü len(list_2) O(n)taramalarını list_1yapma, O(n * m)(burada nve muzunlukları vardır list_2ve list_1sırasıyla). Dönüştürmek Eğer list_1bir karşı set/ frozensetön, çekler yapılabilir içeren O(1)toplam iş yapma, O(n)uzunluğuna list_2(teknik olarak O(max(n, m)), yapmanız beri O(m)yapmaya çalışması set).
ShadowRanger

1

zipListeleri öğe öğe karşılaştırmak için bir araya getirirdim .

main_list = [b for a, b in zip(list1, list2) if a!= b]

OP öğe öğe karşılaştırmak istiyorsa (net değil, örnek her iki şekilde de olabilir), bu diğer yanıtlardan çok daha etkilidir, çünkü her iki yanıt için tek bir ucuz geçiştir, listtek bir yeni listinşa edilir, ek geçiciler yoktur. , pahalı kontroller yok, vb.
ShadowRanger

1
@ShadowRanger bu sadece temel bir nokta olan element-bilge fark için işe yarar
ford prefect

@fordprefect: Evet. Kendi cevabım konumdan bağımsız farklılıkları kapsıyor.
ShadowRanger

1

İki yöntem kullandım ve bir yöntemi diğerine göre yararlı buldum. İşte cevabım:

Giriş verilerim:

crkmod_mpp = ['M13','M18','M19','M24']
testmod_mpp = ['M13','M14','M15','M16','M17','M18','M19','M20','M21','M22','M23','M24']

Yöntem 1: np.setdiff1dBu yaklaşımı diğerlerine göre seviyorum çünkü konumu koruyor

test= list(np.setdiff1d(testmod_mpp,crkmod_mpp))
print(test)
['M15', 'M16', 'M22', 'M23', 'M20', 'M14', 'M17', 'M21']

Yöntem2: Yöntem1'deki ile aynı cevabı verse de sırayı bozuyor

test = list(set(testmod_mpp).difference(set(crkmod_mpp)))
print(test)
['POA23', 'POA15', 'POA17', 'POA16', 'POA22', 'POA18', 'POA24', 'POA21']

Method1 np.setdiff1dgereksinimlerimi mükemmel bir şekilde karşılıyor. Bilgi için bu cevap.


0

Gerçekleşme sayısı dikkate alınacaksa, muhtemelen aşağıdakilere benzer bir şey kullanmanız gerekir collections.Counter:

list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 
from collections import Counter
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['f', 'm']

Söz verildiği gibi, bu aynı zamanda farklı oluşum sayılarını "fark" olarak ele alabilir:

list_1=["a", "b", "c", "d", "e", 'a']
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['a', 'f', 'm']

-1

Ser1'den, ser2'de bulunan öğeleri kaldırın.

Giriş

ser1 = pd.Series ([1, 2, 3, 4, 5]) ser2 = pd.Series ([4, 5, 6, 7, 8])

Çözüm

SER1 [~ ser1.isin (ser2)]


Stack Overflow'a hoş geldiniz. Bu sorunun sekiz yanıtı daha var ve bunlardan biri asıl gönderen tarafından kabul edildi. Lütfen yanıtınızın daha önce sunulan yanıtlara göre nasıl geliştiğini açıklayın.
chb
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.