Python3の麗かな集合

まえがき

Python3には集合型(set型、frozenset型)というものがる。これは順序なしのコレクションである。集合とは数学における集合のこと。和集合、積集合、差集合、対象差などの数学的演算をサポートするものである。 主に他のコレクションであるリスト、タプル、辞書に大量なデータの中から重複を除去したり、存在の判定(高速らしい)などができる。

集合型には2つの実装があり、 違いはset型はミュータブルでfrozenset型はイミュータブルである。イミュータブルであるということはハッシュ可能ということで、辞書のキーや集合の要素として使える。

謝辞

「Pythonチュートリアル」の著者、Pythonの産みの親、Guido van Rossum氏。
翻訳者であり日本野人の会CEOである、鴨澤眞夫氏。
「退屈なことはPythonにやらせよう」の著者、Al Sweigart氏、訳者の相川愛三氏。
出版に携わった全ての人に感謝したい。

変更履歴

2020/03/20:新規作成

この記事の環境

  • モジュール:標準モジュール
  • OS:MacOS
  • DE:Spacemacs
  • 環境構築:virtualenv & pyenv

集合の作り方

集合の作り方は辞書と同じでブレイシズという括弧を使う。集合は値だけなので括弧は同じでも問題ないが、空の集合を宣言したい場合はset()関数を使う必要がある。ブレイシズの括弧だけでは辞書になってしまうので注意。この辺り、別の括弧を使えるようにしたらいいのにと思う。

集合は宣言時に重複する値を入れらるが、出力時には自動的に除去される。また、順序がないのでバラバラに出力される。また、すでに述べたとおりfrozenset型は集合の要素にできるが、言い方を帰れば集合の集合、つまり集合の内側の集合はfrozenset型でなければならない。

集合の作り方、宣言:コード

# 宣言
set_empty = set()  # 空の集合
set_a = {1, 2, 3, 4, 5, 6, 7, 8}  # 順序は関係ない
set_frozen = frozenset(32, 33, 34, 35)  # イミュータブルやで
set_b = {"A", "B", set_frozen}

# 重複は取り除かれる
set_ex = {"A", "A", "B", "B", "C", "D", "E", "E", "F", }

集合の作り方、宣言:実行結果

数値をバラバラに入れても順番通りに出力される。文字は順不同。

集合の操作(メソッドなし)

出来る事:inやnot inなどの共通のものと、集合専用のメソッド、演算子が用意されている。

出来ない事:順序がないので、インデックスも指定できないしスライシングもできない。とにかくシーケンス型だと思わない事。

集合の非専用の操作&メソッド:コード

# 共通する操作(一部、専用を含む)、メソッド
set_x = {"林檎", "桃", "柿", "栗", "梨", "葡萄"}

"栗" in set_x
"季" not in set_x

# 集合の要素の数、中身
len(set_x)
# コピーを返す、公式ドキュメントは「浅いコピー」。
set_c = set_x.copy()
# 引数を集合に追加する。**これは集合専用っぽい。**
set_x.add("Banana")
# 引数を集合から取り出す。引数が含まれていないと「KeyError」送出。
set_x.remove("桃")
# 集合の先頭から取り出して返す、集合が空の場合にのみ「KeyError」送出。
set_x.pop()
# 引数が集合に含まれている場合にのみ取り除く(便利)。**これも集合専用っぽい。**
set_x.discard("Mango")
# 全ての要素を集合から取り除き、空の集合にする。
set_x.clear()
# 集合を削除する
del set_x

集合の非専用の操作&メソッド:実行結果

集合の操作、演算子:コード

# 集合を操作する演算子
set_x = {"林檎", "桃", "柿", "栗", "梨", "葡萄"}
set_y = {"桃", "柿", "栗", "梨"}
set_z = {"薩摩芋", "無花果", "落花生", "猿梨", "木通"}
set_zz = {"パイナップル", "桃", "柿", "栗", "梨", "パプリカ"}

# ***存在判定***
# 集合が引数と共通の要素を持たない場合にTrue
set_zz.isdisjoint(set_y)
# 左辺が全て右辺に含まれるか判定
set_y <= set_x
# 左辺が右辺の部分集合かを判定(真部分集合)
set_y < set_x
# 左辺が右辺の要素を全て内包するか判定(右辺が全て左辺に含まれるか判定)
set_y >= set_x
# 左辺が右辺の上位集合かを判定(真上位集合)
set_y > set_x


# ***新しい集合を作る***
# 右辺は複数指定できる
# 左辺と右辺を全て含む集合を返す:和集合
set_new1 = set_y | set_z
# 左辺と右辺に共通する要素を持つ集合を返す:積集合
set_new2 = set_x & set_new1
# 左辺含まれ、右辺に含まれない要素を持つ集合を返す:差集合
set_new3 = set_x - set_y
# 左辺と右辺のいずれか一方にのみ含まれる要素を持つ集合を返す:対称差集合
set_new4 = set_x ^ set_zz

# ***集合を更新する***
# 右辺は複数指定できる
# 左辺に右辺の要素を全て追加し左辺を更新する:和集合
set_zz |= set_y
# 左辺と右辺に共通する要素のみ残して左辺を更新する:積集合
set_zz &= set_y
# 左辺に含まれる右辺と同じ要素を取り除いて左辺を更新する:差集合
set_zz -= set_y
# 左辺または右辺の一方にのみ存在する要素のみで左辺を更新する:対称差集合
set_zz ^= set_y

集合の操作、演算子:実行結果

集合の操作、メソッド:コード

# 集合のメソッド
set_x = {"林檎", "桃", "柿", "栗", "梨", "葡萄"}
set_y = {"桃", "柿", "栗", "梨"}
set_z = {"薩摩芋", "無花果", "落花生", "猿梨", "木通"}
set_zz = {"パイナップル", "桃", "柿", "栗", "梨", "パプリカ"}


# ***存在判定メソッド***
# 集合が引数に全て含まれるかを判定
set_x.issubset(set_y)
# 集合に引数の全ての要素が含まれるかを判定
set_x.issuperset(set_y)

# ***新しい集合を作る***
# 新しい集合を返す
# 和集合:.union(*others)
set_uni = set_x.union(set_y)
# 積集合:.intersection(*others)
set_inter = set_x.intersection(set_y)
# 差集合:.difference(*others)
set_diff = set_x.difference(set_z)
# 対称差集合:.symmetric_difference(others)
set_symm = set_y.symmetric_difference(set_zz)

# ***集合を更新する***
# 対象の集合を変更する
# 和集合:.update(*others)
set_x.update(set_y)
# 積集合:.intersection_update(*others)
set_x.intersection_update(set_y)
# 差集合:.difference_update(*others)
set_x.difference_update(set_z)
# 対称差集合:.symmetric_difference_update(others)
set_y.symmetric_difference_update(set_zz)

集合の操作、メソッド:実行結果

集合の内包表記:コード

"""\
集合にも内包表記はある。
"""

set_in = {x for x in "abcdef" if x not in "def"}

>>>set_in
{"a", "b", "c"}

むすび

データが何万以上あるような場合は集合を使うと便利なのかなと思った。重複の除去、存在の確認。どれくらい早く探せるかリストや辞書と比べてみるといいのかもしれないので、別の記事でやってみます。

コメントする