note.nkmk.me

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

Date: 2018-03-25 / tags: Python, 算数・数学

Pythonには標準のデータ型として集合を扱うset型が用意されている。

set型は重複しない要素(同じ値ではない要素、ユニークな要素)のコレクションで、和集合、積集合、差集合などの集合演算を行うことができる。

ここでは、基本操作の、

  • 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'>
source: set.py

異なる型を要素として持つこともできる。ただし、リスト型のような更新可能なオブジェクトは登録できない。タプルはOK。

また、set型は順序をもたないので、生成時の順序は記憶されない。

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

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

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

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

s = {100, 100.0}

print(s)
# {100}
source: set.py

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

s = {}

print(s)
print(type(s))
# {}
# <class 'dict'>
source: set.py

コンストラクタ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'>
source: set.py

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

fs_l = frozenset(l)

print(fs_l)
print(type(fs_l))
# frozenset({1, 2, 3, 4})
# <class 'frozenset'>
source: set.py

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

s = set()

print(s)
print(type(s))
# set()
# <class 'set'>
source: set.py

set()を利用してリストやタプルから重複した要素を取り除くことができるが、元のリストの順序は保持されない。

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

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

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

集合内包表記

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

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

print(s)
# {0, 1, 4, 9, 16}
source: set.py

リスト内包表記については以下の記事を参照。

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

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

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

print(s)
print(len(s))
# {1, 2, 3, 4}
# 4
source: set.py

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

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

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

s = {0, 1, 2}

s.add(3)
print(s)
# {0, 1, 2, 3}
source: set.py

集合から要素を削除: 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}
source: set.py

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

s = {0, 1, 2}

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

# s = {0, 1, 2}

# s.remove(10)
# KeyError: 10
source: set.py

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'
source: set.py

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

s = {0, 1, 2}

s.clear()
print(s)
# set()
source: set.py

和集合(合併、ユニオン): |演算子, 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}
source: set.py

メソッドには複数の引数を指定することができる。また集合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}
source: set.py

積集合(共通部分、交差、インターセクション): &演算子, 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}
source: set.py

差集合: -演算子, 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}
source: set.py

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

対称差集合(どちらか一方にだけ含まれる要素の集合)は^演算子またはsymmetric_difference()で取得できる。

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

s_symmetric_difference = s1.symmetric_difference(s2)
print(s_symmetric_difference)
# {0, 3}
source: set.py

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

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

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

print(s1 <= s2)
# True

print(s1.issubset(s2))
# True
source: set.py

<=演算子もissubset()メソッドも、等価な集合に対してTrueを返す。

真部分集合であるかを判定するには、等価な集合に対してFalseを返す<演算子を使う。

print(s1 <= s1)
# True

print(s1.issubset(s1))
# True

print(s1 < s1)
# False
source: set.py

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

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

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

print(s2 >= s1)
# True

print(s2.issuperset(s1))
# True
source: set.py

>=演算子もissuperset()メソッドも、等価な集合に対してTrueを返す。

真上位集合であるかを判定するには、等価な集合に対してFalseを返す>演算子を使う。

print(s1 >= s1)
# True

print(s1.issuperset(s1))
# True

print(s1 > s1)
# False
source: set.py

互いに素か判定: isdisjoint()

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

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

print(s1.isdisjoint(s2))
# False

print(s1.isdisjoint(s3))
# True
source: set.py
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事