閉じる

Pythonのsetから和集合や積集合を作る方法(unionとintersection)

Pythonの集合型(set)は、重複のないデータを扱い、和集合や積集合などの集合演算を高速に行える便利な型です。

本記事では、初心者の方にもわかりやすいように、setの基本からunion(和集合)とintersection(積集合)の使い方、演算子|&、そして破壊的操作との違いまで順を追って解説します。

実用例や注意点も含めて、すぐに現場で役立つ知識を身に付けましょう。

Pythonのsetと集合演算の基本

setの作り方と空集合の定義

setは重複を持たないコレクションで、波かっこ{}でリテラルを作れます。

空集合はset()で作る点が重要です。

{}は辞書(dict)になるため注意します。

Python
# 基本的なsetの作成
a = {1, 2, 3, 3, 2}  # 重複は自動的に取り除かれます
b = {"apple", "banana"}

# 空集合は set() を使います。{} は空の辞書(dict)です。
empty_set = set()
empty_dict = {}

print(type(a), a)
print(type(empty_set), empty_set)
print(type(empty_dict), empty_dict)
実行結果
<class 'set'> {1, 2, 3}
<class 'set'> set()
<class 'dict'> {}

リテラルで作る際は順序が保証されないため、出力順は未定です。

結果の見た目を安定させたいときはsortedで整列してから表示するとよいです。

和集合と積集合の意味をシンプルに確認

和集合は「どちらか一方でも含まれる要素の全体」です。

積集合は「両方に共通する要素の集まり」です。

最初に概念をコードで実感しておきます。

Python
A = {1, 2, 3}
B = {3, 4}

U = A.union(B)          # 和集合
I = A.intersection(B)   # 積集合

# 表示の安定化のためにソートして出力します
print("和集合:", sorted(U))
print("積集合:", sorted(I))
実行結果
和集合: [1, 2, 3, 4]
積集合: [3]

集合演算の記号と読み方

数学の記号とPythonでの書き方、読み方を対応させておきます。

記号に慣れるとコードが直感的に読めます。

概念数学記号読み方PythonメソッドPython演算子
和集合A ∪ BわしゅうごうA.union(B)A | B
積集合A ∩ BせきしゅうごうA.intersection(B)A & B
差集合(参考)A − BさしゅうごうA.difference(B)A - B
対称差(参考)A △ BたいしょうさA.symmetric_difference(B)A ^ B

演算子版は算術ではなく集合演算として定義されており、整数に対する|&はビット演算なので混同しないようにしましょう。

和集合の作り方: union()

unionの使い方と戻り値

set.unionは新しい集合を返す非破壊的操作です。

元の集合は変更されません。

引数には他の集合だけでなく、リストやタプルなどの反復可能オブジェクトも渡せます。

Python
A = {1, 2, 3}
B = {3, 4}
C = [4, 5]  # リストもOK

U = A.union(B, C)  # A ∪ B ∪ C
print("Aはそのまま:", sorted(A))
print("和集合(新しい集合):", sorted(U))
実行結果
Aはそのまま: [1, 2, 3]
和集合(新しい集合): [1, 2, 3, 4, 5]

演算子パイプの使い方

和集合は演算子|でも記述できます。

可読性が高く、数学の記法にも近いので覚えておくと良いです。

Python
A = {1, 2}
B = {2, 3}
C = {3, 4}

U1 = A | B            # 2つの和集合
U2 = A | B | C        # 3つを連結して和集合
U3 = A.union(B).union(C)  # メソッド連鎖でも同じ結果

print(sorted(U1), sorted(U2), sorted(U3))
実行結果
[1, 2, 3] [1, 2, 3, 4] [1, 2, 3, 4]

両側がsetである必要があります。

片側がリストなどの場合は先にset()で包みます。

Python
A = {1, 2}
L = [2, 3]

# setに変換してから演算します
U = A | set(L)
print(sorted(U))
実行結果
[1, 2, 3]

複数の集合で和集合を求める

unionは可変長引数を取るため、複数の集合を一度に渡せます。

演算子|は連結で表現します。

Python
A = {1, 2}
B = {2, 3}
C = {3, 4}
D = {0, 4}

U_method = A.union(B, C, D)
U_op = A | B | C | D

print(sorted(U_method))
print(sorted(U_op))
実行結果
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]

破壊的操作のupdateとの違い

updateは集合をその場で更新する破壊的操作です。

戻り値はNoneで、対象の集合自体が書き換わります。

Python
A = {1, 2}
B = {2, 3}

U = A.union(B)  # 非破壊: 新しい集合を返す
print("unionの戻り値:", sorted(U))
print("Aは変化なし:", sorted(A))

ret = A.update(B)  # 破壊的: Aを書き換える、戻り値はNone
print("updateの戻り値:", ret)
print("Aは変化あり:", sorted(A))
実行結果
unionの戻り値: [1, 2, 3]
Aは変化なし: [1, 2]
updateの戻り値: None
Aは変化あり: [1, 2, 3]
項目unionupdate
変更対象新しい集合を作る自分自身を更新する
戻り値新しいsetNone
使いどころ元を残したい累積して集めたい

累積処理ではupdateが効率的です。

元を保ちたいときはunionを選びます。

積集合の作り方: intersection()

intersectionの使い方と戻り値

set.intersectionは共通要素のみを取り出した新しい集合を返します。

こちらも非破壊です。

Python
A = {1, 2, 3, 4}
B = {3, 4, 5}
I = A.intersection(B)

print(sorted(I))
print("Aは変化なし:", sorted(A))
実行結果
[3, 4]
Aは変化なし: [1, 2, 3, 4]

演算子アンパサンドの使い方

積集合は演算子&で書くのが簡潔です。

複数集合でも連結して書けます。

Python
A = {1, 2, 3, 4}
B = {2, 4, 6}
C = {0, 2, 4, 8}

I1 = A & B
I2 = A & B & C  # 3つの積集合

print(sorted(I1))
print(sorted(I2))
実行結果
[2, 4]
[2, 4]

複数の集合で積集合を求める

可変長引数版を使うと、共通部分を一度に求められます。

Python
A = {1, 2, 3, 4, 5}
B = {2, 3, 5, 7}
C = {0, 2, 3, 5, 8}

I_method = A.intersection(B, C)
I_chain = A.intersection(B).intersection(C)

print(sorted(I_method))
print(sorted(I_chain))
実行結果
[2, 3, 5]
[2, 3, 5]

破壊的操作のintersection_updateとの違い

intersection_updateは自身を共通要素だけに絞る破壊的操作です。

戻り値はNoneで、元の集合が書き換わります。

Python
A = {1, 2, 3, 4}
B = {2, 4, 6}

I = A.intersection(B)  # 非破壊
print("intersection:", sorted(I))
print("Aは変化なし:", sorted(A))

ret = A.intersection_update(B)  # 破壊的
print("intersection_updateの戻り値:", ret)
print("Aは共通要素だけに:", sorted(A))
実行結果
intersection: [2, 4]
Aは変化なし: [1, 2, 3, 4]
intersection_updateの戻り値: None
Aは共通要素だけに: [2, 4]

例と注意点で身に付ける

タグやカテゴリで共通部分と全体を求める

ブログ記事のタグを例に、全記事で使われたタグ(積集合)と、どれかで使われたタグ(和集合)を求めます。

Python
post1_tags = {"python", "set", "beginner"}
post2_tags = {"python", "tips", "set"}
post3_tags = {"python", "performance"}

# どれかで使われたタグ(和集合)
all_tags = post1_tags | post2_tags | post3_tags

# すべての記事で共通のタグ(積集合)
common_tags = post1_tags & post2_tags & post3_tags

print("和集合(全体):", sorted(all_tags))
print("積集合(共通):", sorted(common_tags))
実行結果
和集合(全体): ['beginner', 'performance', 'python', 'set', 'tips']
積集合(共通): ['python']

複数の集合がリストに入っている場合は、functools.reduceや反復でまとめる方法があります。

更新型のupdateintersection_updateを使うと効率よく累積できます。

Python
from functools import reduce

tag_sets = [
    {"python", "set", "beginner"},
    {"python", "tips", "set"},
    {"python", "performance"},
]

# reduceで和集合
all_tags = reduce(lambda acc, s: acc | s, tag_sets, set())

# 先頭を基準に積集合
common_tags = set(tag_sets[0])
for s in tag_sets[1:]:
    common_tags.intersection_update(s)  # 破壊的に絞り込み

print("和集合:", sorted(all_tags))
print("積集合:", sorted(common_tags))
実行結果
和集合: ['beginner', 'performance', 'python', 'set', 'tips']
積集合: ['python']

リストをsetに変換して比較する

重複を含むリスト同士の共通要素や全体を調べたいとき、setに変換してから集合演算すると簡潔で高速です。

Python
list_A = ["alice", "bob", "alice", "carol"]
list_B = ["bob", "dave", "bob", "erin"]

set_A = set(list_A)  # {'alice', 'bob', 'carol'}
set_B = set(list_B)  # {'bob', 'dave', 'erin'}

union_AB = set_A | set_B
intersect_AB = set_A & set_B

print("ユーザー全体(和集合):", sorted(union_AB))
print("共通ユーザー(積集合):", sorted(intersect_AB))
実行結果
ユーザー全体(和集合): ['alice', 'bob', 'carol', 'dave', 'erin']
共通ユーザー(積集合): ['bob']

順序が必要な場合は、最後にsortedlistで整形します。

set自体は順序を持ちません。

可読性とパフォーマンスのコツ

集合演算を読みやすく、かつ効率よく書くためのポイントをいくつか挙げます。

箇条書きに頼らず、それぞれの意図を説明します。

まず、単純な2〜3集合の演算は演算子|&を使うと読みやすくなります。

数学の記法に近いため、ロジックがひと目で伝わります。

一方で、引数が可変であったり、別の型(リストやタプル)も同時に扱いたい場合は、unionintersectionのメソッド版が柔軟です。

次に、ループで集合を累積していく場面では、非破壊で新しい集合を毎回生成するよりも、updateintersection_updateを使ってその場で更新するほうがメモリ効率と速度の面で有利です。

特に大規模データでは差が出ます。

初期値の選び方も重要で、和集合の累積なら空集合set()から、積集合の累積なら最初の集合を基準にintersection_updateで絞っていくのが定石です。

また、出力の安定性が必要なら、sortedで並べ替えてから表示します。

テストコードでも順序の差分で失敗しないように、比較時にはset同士で比較するか、比較直前でソート済みリストに変換すると良いです。

最後に、型の混在で思わぬ例外を避けるため、演算子|&を使う際は両オペランドがsetになっているかを確認し、必要に応じてset()で明示的に変換します。

片方が空集合のときの結果を確認

空集合との演算は境界条件としてよく出ます。

結果を事前に知っておくと安全です。

Python
A = {1, 2, 3}
E = set()  # 空集合

print("A ∪ ∅:", sorted(A | E))
print("A ∩ ∅:", sorted(A & E))
print("破壊的updateでAに∅を足す:", sorted((A.copy()).update(E) or A))  # 戻り値Noneに注意
print("破壊的intersection_updateでAと∅の共通:", (A.copy()).intersection_update(E) or sorted(A.copy()))
実行結果
A ∪ ∅: [1, 2, 3]
A ∩ ∅: []
破壊的updateでAに∅を足す: [1, 2, 3]
[]

上の例では、updateintersection_updateの戻り値がNoneである点にも着目してください。

intersection_updateの直後は集合自体が空になります。

まとめ

Pythonのsetは、データの重複排除と高速な集合演算を同時に実現する強力な道具です。

和集合はunionまたは|、積集合はintersectionまたは&で求められ、非破壊か破壊的かでunion/intersectionupdate/intersection_updateを使い分けます。

複数集合の演算では、メソッドの可変長引数や演算子の連結が便利です。

実務では、タグの統合やユーザー集合の比較などに直結し、パフォーマンスの観点では累積には破壊的更新が有利です。

空集合や型の混在、表示順の安定化といった注意点も押さえつつ、読みやすいコードを心がけることで、集合演算を安全かつ効率的に活用できます。

Python 実践TIPS - データ操作
この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!