Birden çok kümenin kesişimini bulmanın en iyi yolu?


266

Kümeler listesi var:

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

S1 ∩ s2 ∩ s3 istiyorum ...

Bir çift dizi gerçekleştirerek bunu yapmak için bir işlev yazabilirim s1.intersection(s2) vb .

Önerilen, daha iyi veya yerleşik bir yol var mı?

Yanıtlar:


454

Üzerindeki Python sürüm 2.6'dan set.intersection(), aşağıdakiler gibi birden çok bağımsız değişken kullanabilirsiniz:

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

Kümeler bir listede bulunuyorsa, bu şu anlama gelir:

u = set.intersection(*setlist)

nerede *a_listolduğunu listesini genişletme

Not set.intersectionolduğunu değil statik bir yöntem, ancak bu listenin geri kalanı ile ilk setin kavşak uygulamak için işlevsel ifadesi kullanır. Dolayısıyla, argüman listesi boşsa bu başarısız olur.


65

2.6 itibariyle, set.intersectionkeyfi olarak birçok tekrarlanabilir.

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

Açıkça set.intersectionburada istediğiniz şeydir, ancak "tüm bunların toplamını alın" genellemesine ihtiyacınız varsa, "tüm bunların ürününü alın", "tüm bunların xorunu alın", aradığınız şey reducefonksiyon:

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

veya

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

12

Python 2.6 veya daha üst sürümünüz yoksa, alternatif döngü için açık yazmaktır:

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

Ayrıca şunları kullanabilirsiniz reduce:

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

Bununla birlikte, Guido'nun kendisi de dahil olmak üzere birçok Python programcısı bundan hoşlanmamaktadır :

Yaklaşık 12 yıl önce, Python, onları özleyen ve çalışma yamaları gönderen bir Lisp korsanının izniyle (inanıyorum) lambda, azaltma (), filtre () ve harita () 'yı satın aldı. Ancak, PR değerine rağmen, bu özelliklerin Python 3000'den kesilmesi gerektiğini düşünüyorum.

Şimdi azaltmak (). Bu aslında her zaman en çok nefret ettiğim şeydir, çünkü + veya * içeren birkaç örnekten ayrı olarak, neredeyse önemsiz olmayan bir işlev argümanı ile bir azaltma () çağrısı gördüğümde, kalem ve kağıt almam gerekiyor reduce () 'nin ne yapması gerektiğini anlamadan önce bu işleve gerçekten ne beslendiğini diyagramla. Bu yüzden aklımda, reduce () 'nin uygulanabilirliği hemen hemen ilişkisel operatörlerle sınırlıdır ve diğer tüm durumlarda birikim döngüsünü açıkça yazmak daha iyidir.


8
Guido'nun reducebu durumda geçerli olan "ilişkisel işleçlerle sınırlı" olduğunu belirttiğini unutmayın . reduceanlaması çok zor, ama &o kadar da kötü değil.
Mike Graham


Check out python.org/doc/essays/list2str azaltmak içeren yararlı optimizasyonlar için. Bu genel yapı listeleri, setleri, ipler vb Worth için oldukça güzel kullanılabilecek bir görünüm de github.com/EntilZha/PyFunctional
Andreas

Boşken döngünüzü keserek optimize edebileceğinizi unutmayın result.
bfontaine

1

Burada mevcut en iyi yöntemden yararlanmaya çalışırken çoklu set kavşağı için genel bir işlev sunuyorum:

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 hoşlanmayabilir reduce, ama ben buna bayılıyorum :)


setsErişmeye sets[0]ve yakalamaya çalışmak yerine uzunluğunu kontrol etmelisiniz IndexError.
bfontaine

Bu basit bir kontrol değil; a_setson dönüşte kullanılır.
tzot

Yapamaz mısın return reduce(sets[0], sets[1:]) if sets else set()?
bfontaine

Ha evet, teşekkür ederim. Bir güvenerek çünkü kod değiştirmelisiniz try/ ' exceptEğer yapabilirsen kaçınılmalıdır. Bu bir kod kokusu, verimsiz ve diğer sorunları gizleyebilir.
bfontaine

0

Jean-François Fabre set.intesection (* list_of_sets) yanıtı kesinlikle en Pyhtonic ve haklı olarak kabul edilen cevaptır.

Azaltmak isteyenler için aşağıdakiler de işe yarayacaktır:

reduce(set.intersection, list_of_sets)

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.