Cara terbaik untuk menemukan persimpangan beberapa set?

267

Saya punya daftar set:

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

Saya ingin s1 ∩ s2 ∩ s3 ...

Saya dapat menulis fungsi untuk melakukannya dengan melakukan serangkaian berpasangan s1.intersection(s2), dll.

Apakah ada cara yang disarankan, lebih baik, atau terintegrasi?

pengguna116293
sumber

Jawaban:

454

Dari Python versi 2.6 pada Anda dapat menggunakan beberapa argumen set.intersection(), seperti

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

Jika set ada dalam daftar, ini berarti:

u = set.intersection(*setlist)

di mana *a_listadalah daftar ekspansi

Perhatikan bahwa set.intersectionini bukan metode statis, tetapi ini menggunakan notasi fungsional untuk menerapkan persimpangan set pertama dengan sisa daftar. Jadi, jika daftar argumen kosong ini akan gagal.

sth
sumber
65

Pada 2.6, set.intersectionmengambil banyak iterables sewenang-wenang.

>>> 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])
Mike Graham
sumber
24

Jelas set.intersectionapa yang Anda inginkan di sini, tetapi jika Anda membutuhkan generalisasi "ambil jumlah semua ini", "ambil produk semua ini", "ambil semua ini", apa yang Anda cari adalah reducefungsi:

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

atau

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

Jika Anda tidak memiliki Python 2.6 atau lebih tinggi, alternatifnya adalah menulis eksplisit untuk loop:

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

Anda juga dapat menggunakan reduce:

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

Namun, banyak programmer Python tidak menyukainya, termasuk Guido sendiri :

Sekitar 12 tahun yang lalu, Python mendapatkan lambda, mengurangi (), filter () dan peta (), milik (saya percaya) seorang hacker Lisp yang merindukan mereka dan mengirimkan patch yang berfungsi. Namun, terlepas dari nilai PR, saya pikir fitur ini harus dipotong dari Python 3000.

Jadi sekarang kurangi (). Ini sebenarnya yang paling saya benci, karena, terlepas dari beberapa contoh yang melibatkan + atau *, hampir setiap kali saya melihat panggilan pengurangan () dengan argumen fungsi non-sepele, saya perlu mengambil pena dan kertas untuk diagram apa yang sebenarnya dimasukkan ke dalam fungsi itu sebelum saya mengerti apa yang seharusnya dilakukan pengurangan (). Jadi dalam pikiran saya, penerapan pengurangan () cukup banyak terbatas pada operator asosiatif, dan dalam semua kasus lainnya lebih baik untuk menuliskan lingkaran akumulasi secara eksplisit.

Ayman Hourieh
sumber
8
Perhatikan bahwa Guido mengatakan penggunaan reduce"terbatas pada operator asosiatif", yang berlaku dalam kasus ini. reducesangat sering sulit untuk diketahui, tetapi &tidak terlalu buruk.
Mike Graham
Lihat python.org/doc/essays/list2str untuk optimisasi bermanfaat yang melibatkan pengurangan. Secara umum dapat digunakan dengan cukup baik untuk membangun daftar, set, string dll. Yang patut dilihat juga adalah github.com/EntilZha/PyFunctional
Andreas
Perhatikan bahwa Anda dapat mengoptimalkan dengan memutus perulangan saat resultkosong.
bfontaine
1

Di sini saya menawarkan fungsi generik untuk beberapa set persimpangan yang mencoba memanfaatkan metode terbaik yang tersedia:

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 mungkin tidak suka reduce, tapi aku agak menyukainya :)

tzot
sumber
Anda harus memeriksa panjang setsalih-alih mencoba mengakses sets[0]dan menangkap IndexError.
bfontaine
Ini bukan cek biasa; a_setdigunakan pada pengembalian akhir.
tzot
Tidak bisa Anda lakukan return reduce(sets[0], sets[1:]) if sets else set()?
bfontaine
Ha iya, terima kasih. Kode harus berubah karena bergantung pada a try/ exceptharus dihindari jika Anda bisa. Ini bau kode, tidak efisien, dan dapat menyembunyikan masalah lain.
bfontaine
0

Jawaban set.intesection Jean-François Fabre (* list_of_sets) jelas merupakan yang paling Pyhtonic dan merupakan jawaban yang diterima.

Bagi mereka yang ingin menggunakan mengurangi, berikut ini juga akan berfungsi:

reduce(set.intersection, list_of_sets)

Minas
sumber