วิธีที่ดีที่สุดในการค้นหาจุดตัดของหลาย ๆ ชุด?


266

ฉันมีรายการชุด:

setlist = [s1,s2,s3...]

ฉันต้องการ s1 ∩ s2 ∩ s3 ...

ฉันสามารถเขียนฟังก์ชั่นที่จะทำได้โดยการทำชุดของจำนวนคู่s1.intersection(s2)ตามลำดับเป็นต้น

มีวิธีแนะนำดีกว่าหรือมีในตัวหรือไม่?

คำตอบ:


454

จาก Python เวอร์ชัน 2.6 คุณสามารถใช้อาร์กิวเมนต์หลายตัวset.intersection()เช่น

u = set.intersection(s1, s2, s3)

หากชุดอยู่ในรายการสิ่งนี้แปลเป็น:

u = set.intersection(*setlist)

การขยายรายการ*a_listอยู่ที่ไหน

ทราบว่าset.intersectionเป็นไม่ได้เป็นวิธีการที่คงที่ แต่นี้ใช้สัญกรณ์การทำงานที่จะใช้จุดตัดของชุดแรกกับส่วนที่เหลือของรายการ ดังนั้นหากรายการอาร์กิวเมนต์ว่างเปล่าสิ่งนี้จะล้มเหลว


65

ตั้งแต่ 2.6 set.intersectionใช้เวลาหลาย iterables โดยพลการ

>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s3 = set([2, 4, 6])
>>> s1 & s2 & s3
set([2])
>>> s1.intersection(s2, s3)
set([2])
>>> sets = [s1, s2, s3]
>>> set.intersection(*sets)
set([2])

24

เห็นได้ชัดว่าset.intersectionเป็นสิ่งที่คุณต้องการที่นี่ แต่ในกรณีที่คุณต้องการความหมายทั่วไปของ "เอาผลรวมของสิ่งเหล่านี้", "นำผลิตภัณฑ์ของสิ่งเหล่านี้ทั้งหมด", "ใช้ xor ของสิ่งเหล่านี้" สิ่งที่คุณกำลังมองหาคือreduceฟังก์ชั่น:

from operator import and_
from functools import reduce
print(reduce(and_, [{1,2,3},{2,3,4},{3,4,5}])) # = {3}

หรือ

print(reduce((lambda x,y: x&y), [{1,2,3},{2,3,4},{3,4,5}])) # = {3}

12

หากคุณไม่มี Python 2.6 หรือสูงกว่าทางเลือกอื่นคือการเขียนลูปให้ชัดเจน:

def set_list_intersection(set_list):
  if not set_list:
    return set()
  result = set_list[0]
  for s in set_list[1:]:
    result &= s
  return result

set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print set_list_intersection(set_list)
# Output: set([1])

คุณยังสามารถใช้reduce:

set_list = [set([1, 2]), set([1, 3]), set([1, 4])]
print reduce(lambda s1, s2: s1 & s2, set_list)
# Output: set([1])

อย่างไรก็ตามโปรแกรมเมอร์ Python หลายคนไม่ชอบมันรวมถึง Guido เอง :

ประมาณ 12 ปีที่แล้วงูหลามแลมบ์ดาลด () กรอง () และแผนที่ () รับความอนุเคราะห์จาก (ฉันเชื่อว่า) แฮกเกอร์ Lisp ที่คิดถึงพวกเขาและส่งแพทช์การทำงาน แต่ถึงแม้จะมีค่า PR ฉันคิดว่าคุณสมบัติเหล่านี้ควรจะตัดจาก Python 3000

ดังนั้นลด () นี่เป็นสิ่งที่ฉันเกลียดที่สุดเสมอเพราะนอกเหนือจากตัวอย่างเล็ก ๆ น้อย ๆ ที่เกี่ยวข้องกับ + หรือ * เกือบทุกครั้งที่ฉันเห็นการเรียกลด () ด้วยการโต้แย้งฟังก์ชั่นที่ไม่สำคัญฉันต้องหยิบปากกาและกระดาษมา แผนภาพสิ่งที่ถูกป้อนเข้าสู่ฟังก์ชันนั้นก่อนที่ฉันจะเข้าใจว่าควรจะลดขนาดไหน () ดังนั้นในใจของฉันการบังคับใช้ลด () ค่อนข้าง จำกัด กับผู้ประกอบการเชื่อมโยงและในกรณีอื่น ๆ ทั้งหมดควรเขียนวงวนสะสมอย่างชัดเจน


8
โปรดทราบว่ากุยโดกล่าวว่าการใช้งานreduceเป็น "จำกัด เฉพาะผู้ให้บริการเชื่อมโยง" ซึ่งใช้ได้ในกรณีนี้ reduceมักจะยากที่จะคิดออก แต่สำหรับ&ไม่เลว
Mike Graham


ลองดูpython.org/doc/essays/list2strสำหรับการปรับแต่งที่มีประโยชน์ซึ่งเกี่ยวกับการลด โดยทั่วไปสามารถใช้ค่อนข้างดีเพื่อสร้างรายการชุดสตริง ฯลฯ คุ้มค่าดูยังเป็นgithub.com/EntilZha/PyFunctional
Andreas

หมายเหตุคุณสามารถปรับให้เหมาะสมโดยการตัดลูปออกเมื่อresultว่างเปล่า
bfontaine

1

ที่นี่ฉันขอเสนอฟังก์ชั่นทั่วไปสำหรับการตัดหลายชุดที่พยายามใช้ประโยชน์จากวิธีที่ดีที่สุดที่มี:

def multiple_set_intersection(*sets):
    """Return multiple set intersection."""
    try:
        return set.intersection(*sets)
    except TypeError: # this is Python < 2.6 or no arguments
        pass

    try: a_set= sets[0]
    except IndexError: # no arguments
        return set() # return empty set

    return reduce(a_set.intersection, sets[1:])

Guido อาจไม่ชอบreduceแต่ฉันชอบมัน :)


คุณควรตรวจสอบความยาวของsetsแทนการพยายามที่จะเข้าถึงและจับsets[0] IndexError
bfontaine

นี่ไม่ใช่เช็คธรรมดา a_setถูกนำมาใช้ในการกลับมาครั้งสุดท้าย
tzot

ทำreturn reduce(sets[0], sets[1:]) if sets else set()ไม่ได้เหรอ
bfontaine

ฮาใช่ขอบคุณ ควรเปลี่ยนรหัสเนื่องจากอาศัยtry/ exceptควรหลีกเลี่ยงหากคุณทำได้ มันเป็นกลิ่นรหัสไม่มีประสิทธิภาพและสามารถซ่อนปัญหาอื่น ๆ ได้
bfontaine

0

Jean-François Fabre set.intesection (* list_of_sets) คำตอบคือ Pyhtonic ที่สุดและเป็นคำตอบที่ถูกต้อง

สำหรับผู้ที่ต้องการใช้การลดต่อไปนี้จะทำงาน:

reduce(set.intersection, list_of_sets)

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