Python, set型で集合演算(和集合、積集合や部分集合の判定など)

Pythonには標準のデータ型として集合を扱うset型が用意されています。 set型は重複しない要素(同じ値ではない要素、ユニークな要素)のコレクションで、和集合、積集合、差集合などの集合演算を行うことができます。

  1. 組み込み型 set(集合)型 — Python 3.6.4 ドキュメント

ここでは、基本操作の、

setオブジェクトの生成: {}, set() 集合内包表記 集合の要素数: len()関数 集合に要素を追加: add() 集合から要素を削除: discard(), remove(), pop(), clear()

および、集合演算である、

和集合(合併、ユニオン): |演算子, union() 積集合(共通部分、交差、インターセクション): &演算子, intersection() 差集合: -演算子, difference() 対称差集合: ^演算子, symmetric_difference() 部分集合か判定: <=演算子, issubset() 上位集合か判定: >=演算子, issuperset() 互いに素か判定: isdisjoint()

について、サンプルコードとともに説明します。 なお、set型は要素を追加したり削除したりできるミュータブルな型。set型と同じく集合演算などのメソッドを持っているがイミュータブル(要素の追加、削除などの改変が出来ない)なfrozenset型もあります。

setオブジェクトの生成: {}, set()

波括弧{}で生成 set型のオブジェクトは波括弧{}で要素を囲むと生成できます。 重複する値がある場合は無視されて、一意な値のみが要素として残る。

s = {1, 2, 2, 3, 1, 4}

print(s)
print(type(s))
# {1, 2, 3, 4}
# <class 'set'>

異なる型を要素として持つこともできます。ただし、リスト型のような更新可能なオブジェクトは登録できません。タプルは問題ありません。 また、set型は順序をもたないので、生成時の順序は記憶されません。

s = {1.23, '百', (0, 1, 2), '百'}

print(s)
# {(0, 1, 2), 1.23, '百'}

# s = {[0, 1, 2]}
# TypeError: unhashable type: 'list'

intやfloatのように異なる型でも値が等価であれば重複していると見なされます。

s = {100, 100.0}

print(s)
# {100}

空の波括弧{}は辞書型dictと見なされるため、空のset型オブジェクト(空集合)を生成するには次に説明するコンストラクタを使います。

s = {}

print(s)
print(type(s))
# {}
# <class 'dict'>

コンストラクタset()で生成 set型のオブジェクトはコンストラクタset()でも生成できます。 引数としてリストやタプルのようなイテラブルオブジェクトを指定すると、重複する要素が除外されて一意な値のみが要素となるsetオブジェクトが生成されます。

l = [1, 2, 2, 3, 1, 4]

print(l)
print(type(l))
# [1, 2, 2, 3, 1, 4]
# <class 'list'>

s_l = set(l)

print(s_l)
print(type(s_l))
# {1, 2, 3, 4}
# <class 'set'>

イミュータブルなfrozenset型はコンストラクタfrozenset()で生成します。

fs_l = frozenset(l)

print(fs_l)
print(type(fs_l))
# frozenset({1, 2, 3, 4})
# <class 'frozenset'>

引数を省略すると、空のset型オブジェクト(空集合)が生成されます。

s = set()

print(s)
print(type(s))
# set()
# <class 'set'>

set()を利用してリストやタプルから重複した要素を取り除くことができるが、元のリストの順序は保持されません。 set型をリストやタプルに変換するにはlist(), tuple()を使います。

l = [2, 2, 3, 1, 3, 4]

l_unique = list(set(l))
print(l_unique)
# [1, 2, 3, 4]

順序を保持したまま重複した要素を削除する場合や、重複した要素のみを抽出したりする場合や、二次元配列(リストのリスト)の重複要素を処理したい場合などについては

集合内包表記

リスト内包表記と同様に集合内包表記もあります。リスト内包表記の角括弧[]を波括弧{}にするだけです。

s = {i**2 for i in range(5)}

print(s)
# {0, 1, 4, 9, 16}

リスト内包表記については

集合の要素数: len()関数

集合の要素数は組み込み関数len()で取得できます。

s = {1, 2, 2, 3, 1, 4}

print(s)
print(len(s))
# {1, 2, 3, 4}
# 4

重複する値の要素を持つリストのそれぞれの要素数をカウントしたい場合などは

集合に要素を追加: add()

集合に要素を追加するには、add()メソッドを使います。

s = {0, 1, 2}

s.add(3)
print(s)
# {0, 1, 2, 3}

集合から要素を削除: discard(), remove(), pop(), clear()

集合から要素を削除するには、discard(), remove(), pop(), clear()メソッドを使います。 discard()メソッドは引数に指定した要素を削除します。集合に存在しない値を指定すると何もしない。

s = {0, 1, 2}

s.discard(1)
print(s)
# {0, 2}

s = {0, 1, 2}

s.discard(10)
print(s)
# {0, 1, 2}

remove()メソッドも引数に指定した要素を削除するが、集合に存在しない値を指定するとエラーKeyErrorになります。

s = {0, 1, 2}

s.remove(1)
print(s)
# {0, 2}

# s = {0, 1, 2}

# s.remove(10)
# KeyError: 10

pop()メソッドは集合から要素を削除し、その値を返します。どの値を削除するかは選択できません。空集合だとエラーKeyErrorになります。

s = {2, 1, 0}

v = s.pop()

print(s)
print(v)
# {1, 2}
# 0

s = {2, 1, 0}

print(s.pop())
# 0

print(s.pop())
# 1

print(s.pop())
# 2

# print(s.pop())
# KeyError: 'pop from an empty set'

clear()メソッドはすべての要素を削除し、空集合とします。

s = {0, 1, 2}

s.clear()
print(s)
# set()

和集合(合併、ユニオン): |演算子, union()

和集合(合併、ユニオン)は|演算子またはunion()メソッドで取得できます。

s1 = {0, 1, 2}
s2 = {1, 2, 3}
s3 = {2, 3, 4}

s_union = s1 | s2
print(s_union)
# {0, 1, 2, 3}

s_union = s1.union(s2)
print(s_union)
# {0, 1, 2, 3}

メソッドには複数の引数を指定することができます。また集合set型だけでなく、set()でset型に変換できるリストやタプルなども引数に指定できます。以降の演算子、メソッドも同じです。

s_union = s1.union(s2, s3)
print(s_union)
# {0, 1, 2, 3, 4}

s_union = s1.union(s2, [5, 6, 5, 7, 5])
print(s_union)
# {0, 1, 2, 3, 5, 6, 7}

積集合(共通部分、交差、インターセクション): &演算子, intersection()

積集合(共通部分、交差、インターセクション)は&演算子またはintersection()メソッドで取得できます。

s_intersection = s1 & s2
print(s_intersection)
# {1, 2}

s_intersection = s1.intersection(s2)
print(s_intersection)
# {1, 2}

s_intersection = s1.intersection(s2, s3)
print(s_intersection)
# {2}

差集合: -演算子, difference()

差集合は-演算子またはdifference()メソッドで取得できます。

s_difference = s1 - s2
print(s_difference)
# {0}

s_difference = s1.difference(s2)
print(s_difference)
# {0}

s_difference = s1.difference(s2, s3)
print(s_difference)
# {0}

対称差集合: ^演算子, symmetric_difference()

対称差集合(どちらか一方にだけ含まれる要素の集合)は^演算子またはsymmetric_difference()で取得できます。 論理演算における排他的論理和(XOR)に相当。

s_symmetric_difference = s1 ^ s2
print(s_symmetric_difference)
# {0, 3}

s_symmetric_difference = s1.symmetric_difference(s2)
print(s_symmetric_difference)
# {0, 3}

部分集合か判定: <=演算子, issubset()

ある集合が別の集合の部分集合かを判定するには、<=演算子またはissubset()メソッドを使います。

s1 = {0, 1}
s2 = {0, 1, 2, 3}

print(s1 <= s2)
# True

print(s1.issubset(s2))
# True

<=演算子もissubset()メソッドも、等価な集合に対してTrueを返します。 真部分集合であるかを判定するには、等価な集合に対してFalseを返す<演算子を使います。

print(s1 <= s1)
# True

print(s1.issubset(s1))
# True

print(s1 < s1)
# False

上位集合か判定: >=演算子, issuperset()

ある集合が別の集合の上位集合かを判定するには、>=演算子またはissuperset()を使います。

s1 = {0, 1}
s2 = {0, 1, 2, 3}

print(s2 >= s1)
# True

print(s2.issuperset(s1))
# True

=演算子もissuperset()メソッドも、等価な集合に対してTrueを返します。 真上位集合であるかを判定するには、等価な集合に対してFalseを返す>演算子を使います。

print(s1 >= s1)
# True

print(s1.issuperset(s1))
# True

print(s1 > s1)
# False

互いに素か判定: isdisjoint()

二つの集合が互いに素かを判定するには、isdisjoint()メソッドを使います。

s1 = {0, 1}
s2 = {1, 2}
s3 = {2, 3}

print(s1.isdisjoint(s2))
# False

print(s1.isdisjoint(s3))
# True
Last Updated: 6/26/2019, 10:48:26 PM