集合演算

Pythonのデータ構造の一つである集合(set)は、重複する値を持たない一意の要素を管理するだけでなく、数学的な「集合論」に基づいた高度なデータ処理を高速に行うことができます。
複数のデータ群を比較し、共通するユーザーを抽出したり、重複を排除して統合したり、特定のデータだけを差し引いたりする操作を集合演算と呼びます。リスト(list)でループ処理を使って記述すると複雑になりがちなロジックも、集合演算を使えばわずか1行で、かつC言語並みの超高速スピードで処理することが可能です。本記事では、主要な演算のメカニズムをベン図(Venn Diagram)をイメージしながら解説します。
1. 和集合(Union:すべてを統合する)
和集合とは、2つの集合の**どちらか一方、あるいは両方**に含まれているすべての要素を、重複を排除して丸ごとまとめた集合です。Pythonでは、union() メソッド、または |(パイプ)演算子を使用します。
A = {1, 2, 3}
B = {3, 4, 5}
# メソッド形式、または演算子(|)のどちらでも結果は同じです
union_result = A.union(B)
union_operator = A | B
# 重複していた「3」は自動的に1つにまとめられます
print(union_result) # 出力: {1, 2, 3, 4, 5}
2. 差集合(Difference:片方から差し引く)
差集合とは、**ベースとなる集合から、もう一方の集合に含まれる要素を取り除いた**残りの集合です。Pythonでは、difference() メソッド、または -(マイナス)演算子を使用します。
# 集合Aから集合Bの要素(3)を差し引く(A - B)
diff_result_1 = A.difference(B)
diff_operator_1 = A - B
print(diff_result_1) # 出力: {1, 2}
# 順序を逆にすると、ベースが変わるため結果も変わります(B - A)
diff_result_2 = B - A
print(diff_result_2) # 出力: {4, 5}
3. 積集合(Intersection:共通部分を抽出する)
積集合とは、**2つの集合のどちらにも共通して存在している**要素だけを抽出した集合です。Pythonでは、intersection() メソッド、または &(アンパサンド)演算子を使用します。
# 集合Aと集合Bに共通する要素を求める
inter_result = A.intersection(B)
inter_operator = A & B
print(inter_result) # 出力: {3}
応用:対称差集合(Symmetric Difference:重複しない部分)
実務で時折必要になる応用演算として、積集合の真逆、つまり**「どちらか片方だけにしか存在しない要素(共通部分以外)」**を抽出する対称差集合があります。symmetric_difference() メソッド、または ^(ハット)演算子を使用します。
# 共通の「3」を除外した、それぞれの独自要素だけを抽出
sym_diff_result = A.symmetric_difference(B)
sym_diff_operator = A ^ B
print(sym_diff_result) # 出力: {1, 2, 4, 5}
集合演算の記法・仕様まとめ
実務で使い分けるための記法と、応用的なアプローチの比較です。
| 操作名 | 演算子 | メソッド形式 | メソッド形式だけの最大の強み(重要) |
|---|---|---|---|
| 和集合(すべて) | A | B |
A.union(B) |
演算子(`|`, `&` など)を使う場合、相手(B)も絶対に「set型」でなければエラーになります。
しかし、**メソッド形式(`.union()` など)を使う場合、引数にはリストやタプルなど、あらゆるイテラブルオブジェクトをそのまま放り込めます。**(Pythonが内部で自動的に集合に変換して計算してくれます) |
| 差集合(引き算) | A - B |
A.difference(B) |
|
| 積集合(共通) | A & B |
A.intersection(B) |
|
| 対称差集合(排他) | A ^ B |
A.symmetric_difference(B) |
💡 実務テクニック:メソッド形式の柔軟な例
my_set = {1, 2, 3}
my_list = [3, 4, 5] # 相手が通常のリスト型であっても...
# ⭕ メソッド形式なら、自動で型を考慮して積集合を計算できます
result = my_set.intersection(my_list)
print(result) # 出力: {3}
# ❌ 演算子を使うと「TypeError: unsupported operand type(s)」でクラッシュします
# result = my_set & my_list
まとめ
- ベン図をコードに落とし込む: 集合演算は、データの「重複の有無」や「共通要素の抽出」を数学的な美しさで解決します。データベースの結合(JOIN)や、システム間の差分チェックをプログラム側で高速に行いたいときの最強の武器になります。
- 演算子とメソッドの性質を知る: コードの短さを最優先するなら
A & Bのような演算子が便利ですが、データソースがリストなど多岐にわたる実務の関数内では、型変換の手間を省いて安全に処理できるA.intersection(B)などのメソッド形式が真価を発揮します。 - リスト処理からの脱却: 「リストを `for` 文で回して、もう一つのリストに含まれているか `if` で確認して…」という泥臭いコードを見かけたら、集合型(`set`)にキャストして集合演算に置き換えられないか検討しましょう。可読性もパフォーマンスも劇的に向上します。