Python ค้นหาองค์ประกอบในรายการหนึ่งที่ไม่ได้อยู่ในรายการอื่น [ซ้ำกัน]


139

ฉันต้องการเปรียบเทียบสองรายการเพื่อสร้างรายการองค์ประกอบเฉพาะที่พบในรายการหนึ่ง แต่ไม่พบในรายการอื่น ตัวอย่างเช่น:

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

ฉันต้องการวนซ้ำ list_1 และต่อท้าย main_list องค์ประกอบทั้งหมดจาก list_2 ที่ไม่พบใน list_1

ผลลัพธ์ควรเป็น:

main_list=["f", "m"]

ฉันจะทำกับ python ได้อย่างไร?


2
คุณกำลังมองหาองค์ประกอบlist_2ที่ไม่ปรากฏที่ไหนเลยlist_1หรือองค์ประกอบในlist_2นั้นไม่มีอยู่ในดัชนีเดียวกันในlist_1?
Patrick Haugh

คำตอบ:


100

TL; DR:
โซลูชัน (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`

วิธีแก้ปัญหา (2) คุณต้องการรายการที่จัดเรียง

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)




คำอธิบาย:
(1)คุณสามารถใช้ NumPy ของsetdiff1d( array1, array2, assume_unique= False)

assume_uniqueถามผู้ใช้ว่าอาร์เรย์นั้นไม่ซ้ำกันหรือไม่
หากFalseมีการกำหนดองค์ประกอบที่ไม่ซ้ำกันก่อน
ถ้าTrueฟังก์ชันจะถือว่าองค์ประกอบนั้นไม่ซ้ำกันอยู่แล้วและฟังก์ชันจะข้ามการกำหนดองค์ประกอบที่ไม่ซ้ำกัน

อัตราผลตอบแทนนี้ค่าที่ไม่ซ้ำกันในarray1ที่ไม่ได้array2อยู่ใน assume_uniqueเป็นFalseค่าเริ่มต้น

หากคุณกังวลเกี่ยวกับองค์ประกอบเฉพาะ (ขึ้นอยู่กับการตอบสนองของ Chinny84 ) ให้ใช้ (โดยที่assume_unique=False=> ค่าเริ่มต้น):

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) สำหรับผู้ที่ต้องการเรียงคำตอบฉันได้สร้างฟังก์ชันที่กำหนดเอง:

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

หากต้องการรับคำตอบให้เรียกใช้:

main_list = setdiff_sorted(list_2,list_1)

หมายเหตุด้านข้าง:
(a) โซลูชันที่ 2 (ฟังก์ชันกำหนดเองsetdiff_sorted) ส่งคืนรายการ (เทียบกับอาร์เรย์ในโซลูชัน 1)

(b) หากคุณไม่แน่ใจว่าองค์ประกอบนั้นไม่ซ้ำกันหรือไม่เพียงแค่ใช้การตั้งค่าเริ่มต้นของ NumPy setdiff1dในทั้งสองโซลูชัน A และ B อะไรคือตัวอย่างของภาวะแทรกซ้อน ดูหมายเหตุ (c)

(c) สิ่งต่างๆจะแตกต่างกันไปหากรายการใดรายการหนึ่งจากสองรายการไม่ซ้ำกัน
พูดlist_2ไม่ซ้ำใคร: list2 = ["a", "f", "c", "m", "m"]. เก็บlist1ตามที่เป็นอยู่: list_1 = ["a", "b", "c", "d", "e"]
การตั้งค่าเริ่มต้นของassume_uniqueผลตอบแทน["f", "m"](ในทั้งสองวิธี) อย่างไรก็ตามหากคุณตั้งค่าเป็นค่าเริ่มต้น โปรดทราบว่าคำตอบทั้งสองจะเรียงลำดับassume_unique=True["f", "m", "m"]การแก้ปัญหาทั้งสองให้ ทำไม? เนื่องจากผู้ใช้สันนิษฐานว่าองค์ประกอบไม่ซ้ำกัน) ดังนั้นควรเก็บไว้ดีกว่าassume_unique


หากรายการของคุณได้รับคำสั่งแล้วสิ่งนี้จะส่งคืนรายการที่สั่งซื้อด้วย โซลูชันดั้งเดิมของการแปลงเป็นเซตจากนั้นรับความแตกต่าง (โซลูชันที่แสดงด้านล่าง) จะส่งคืนรายการที่ไม่เรียงลำดับซึ่งอาจทำให้ตรวจสอบผลลัพธ์ด้วยสายตาได้ยากขึ้น
Doubledown

1
สวัสดี @Doubledown! ข้อกังวลของคุณได้รับการแก้ไขแล้วในโพสต์ที่แก้ไข หวังว่านี่จะช่วยได้!
jcoderepo

185

คุณสามารถใช้ชุด:

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

เอาท์พุท:

>>> 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 นี่คือเวอร์ชันที่เป็นระเบียบมากขึ้น:

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

2
นี่เป็นสิ่งที่ดีถ้าเราสนใจเฉพาะuniqueองค์ประกอบ แต่ถ้าเรามีหลายตัวm'sเช่นสิ่งนี้จะไม่หยิบมันขึ้นมา
Chinny84

นั่นคือเรื่องจริง ฉันคิดว่าโปสเตอร์กำลังมองหาองค์ประกอบที่เป็นเอกลักษณ์ ฉันคิดว่ามันขึ้นอยู่กับว่าเขาหมายถึงอะไรโดย "เฉพาะ"
nrlakin

ฉันไม่ได้ลงคะแนนคำตอบของคุณโดยเฉพาะอย่างยิ่งสำหรับคำถามเดิมที่ไม่ชัดเจน
Chinny84

13
คุณสามารถเขียนสิ่งนี้เพื่อlist(set(list_2).difference(list_1))หลีกเลี่ยงการsetเปลี่ยนใจเลื่อมใสอย่างชัดเจน...
Jon Clements

ไม่ต้องห่วง! ขอบคุณ @leaf สำหรับความช่วยเหลือในการจัดรูปแบบ
nrlakin

62

ไม่แน่ใจว่าทำไมคำอธิบายข้างต้นจึงซับซ้อนมากเมื่อคุณมีวิธีดั้งเดิมที่พร้อมใช้งาน:

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

6
การรักษาระเบียบอาจเป็นเหตุผล
Keith

58

ใช้ความเข้าใจในรายการดังนี้:

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

เอาท์พุท:

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

แก้ไข:

ดังที่กล่าวไว้ในความคิดเห็นด้านล่างซึ่งมีรายการขนาดใหญ่ข้างต้นไม่ใช่ทางออกที่ดี เมื่อเป็นกรณีที่เป็นตัวเลือกที่ดีกว่าจะแปลงlist_1ไปเป็นsetครั้งแรก:

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
หมายเหตุ: สำหรับขนาดใหญ่list_1, คุณต้องการ preconvert ไปset/ frozensetเช่นset_1 = frozenset(list_1)นั้นmain_list = [item for item in list_2 if item not in set_1]ช่วยลดเวลาในการตรวจสอบจากO(n)ต่อรายการที่จะ O(1)(ประมาณ)
ShadowRanger

@ettanany โปรดระวังหากคุณลองวิธีแก้ปัญหาตามที่ ettanany โพสต์ไว้ ฉันลองวิธีแก้ปัญหาของ ettanany ตามที่เป็นอยู่และมันช้ามากสำหรับรายการที่ใหญ่กว่า คุณสามารถอัปเดตคำตอบเพื่อรวมคำแนะนำของ Shadowranger ได้หรือไม่?
Doubledown

เป็นไปได้ไหมที่จะได้รับดัชนีแทนที่จะเป็นสตริง
JareBear

@JareBear คุณสามารถใช้enumerate():[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany

@ ettanany's thank มาก !! ฉันจะใช้มันโดยเร็วฉันได้ทำมันแล้ว แต่โค้ดของคุณดูสะอาดขึ้นมาก
JareBear

6

หากคุณต้องการเป็นทางออกหนึ่งซับ (ละเว้นการนำเข้า) ที่ต้องใช้เพียงO(max(n, m))การทำงานสำหรับปัจจัยการผลิตที่มีความยาวnและmไม่O(n * m)ทำงานคุณสามารถทำได้ด้วยโมดูล :itertools

from itertools import filterfalse

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

สิ่งนี้ใช้ประโยชน์จากฟังก์ชั่นการทำงานที่ใช้ฟังก์ชันเรียกกลับในการก่อสร้างทำให้สามารถสร้างการโทรกลับครั้งเดียวและนำกลับมาใช้ใหม่สำหรับทุกองค์ประกอบโดยไม่จำเป็นต้องเก็บไว้ที่ใดที่หนึ่ง (เนื่องจากfilterfalseเก็บไว้ภายใน) ความเข้าใจในรายการและนิพจน์ตัวสร้างสามารถทำได้ แต่มันน่าเกลียด†

ที่ได้ผลลัพธ์เดียวกันในบรรทัดเดียว:

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

ด้วยความเร็ว:

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

แน่นอนว่าหากการเปรียบเทียบมีจุดมุ่งหมายเพื่อกำหนดตำแหน่งดังนั้น:

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

ควรผลิต:

main_list = [2, 3, 4]

(เนื่องจากไม่มีค่าใดที่list_2มีการจับคู่ที่ดัชนีเดียวกันในlist_1) คุณควรใช้คำตอบของ Patrickซึ่งไม่เกี่ยวข้องกับlists หรือ s ชั่วคราวset(แม้ว่าsets จะเป็นค่าประมาณO(1)แต่ก็มีปัจจัย "คงที่" ต่อการตรวจสอบสูงกว่าความเท่าเทียมกันทั่วไป ตรวจสอบ) และเกี่ยวข้องกับO(min(n, m))งานน้อยกว่าคำตอบอื่น ๆ และหากปัญหาของคุณมีความอ่อนไหวต่อตำแหน่งเป็นวิธีแก้ปัญหาที่ถูกต้องเพียงวิธีเดียวเมื่อองค์ประกอบที่ตรงกันปรากฏในออฟเซ็ตที่ไม่ตรงกัน

†: วิธีการทำสิ่งเดียวกันกับการทำความเข้าใจรายการเป็นซับเดียวคือการละเมิดการวนซ้ำที่ซ้อนกันเพื่อสร้างและแคชค่าในลูป "ด้านนอกสุด" เช่น:

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

ซึ่งยังให้ประโยชน์ด้านประสิทธิภาพเล็กน้อยใน Python 3 (เนื่องจากตอนนี้set_1ถูกกำหนดขอบเขตไว้ในรหัสความเข้าใจแทนที่จะมองจากขอบเขตที่ซ้อนกันสำหรับการตรวจสอบแต่ละครั้งใน Python 2 นั้นไม่สำคัญเพราะ Python 2 ไม่ใช้การปิดสำหรับ รายการความเข้าใจพวกเขาดำเนินการในขอบเขตเดียวกับที่ใช้ใน)


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)

เอาท์พุท:

['f', 'm']

เช่นเดียวกับโซลูชันที่อิงตามความเข้าใจรายการที่เทียบเท่าสิ่งนี้จะช้าหากlist_1มีขนาดใหญ่และlist_2มีขนาดที่ไม่สำคัญเนื่องจากเกี่ยวข้องกับการlen(list_2) O(n)สแกนlist_1ทำให้O(n * m)(โดยที่nและmมีความยาวlist_2และlist_1ตามลำดับ) หากคุณแปลงlist_1เป็น a set/ frozensetup front การตรวจสอบมีจะสามารถทำได้O(1)ทำให้การทำงานทั้งหมดO(n)ตามความยาวlist_2(ในทางเทคนิคO(max(n, m))เนื่องจากคุณO(m)ทำงานเพื่อสร้างset)
ShadowRanger

1

ฉันจะzipรวมรายการเพื่อเปรียบเทียบองค์ประกอบตามองค์ประกอบ

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

หาก OP ต้องการที่จะเปรียบเทียบองค์ประกอบโดยองค์ประกอบ (มันไม่ชัดเจนตัวอย่างเช่นอาจจะไปทางใดทางหนึ่ง) นี้เป็นมากมีประสิทธิภาพมากขึ้นกว่าคำตอบอื่น ๆ เนื่องจากเป็นบัตรเดียวราคาถูกกว่าทั้งlists กับซิงเกิลใหม่listถูกสร้างขึ้นไม่มีชั่วคราวเพิ่มเติม ไม่มีการตรวจสอบการกักกันราคาแพง ฯลฯ
ShadowRanger

1
@ShadowRanger สิ่งนี้จะใช้ได้เฉพาะกับความแตกต่างที่ชาญฉลาดขององค์ประกอบซึ่งเป็นประเด็นสำคัญ
ฟอร์ดนายอำเภอ

@fordprefect: ใช่. คำตอบของฉันครอบคลุมความแตกต่างที่ไม่ขึ้นกับตำแหน่ง
ShadowRanger

1

ฉันใช้สองวิธีและฉันพบว่าวิธีหนึ่งมีประโยชน์มากกว่าวิธีอื่น นี่คือคำตอบของฉัน:

ข้อมูลอินพุตของฉัน:

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

วิธีที่ 1: np.setdiff1dฉันชอบแนวทางนี้มากกว่าวิธีอื่นเพราะรักษาตำแหน่งไว้

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

วิธีที่ 2: แม้ว่าจะให้คำตอบเช่นเดียวกับวิธีที่ 1 แต่รบกวนคำสั่ง

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

Method1 np.setdiff1dตรงตามความต้องการของฉันอย่างสมบูรณ์แบบ คำตอบนี้สำหรับข้อมูล


0

หากควรคำนึงถึงจำนวนครั้งที่เกิดขึ้นคุณอาจต้องใช้สิ่งต่างๆเช่น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']

ตามที่สัญญาไว้สิ่งนี้ยังสามารถรองรับจำนวนเหตุการณ์ที่แตกต่างกันเป็น "ความแตกต่าง":

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 ลบรายการที่มีอยู่ใน ser2

อินพุต

ser1 = pd. ซีรีส์ ([1, 2, 3, 4, 5]) ser2 = pd.Series ([4, 5, 6, 7, 8])

สารละลาย

ser1 [~ ser1.isin (ser2)]


ยินดีต้อนรับสู่ Stack Overflow คำถามนี้มีอีกแปดคำตอบซึ่งหนึ่งในนั้นได้รับการตอบรับจากผู้โพสต์ต้นฉบับ โปรดอธิบายว่าคำตอบของคุณดีขึ้นอย่างไรจากสิ่งที่นำเสนอไปแล้ว
chb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.