複数のセットの共通部分を見つける最良の方法は?


267

セットのリストがあります:

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任意の数の反復可能オブジェクトを取ります。

>>> 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以降がない場合は、明示的なforループを作成する方法もあります。

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

ただし、Guido自身を含め、多くのPythonプログラマはこれ嫌っています。

約12年前、Pythonはラムダ、reduce()、filter()、map()を取得しました。これは、Lispハッカーがそれを逃して作業パッチを提出したおかげです。しかし、PRの値にもかかわらず、これらの機能はPython 3000から削除する必要があると思います。

だから今はreduce()です。これは実際に私が常に最も嫌っていたものです。+または*を含むいくつかの例を除いて、重要な関数引数を含むreduce()呼び出しを見るたびに、ペンと紙をつかむ必要があるためです。 reduce()が何をすべきかを理解する前に、その関数に実際に供給されているものを図に示します。したがって、私の考えでは、reduce()の適用範囲は結合演算子にかなり限定されており、他のすべての場合では、累算ループを明示的に記述する方が適切です。


8
Guidoは、使用reduceは「連想演算子に限定されている」と言っていることに注意してください。これは、この場合に適用されます。reduce多くの場合、理解するのは難しいです&が、それほど悪くはありません。
マイクグラハム


python.org/doc/essays/list2strでreduceに関連する便利な最適化を確認してください。一般に、リスト、セット、文字列などを作成するために非常にうまく使用できます。一見の価値があるのもgithub.com/EntilZha/PyFunctional
Andreas

resultが空のときにループを中断することで最適化できることに注意してください。
bfontaine 2017

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

グイドは嫌いかもしれませんreduceが、私はそれが少し好きです:)


setsにアクセスsets[0]してキャッチする代わりに、の長さを確認する必要がありIndexErrorます。
bfontaine 2017

これは単純なチェックではありません。a_set最終戻り時に使用されます。
tzot

できませんかreturn reduce(sets[0], sets[1:]) if sets else set()
bfontaine 2017

はいはい、ありがとうございます。try/に依存exceptすることはできる限り避けなければならないため、コードを変更する必要があります。これはコードのにおいであり、非効率的で、他の問題を隠すことができます。
bfontaine 2017

0

Jean-FrançoisFabre set.intesection(* list_of_sets)の回答は、明らかに最もピュトニックであり、正しい回答です。

reduceを使用したい場合は、以下も機能します。

reduce(set.intersection, list_of_sets)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.