Python menemukan elemen dalam satu daftar yang tidak ada di [duplikat] lainnya

137

Saya perlu membandingkan dua daftar untuk membuat daftar baru dari elemen spesifik yang ditemukan di satu daftar tetapi tidak di daftar lainnya. Sebagai contoh:

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

Saya ingin mengulang list_1 dan menambahkan ke main_list semua elemen dari list_2 yang tidak ditemukan di list_1.

Hasilnya seharusnya:

main_list=["f", "m"]

Bagaimana saya bisa melakukannya dengan python?

CosimoCD
sumber
2
Apakah Anda mencari elemen list_2yang tidak muncul di tempat list_1atau elemen list_2yang tidak ada di indeks yang sama list_1?
Patrick Haugh

Jawaban:

98

TL; DR:
SOLUSI (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`

SOLUSI (2) Anda menginginkan daftar yang diurutkan

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)




PENJELASAN:
(1) Anda dapat menggunakan NumPy ini setdiff1d( array1, array2, assume_unique= False).

assume_uniquememinta pengguna JIKA array SUDAH UNIK.
Jika False, maka elemen uniknya ditentukan terlebih dahulu.
Jika True, fungsi akan menganggap bahwa elemen tersebut sudah unik DAN fungsi akan melewatkan penentuan elemen unik.

Ini menghasilkan nilai unik array1yang tidak ada di dalamnyaarray2 . assume_uniqueadalah Falsesecara default.

Jika Anda khawatir dengan elemen unik (berdasarkan respons Chinny84 ), maka cukup gunakan (di mana assume_unique=False=> nilai default):

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) Bagi mereka yang ingin jawaban diurutkan, saya telah membuat fungsi khusus:

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

Untuk mendapatkan jawabannya, jalankan:

main_list = setdiff_sorted(list_2,list_1)

CATATAN SAMPING:
(a) Solusi 2 (fungsi kustom setdiff_sorted) mengembalikan daftar (dibandingkan dengan array dalam solusi 1).

(b) Jika Anda tidak yakin apakah elemennya unik, cukup gunakan pengaturan default NumPy setdiff1ddi kedua solusi A dan B. Apa yang bisa menjadi contoh komplikasi? Lihat catatan (c).

(c) Hal-hal akan berbeda jika salah satu dari dua daftar tersebut tidak unik.
Katakanlah list_2tidak unik: list2 = ["a", "f", "c", "m", "m"]. Pertahankan list1apa adanya: list_1 = ["a", "b", "c", "d", "e"]
Menyetel nilai default assume_uniquehasil ["f", "m"](di kedua solusi). NAMUN, jika Anda menetapkan assume_unique=True, kedua solusi memberikan ["f", "m", "m"]. Mengapa? Ini karena pengguna MENGANGGAP bahwa elemennya unik). Karenanya, LEBIH BAIK UNTUK DIPERTAHANKANassume_uniqueke nilai defaultnya. Perhatikan bahwa kedua jawaban diurutkan.

jcoderepo.dll
sumber
Jika daftar Anda sudah dipesan, ini juga akan mengembalikan daftar berurutan. Solusi asli untuk mengonversi ke kumpulan dan kemudian mendapatkan perbedaannya (solusi yang ditunjukkan di bawah) mengembalikan daftar tak berurutan yang mungkin mempersulit pemeriksaan hasil Anda secara visual.
Doubledown
1
Hai, @Doubledown! Kekhawatiran Anda telah diatasi dalam postingan yang diedit. Semoga ini membantu!
jcoderepo
183

Anda dapat menggunakan set:

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

Keluaran:

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

Sesuai komentar @JonClements, berikut adalah versi yang lebih rapi:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']
nrlakin
sumber
2
Ini bagus jika kita hanya peduli tentang uniqueelemen tetapi bagaimana jika kita memiliki banyak m'smisalnya ini tidak akan mengambilnya.
Chinny84
Itu benar. Saya berasumsi poster itu mencari elemen unik. Saya kira itu tergantung pada apa yang dia maksud dengan "spesifik".
nrlakin
Memang ps saya tidak meremehkan jawaban Anda, terutama untuk pertanyaan asli yang tidak jelas.
Chinny84
13
Anda dapat menulis ini untuk list(set(list_2).difference(list_1))menghindari setkonversi eksplisit ...
Jon Clements
Jangan khawatir! Terima kasih @leaf untuk bantuan pemformatannya.
nrlakin
61

Tidak yakin mengapa penjelasan di atas begitu rumit ketika Anda memiliki metode asli yang tersedia:

main_list = list(set(list_2)-set(list_1))
A.Kot
sumber
6
Menjaga ketertiban mungkin menjadi alasannya
Keith
57

Gunakan pemahaman daftar seperti ini:

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

Keluaran:

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

Edit:

Seperti yang disebutkan dalam komentar di bawah, dengan daftar besar, di atas bukanlah solusi ideal. Jika demikian, opsi yang lebih baik adalah mengonversi list_1menjadi yang setpertama:

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]
ettanany
sumber
3
Catatan: Untuk yang lebih besar list_1, Anda ingin mengkonversikan sebelumnya ke set/ frozenset, misalnya set_1 = frozenset(list_1), kemudian main_list = [item for item in list_2 if item not in set_1], mengurangi waktu pemeriksaan dari O(n)per item menjadi (secara kasar) O(1).
ShadowRanger
@ettanany Harap berhati-hati jika Anda mencoba solusi seperti yang diposting ettanany. Saya mencoba solusi ettanany apa adanya dan memang sangat lambat untuk daftar yang lebih besar. Bisakah Anda memperbarui jawaban untuk memasukkan saran shadowranger?
Doubledown
Apakah mungkin mendapatkan indeks, alih-alih string?
JareBear
@JareBear Anda dapat menggunakan enumerate()untuk itu:[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany
@ ettanany terima kasih banyak !! Saya akan menerapkannya secepatnya, saya telah melakukannya. Tetapi kode Anda terlihat jauh lebih bersih.
JareBear
5

Jika Anda ingin solusi satu-kapal (mengabaikan impor) yang hanya membutuhkan O(max(n, m))pekerjaan untuk input panjang ndan m, tidak O(n * m)bekerja, Anda dapat melakukannya dengan para itertoolsmodul :

from itertools import filterfalse

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

Ini memanfaatkan fungsi fungsional yang mengambil fungsi callback pada konstruksi, yang memungkinkannya membuat callback sekali dan menggunakannya kembali untuk setiap elemen tanpa perlu menyimpannya di suatu tempat (karena filterfalsemenyimpannya secara internal); pemahaman daftar dan ekspresi generator bisa melakukan ini, tapi itu jelek. †

Itu mendapatkan hasil yang sama dalam satu baris seperti:

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

dengan kecepatan:

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

Tentu saja, jika perbandingan dimaksudkan untuk menjadi posisi, maka:

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

harus menghasilkan:

main_list = [2, 3, 4]

(karena nilai dalam list_2memiliki kecocokan pada indeks yang sama di list_1), Anda harus menggunakan jawaban Patrick , yang tidak melibatkan lists atau s sementara set(bahkan dengan sets secara kasar O(1), mereka memiliki faktor "konstan" yang lebih tinggi per cek daripada pemeriksaan persamaan sederhana ) dan melibatkan O(min(n, m))pekerjaan, kurang dari jawaban lain, dan jika masalah Anda sensitif terhadap posisi, adalah satu-satunya solusi yang tepat saat elemen yang cocok muncul di offset yang tidak cocok.

†: Cara melakukan hal yang sama dengan pemahaman daftar sebagai satu-liner akan menyalahgunakan perulangan bersarang untuk membuat dan nilai cache di loop "terluar", misalnya:

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

yang juga memberikan keuntungan kinerja kecil pada Python 3 (karena sekarang set_1dicakup secara lokal dalam kode pemahaman, daripada mencari dari lingkup bersarang untuk setiap pemeriksaan; pada Python 2 itu tidak masalah, karena Python 2 tidak menggunakan penutup untuk daftar pemahaman; mereka beroperasi dalam lingkup yang sama dengan yang mereka gunakan).

ShadowRanger
sumber
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)

keluaran:

['f', 'm']
Inconnu
sumber
Seperti solusi berbasis pemahaman daftar yang setara , ini akan menjadi lambat jika list_1besar, dan list_2berukuran tidak sepele, karena melibatkan len(list_2) O(n)pemindaian list_1, membuatnya O(n * m)(di mana ndan madalah panjang list_2dan list_1masing - masing). Jika Anda mengonversi list_1ke a set/ frozensetdi depan, pemeriksaan berisi dapat dilakukan di O(1), membuat total pekerjaan O(n)pada panjang list_2(secara teknis O(max(n, m)), karena Anda O(m)bekerja untuk membuatnya set).
ShadowRanger
1

Saya akan zipmembuat daftar bersama untuk membandingkannya elemen demi elemen.

main_list = [b for a, b in zip(list1, list2) if a!= b]
Patrick Haugh
sumber
Jika OP ingin membandingkan elemen demi elemen (tidak jelas, contohnya bisa berjalan baik), ini jauh lebih efisien daripada jawaban lainnya, karena ini adalah satu pass murah untuk keduanya listdengan satu yang baru listsedang dibangun, tidak ada temporer tambahan , tidak ada pemeriksaan penahanan yang mahal, dll.
ShadowRanger
1
@ShadowRanger ini hanya akan berfungsi untuk perbedaan elemen-bijaksana yang merupakan poin kunci
ford prefek
@fordprefect: Yup. Jawaban saya sendiri mencakup perbedaan posisi-independen.
ShadowRanger
1

Saya menggunakan dua metode dan saya menemukan satu metode berguna di atas yang lain. Inilah jawaban saya:

Data masukan saya:

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

Metode 1: np.setdiff1dSaya menyukai pendekatan ini daripada yang lain karena ini mempertahankan posisinya

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

Metode2: Meskipun memberikan jawaban yang sama seperti di Metode1 tetapi mengganggu urutan

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

Method1 np.setdiff1dmemenuhi persyaratan saya dengan sempurna. Jawaban ini untuk informasi.

Msquare
sumber
0

Jika jumlah kejadian harus diperhitungkan, Anda mungkin perlu menggunakan sesuatu seperti 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']

Seperti yang dijanjikan, ini juga dapat menangani jumlah kemunculan yang berbeda sebagai "perbedaan":

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']
MSeifert
sumber
-1

Dari ser1 hapus item yang ada di ser2.

Memasukkan

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

Larutan

ser1 [~ ser1.isin (ser2)]

adnan
sumber
Selamat datang di Stack Overflow. Pertanyaan ini memiliki delapan jawaban lainnya, salah satunya telah diterima oleh pengirim asli. Harap jelaskan bagaimana jawaban Anda meningkat dari apa yang sudah disajikan.
chb