コンテンツ

Python速習ガイド - ステップ1 基本的な文法とデータ型 (10) - 集合

Info
  • 本講座は、Pythonプログラミングの基礎を手を動かしながら最速で身につけるための講座です。
  • 「スタイルガイド」では、Pythonできれいなコードを書くためのガイドライン(PEP8)で紹介されている内容を主に記載しています。
  • 各コードは実行して結果を確認することができます。
    ページの再読み込みで元の内容に戻りますので、自由にいじってみてください。

「ステップ1 基本的な文法とデータ型」の続きです。

リスト、タプル、辞書に続く、データをまとめるデータ型として「集合 (set)」があります。
集合は重複のない要素の集まりで、順序が存在しません。

  • 集合は波括弧({, })内に要素をカンマ(,)区切りで並べて作成します。
  • 辞書との違いは、キー: 値 のペアではなく、値だけを並べる点です。
  • 空の集合は set() で作成します。
    • {} は空の辞書になるため注意してください。
  • set 関数を使用してリストやタプルから集合を作成することもできます。
スタイルガイド
  • カンマ(,)の後にはスペースを1個分空けましょう。
  • 長い集合の場合は複数行に分けると読みやすくなります。
    • 各要素は行の先頭に スペース4個分 の字下げ(インデント)を追加します。
    • 最後の要素の後ろにもカンマを入れましょう。
📚練習問題

好きなフルーツを3つ以上含む集合 my_fruits を作成してみましょう。
また、set() 関数を使用して同じ内容の集合を作成してください。

集合には以下のような特徴があります:

  1. 要素の重複がない: 同じ値は1つしか保存されません
  2. 順序がない: 要素に順序は存在せず、インデックスによるアクセスなどはできません
  3. 変更可能(ミュータブル): 要素の追加や削除が可能です
  4. ハッシュ化可能な値のみ要素にできる: 辞書のキーと同様、基本的にイミュータブル(変更不可)なオブジェクトのみ要素にできます

リストや辞書と同様に、集合もミュータブル(変更可能)な型です。
集合の操作についてこのあと紹介します。

辞書のキーと同様に、集合には ハッシュ化可能な値 のみを含めることができます。

  • 以下のイミュータブル(変更不可)な型が使えます:
    • 数値 (int, float)
    • 文字列 (str)
    • タプル (tuple) - 中に含まれるすべての要素もハッシュ化可能である場合
    • ブール値 (bool)
    • None
  • 以下のミュータブル(変更可能)な型は集合の要素として使用できません:
    • リスト (list)
    • 辞書 (dict)
    • 集合 (set)

リストと同様に、len関数を用いて集合のサイズを取得することができます。

  • add メソッドを使って集合に要素を追加できます。
  • remove メソッドは指定した要素を削除しますが、要素が存在しない場合はエラーになります。
  • discard メソッドも要素を削除しますが、要素が存在しなくてもエラーになりません。
  • pop メソッドは集合から任意の要素を取り出して削除します。
  • 集合には順序がないため、どの要素が取り出されるかは予測できません。

空集合(set())からpopしようとすると KeyError が発生します。

  • リストや辞書と同様に、innot in 演算子を使って要素の存在をチェックできます。

集合には様々な演算が定義されています。

  • 2つの集合のすべての要素を含む新しい集合を作成します。
  • 2つの集合の共通要素だけを含む新しい集合を作成します。
  • 1つ目の集合から2つ目の集合の要素を除いた新しい集合を作成します。
  • 2つの集合のいずれか一方にのみ含まれる要素を持つ新しい集合を作成します。
  • リストや辞書と同様に、copy メソッドで集合の(浅い)コピーを作成できます。
  • 辞書と同様にupdateメソッドを使うと、既存の集合を別の集合との和集合で更新します。
  • |演算子を使って値を更新する |= を使っても同様の操作が可能です。
    (足し算を使って値を更新する+=などと同様の省略形です)
  • &=を使うと、既存の集合を別の集合との共通要素だけに更新します。
  • -= を使うと、既存の集合から別の集合の要素を除去します。
  • ^= を使うと、既存の集合と別の集合いずれか一方にのみ存在する要素の集合に更新されます。
  • list関数を使用して、集合をリストに変換できます。
  • sorted 関数を使用すると、集合をソートされたリストに変換できます。
    (要素間に順序が定義されている場合のみ)
  • 集合にbool関数を適用すると、空の集合はFalse、それ以外はTrueを返します。
集合とリスト・タプル・辞書の使い分け

これまで学んだデータ型は、それぞれ異なる特徴と適した用途があります。

  • 集合 (set) を使うのが適切な場合:

    • 重複を排除したい場合
    • 要素の有無を頻繁に調べる場合
    • 複数のデータの共通要素や差分を見つけたい場合
    • 順序が重要でない場合
  • リスト (list) を使うのが適切な場合:

    • データの順序が重要な場合
    • 要素を追加、削除、変更する必要がある場合
    • 同じ値の要素を複数持ちたい場合
    • インデックスでアクセスしたい場合
  • タプル (tuple) を使うのが適切な場合:

    • データが変更されない場合
    • 辞書のキーや集合の要素として使用したい場合
    • 関連データをグループ化したい場合
    • データの順序が重要な場合
  • 辞書 (dict) を使うのが適切な場合:

    • キーと値のペアでデータを管理したい場合
    • キーで素早く値を取得したい場合
    • データに意味のあるラベルを付けたい場合

それぞれの特性を理解し、状況に応じて適切なデータ型を選択することが重要です。

集合(set)は、重複のない要素の集まりを表すデータ型で、様々な演算が定義されています。

主な特徴:

  • 重複する要素を持たない
  • 順序が定義されていない
  • 変更可能(要素の追加・削除ができる)
  • ハッシュ可能な値のみ要素にできる

主な操作:

  • .add(): 要素を追加する
  • .remove(): 要素を削除する(要素がない場合はエラー)
  • .discard(): 要素を削除する(要素がなくてもエラーにならない)
  • .pop(): 任意の要素を取り出して削除する

集合演算:

  • 和集合 (|): 2つの集合のすべての要素を含む集合
  • 積集合 (&): 2つの集合の共通要素だけを含む集合
  • 差集合 (-): 1つ目の集合から2つ目の集合の要素を除いた集合
  • 対称差集合 (^): 2つの集合のいずれか一方にのみ含まれる要素を持つ集合
📚練習問題1: 集合演算

集合演算を使って、以下の問題を解きましょう。

  1. フルーツの名前が含まれる2つの集合 fruits1, fruit2 があります:

    • fruits1 = {"apple", "banana", "orange", "grape"}
    • fruits2 = {"grape", "kiwi", "melon", "banana"}
  2. 両方の集合を合わせた集合を作成してください

  3. fruits1 にあって fruits2 にない果物の集合を作成してください

  4. どちらか一方の集合にだけ含まれる果物の集合を作成してください

解答例
📚練習問題2:ウェブサイト訪問者の分析

あなたはウェブサイトの分析担当者です。先週と今週の訪問者IDのリストがあります。
このデータに関して以下の分析を行いましょう。

  1. 先週と今週の「ユニークな」訪問者数と訪問者IDのリストをそれぞれ出力してください
  2. 101 は自分のIDでした。そのため、このIDは以降の分析対象から除外します
  3. 先週と今週の両方で訪問したユーザー数を出力してください
  4. 今週だけ訪問した(先週は訪問していない)ユーザーの人数を出力してください
  5. 先週と今週を合わせて、何人のユニークな訪問者がいたかを出力してください
  6. 先週だけ訪問した(今週は訪問していない)ユーザーの集合を出力してください
解答例
コラム:集合操作の計算量

集合の操作についても計算量を詳しく見てみましょう。

Pythonの集合はハッシュテーブルを内部で使用しているため、辞書と同様に多くの操作が高速です。
特に要素の存在チェック(in 演算子)はリストと比較して非常に効率的です。

※ 表中の計算量の意味:

  • O(1):入力サイズに関係なく、一定時間で処理される
  • O(n):最悪の場合、集合内の要素数 n に比例した時間がかかる
  • O(m):2つ目の入力集合の要素数 m に比例した時間がかかる
操作 計算量 説明
x in s O(1)(平均) 通常は定数時間だが、多数のハッシュ衝突が発生した場合は O(n) になる可能性がある
len(s) O(1) 集合のサイズは内部カウンタで保持されているため常に定数時間
s.add(x) O(1)(平均) 通常は定数時間だが、ハッシュ衝突や内部再配置が必要な場合は O(n) になることがある
s.remove(x)s.discard(x) O(1)(平均) 要素の検索と削除は通常定数時間だが、ハッシュ衝突の場合は O(n) になる可能性がある
s.pop() O(1)(平均) 任意の要素を取り出すのは通常定数時間だが、内部状態によってはO(n)になることがある
s | t O(n+m) 両方の集合の要素を調べる必要があるため、両方のサイズの合計に比例
s & t O(min(n,m)) 小さい方の集合の各要素について大きい方の集合に含まれるか確認するため、小さい方のサイズに比例
s - t O(n) 第1集合の各要素について第2集合に含まれるか確認するため、第1集合のサイズに比例
s ^ t O(n+m) 両方の集合を調べる必要があるため、両集合のサイズの合計に比例
s.update(t) O(m)(平均) 追加する集合のサイズに比例するが、リハッシュが必要な場合は O(n+m) になる
s.copy() O(n) すべての要素のシャローコピーを行うため、要素数に比例

特に、大きなデータセットから重複を除去したり、要素の存在をチェックしたりする場合には、
リストではなく集合を使用すると大幅にパフォーマンスが向上します。

関連記事