Python の集合演算(和集合・積集合・差集合・対称差)

集合演算は、複数の集合を組み合わせて新しい集合を得る操作だ。Python では演算子とメソッドの両方で実行できる。

和集合(Union)

和集合は、どちらかの集合に含まれるすべての要素を集めたものだ。| 演算子または union() メソッドで取得する。

a = {1, 2, 3}
b = {3, 4, 5}

# 演算子
print(a | b)        # {1, 2, 3, 4, 5}

# メソッド
print(a.union(b))   # {1, 2, 3, 4, 5}

両方に含まれる 3 は、結果には 1 回だけ現れる。これが集合の重複排除の性質だ。

積集合(Intersection)

積集合は、両方の集合に共通して含まれる要素だけを集めたものだ。& 演算子または intersection() メソッドを使う。

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# 演算子
print(a & b)              # {3, 4}

# メソッド
print(a.intersection(b))  # {3, 4}

共通要素がない場合は空集合が返る。

a = {1, 2}
b = {3, 4}
print(a & b)  # set()

差集合(Difference)

差集合は、一方の集合から他方の要素を取り除いたものだ。- 演算子または difference() メソッドで得られる。

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# a から b の要素を除く
print(a - b)              # {1, 2}

# b から a の要素を除く
print(b - a)              # {5, 6}

# メソッド
print(a.difference(b))    # {1, 2}

差集合は順序が重要だ。a - bb - a は異なる結果になる。

a - b

a に含まれるが b には含まれない要素

b - a

b に含まれるが a には含まれない要素

対称差(Symmetric Difference)

対称差は、どちらか一方にのみ含まれる要素を集めたものだ。両方に含まれる要素は除外される。^ 演算子または symmetric_difference() メソッドを使う。

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

# 演算子
print(a ^ b)                        # {1, 2, 5, 6}

# メソッド
print(a.symmetric_difference(b))    # {1, 2, 5, 6}

対称差は和集合から積集合を引いたものと同じだ。

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print(a ^ b)              # {1, 2, 5, 6}
print((a | b) - (a & b))  # {1, 2, 5, 6}

演算子とメソッドの違い

演算子は集合同士でしか使えないが、メソッドは任意のイテラブルを引数に取れる。

a = {1, 2, 3}

# メソッドはリストを直接渡せる
print(a.union([3, 4, 5]))         # {1, 2, 3, 4, 5}
print(a.intersection([2, 3, 4]))  # {2, 3}

# 演算子はエラー
# print(a | [3, 4, 5])  # TypeError

複数のイテラブルを一度に処理できるのもメソッドの利点だ。

a = {1, 2}
print(a.union([3, 4], {5, 6}, (7, 8)))  # {1, 2, 3, 4, 5, 6, 7, 8}

破壊的な集合演算

元の集合を直接変更する破壊的メソッドも用意されている。

非破壊的破壊的
union()update()
intersection()intersection_update()
difference()difference_update()
symmetric_difference()symmetric_difference_update()

破壊的メソッドは戻り値が None で、元の集合を直接変更する。

a = {1, 2, 3}
a.update({4, 5})
print(a)  # {1, 2, 3, 4, 5}

b = {1, 2, 3, 4}
b.intersection_update({2, 3, 5})
print(b)  # {2, 3}

|=&=-=^= といった複合代入演算子も使える。

a = {1, 2, 3}
a |= {4, 5}
print(a)  # {1, 2, 3, 4, 5}

b = {1, 2, 3, 4}
b &= {2, 3, 5}
print(b)  # {2, 3}

複数集合の演算

3 つ以上の集合に対しても演算できる。

a = {1, 2, 3}
b = {2, 3, 4}
c = {3, 4, 5}

# 3 つの和集合
print(a | b | c)  # {1, 2, 3, 4, 5}

# 3 つの積集合
print(a & b & c)  # {3}

メソッドを使えば可変長引数で渡せる。

sets = [{1, 2}, {2, 3}, {2, 4}]
result = set.intersection(*sets)
print(result)  # {2}

set.intersection() のようにクラスメソッドとして呼び出すと、すべての集合の共通要素を求められる。