🔙
🔝

セットさんは重複を許さない

集合(set)とちょっとだけ数学知識が身に付きます👍

Chatpter.1 - セット型とは

セット型 (set) とは

  • リストや辞書と同じようなデータ構造をしています。

  • 見た目はリストに、性質は辞書に似ています。

  • こんな書き方をします。

    nums = {1, 2, 3, 4, 5}
    
    titles = {'セットの花嫁', 'セット会の一存', '怪盗セットテール'}
    

    { } で囲います。辞書と同じです。


  • 空のセットを作る場合はこう書きます。

    set_blank = set()
    
    set()← 空のセットはこの様に表示される

    set_blank = {} と書くと辞書型になってしまうので注意!

    1. リストとセットの相違点

    2. 順序の保持: リストは要素の順序を保持しますが、セットは要素の順序を
      保証しません。

    3. インデックスの利用: リストは要素に対してインデックスを使用して
      アクセスできますが、セットは順序がないため、インデックスを用いた
      アクセスができません。

    4. 重複する要素: リストは同じ要素を複数回持つことができますが、
      セットは重複を許容しません。セット内の要素は一意である必要があります。

    5. 可変性: リストは可変(mutable)であり、要素の追加、変更、削除が可能です。
      一方で、セットも可変ですが、一度作成したセット内の要素は変更できません。
      セットに新しい要素を追加することはできます。

    6. リテラル表現の違い: リストは角括弧 [] を使用して表現されますが、
      セットは波括弧 {} を使用します。

    < ChatGPT の回答を一部加筆修正 >

  • セットの特徴をまとめると、

    リストの [ ] が { } になる。
    見た目はリストに似ています。

    辞書のキーにあたるものが、セットの値になる。
    辞書のキーと同じく、セットの値も重複を絶対に許しません。絶対にだ。
    キーが無いので添字も無く、要素に特殊な方法でアクセスして利用します。

    辞書のキーと同じように、セットの値も変更できない。
    不変(イミュータブル)の型を使う必要があります。

    ※  現在の Python3 では、値の順序がある程度整列されて表示されます。
    セット型には順序が無いので、ソートされているわけではありません。
    ちょっと見やすくしてくれているだけです。・・・イイ!😸


  • セットの使いどころの1つとして手短に言うと、
    「順序を必要としない」「重複する値は不要。1つあればいい」

    こういうリストを作ろうとしたら、そこは代わってセットの出番です。

  • 既に存在する値を追加するとこうなります。

    nums = {1, 2, 3, 4, 5}
    nums.add(1)   # セットは.add() で追加
    print(nums)
    
    {1, 2, 3, 4, 5}
    慣れる為にも自分で打ち込んでみよう!

    何も変化がありません。
    値の重複を許さないので無視されたのです。ひどい...。


  • 重複する値があるセットは、こうなります。

    nums = {1, 2, 3, 4, 5, 3, 2}
    print(nums)
    
    {1, 2, 3, 4, 5}
    慣れる為にも自分で打ち込んでみよう!

    やはり値が一意にまとめられてしまいました。


  • 重複する値があるリストをセットにすると、こうなります。

    nums = [1, 2, 3, 4, 5, 3, 2]
    set_nums = set(nums)    # set() を使ってセットに変換
    print(set_nums)
    
    {1, 2, 3, 4, 5}
    慣れる為にも自分で打ち込んでみよう!

    これもやはり値が一意にまとめられてしまいました。


  • まとめられちゃダメみたいな言い方しましたけど、この特性を活かした使い途があるからこそ存在する機能なのです。

  • それはもう無限の可能性を秘めたすごい機能です。

Chatpter.2 - 一意だョ!全員集合

セットの使い方

セットの使いどころ

セットの使いどころ

  • 多くの可能性を秘めているセット型ですが、大きく分けて2つの使い方があります。

  • 1つ目は「値が一意である特性」を活かした使い方です。

  • 簡単なところで、こんな問題があったとします。


  • 次の数列の中から、二番目に大きな数を求めて下さい。

    [7, 4, 7, 3, 9, 1, 9, 8]


  • max() 関数では一番大きな数しか求められません。

  • リストのまま求めようとすると・・・想像しただけで面倒です。

  • そんな時、 set() が役に立ちます。

    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    set_nums = set(nums)
    sorted_nums = sorted(set_nums, reverse=True)
    print(sorted_nums[1])
    
    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    
    set_nums = set([7, 4, 7, 3, 9, 1, 9, 8])
    
    sorted_nums = sorted(set_nums, reverse=True)
    print(sorted_nums[1])
    
    set() で重複を削除する
    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    
    set_nums = {7, 4, 3, 9, 1, 8}
    
    sorted_nums = sorted(set_nums, reverse=True)
    print(sorted_nums[1])
    
    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    set_nums = {7, 4, 3, 9, 1, 8}
    
    sorted_nums = sorted({7, 4, 3, 9, 1, 8}, reverse=True)
    
    print(sorted_nums[1])
    
    ↓ ソートの結果がリストになって返る
    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    set_nums = {7, 4, 3, 9, 1, 8}
    
    sorted_nums = [9, 8, 7, 4, 3, 1]
    
    print(sorted_nums[1])
    
    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    set_nums = {7, 4, 3, 9, 1, 8}
    sorted_nums = [9, 8, 7, 4, 3, 1]
    
    print([9, 8, 7, 4, 3, 1][1])
    
    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    set_nums = {7, 4, 3, 9, 1, 8}
    sorted_nums = [9, 8, 7, 4, 3, 1]
    
    print(8)
    
    8
  • 次に紹介する2つ目は、数学の「集合」を使った演算です。

  • 集合や集合演算はちょっと難しいので、この後新しく説明します。

集合と集合演算

集合と集合演算

  • 「集合」は、一意の値をグループにしたもので、セットの { } そのものです。

  • 集合 = セットです。

  • 「〇種類で1セット」のセットです。

  • まともに説明すると説明量が多すぎるので、集合について学びたい方は数学の集合か、情報処理の集合を別途学んでみてください。数学は苦手割愛します。😎

  • でも全然わからなくても、集合の事というよりはset() でこんなことができるよ~くらいの情報分だけ簡単に扱いますので、どうぞ肩の力を抜いて気楽に読んでいってください。

  • 使えるようになると捗るset()で扱う「集合演算しゅうごうえんざん」は、まずこの3つです。


  • 和集合 | 2つの集合をあわせる
    差集合 集合2に含まれる値を集合1から取り除く
    積集合 & 2つの集合のどちらにもある値だけにする

  • とりあえずなんのこっちゃですけど、それぞれをプログラムで説明します。

  • 和集合

    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    union_nums = set(nums1) | set(nums2)
    
    print(union_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    union_nums = set([1, 2, 2, 3]) | set([2, 3, 4, 5])
    
    print(union_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    union_nums = {1, 2, 3} | {2, 3, 4, 5}
    
    print(union_nums)
    
    ↓ セットとセットを | で繋ぐと、
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    union_nums = {1, 2, 3, 4, 5}
    
    print(union_nums)
    
    {1, 2, 3, 4, 5}

    1つのセットになって重複した値が削除されます。

  • 複数のセットを1つのセットにまとめた」のが和集合の結果になります。


  • | は「バーティカル・バー」と言って、[Shift] + \ で打ち込めます。
    \ は、Backspace←キーの左にあるほう(¥)です。


  • セットも辞書と同じように + で繋ぐことはできません。

    union_nums = {1, 2, 3} + {2, 3, 4, 5}
    print(union_nums)
    
    Traceback (most recent call last):
      File "Main.py", line 1, in <module>
        union_nums = {1, 2, 3} + {2, 3, 4, 5}
    TypeError: unsupported operand type(s) for +: 'set' and 'set'
    

  • じゃあ辞書も | で繋げられるんじゃね?

    sisters1 = {'一穂': 24, 'れんげ': 7}
    sisters2 = {'ひかげ': 16}
    print(sisters1 | sisters2)
    
    Traceback (most recent call last):
      File "Main.py", line 3, in <module>
        print(sisters1 | sisters2)
    TypeError: unsupported operand type(s) for |: 'dict' and 'dict'
    

    ダメでした。😓

    辞書は辞書型であって「集合 (set型)」ではないからです。😽

    ※ 辞書でもこの方法が使えるようになりました!
    キーが重複する場合は後者の値で更新されてしまいますので注意です。

  • 差集合

    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    difference_nums = set(nums1) - set(nums2)
    
    print(difference_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    difference_nums = set([1, 2, 2, 3]) - set([2, 3, 4, 5])
    
    print(difference_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    difference_nums = {1, 2, 3} - {2, 3, 4, 5}
    
    print(difference_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    difference_nums = {1}
    
    print(difference_nums)
    
    {1}

  • わかると結構便利な差集合演算です。

  • 「元のリストからセットにして重複を消し、差集合でさらに不要な値を消してからリストに戻して使う」

    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    del_nums = {1, 4, 6, 9}
    
    new_nums = list(set(nums) - del_nums)
    
    print(new_nums)    # 案外簡単!
    
    [8, 3, 7]← セットの特性で順序が変わることがあります
  • この場合、セットをprint()で見ると昇順に並んでいるように見えたりしますけど、リストにすると順番が変わりますので、ソートが必要な時は list()sorted() にするか、 .sort() を使ってソートしてください。

  • 「元の順番どおりじゃないと困る!」という場合もありますけど、その時は思いっきり悩んで自力で解決してください。😸
    IQを上げるチャンス!!

  • 積集合

    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    intersection_nums = set(nums1) & set(nums2)
    
    print(intersection_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    intersection_nums = set([1, 2, 2, 3]) & set([2, 3, 4, 5])
    
    print(intersection_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    intersection_nums = {1, 2, 3} & {2, 3, 4, 5}
    
    print(intersection_nums)
    
    nums1 = [1, 2, 2, 3]
    nums2 = [2, 3, 4, 5]
    intersection_nums = {2, 3}
    
    print(intersection_nums)
    
    {2, 3}

  • どの集合演算も、セットの特性を理解すれば自ずと使いどころに気付くと思います。

  • その為にも、値の重複が無く、順番も気にすることが無いリストを扱うことがあったら、無理してでもセットに置き換えて使ってみてください。

  • 本当に意味がわかると、セットの便利さもわかってくると思いますし、リストの使用頻度も減り、なんかいい感じ💖感が上昇します。

  • 集合演算は、要素が空でもエラーは出ません。

  • たとえ両方が空でもエラーは出ません。

  • 不安要素があるならばprint()で値を確認しながら慎重に操作してください。

  • 値は int だけでなく strタプル でも大丈夫です。

  • セットの値は 不変の型(イミュータブル) であれば、たとえ混在していても使えます。

  • += みたいに |= とか &= とか使えるんじゃね?

    nums = {1, 2, 3}
    nums |= {2, 3, 4, 5}
    
    print(nums)
    
    {1, 2, 3, 4, 5}

    nums = {1, 2, 3}
    nums -= {2, 3, 4, 5}
    
    print(nums)
    
    {1}

    nums = {1, 2, 3}
    nums &= {2, 3, 4, 5}
    
    print(nums)
    
    {2, 3}

    やるじゃないの。(; ゚Д゚) ッテカダレョアンタ

    3種類とも試してみてください!
Chatpter.3 - ぼくたちは代入ができない 3期

セットも可変なんだからね!

  • 回で修了するこのコピー。

  • 変数2 = 変数1

    このやり方を「コピー(copy)」または「値のコピー」と言います。

  • 変数のまま 変数2変数1 を代入する(コピーする)ことが、

    出来る! 出来ない!
    str list
    int dict
    tuple set

    米 まだ満腹でない子は細型になっています。🍚🐈

  • セットも、リストや辞書と挙動はまったく一緒です。

  • セットでもset()関数を使うとコピーできます。

    titles = {'セットの花嫁', 'セット会の一存'}
    new_titles = set(titles)
    print(new_titles)
    
    new_titles -= {'セット会の一存'}
    print(new_titles)
    print(titles)
    
    {'セットの花嫁', 'セット会の一存'}← コピー直後 の new_titles
    {'セットの花嫁'}← 書き換えた方の new_titles
    {'セットの花嫁', 'セット会の一存'}← コピー元の titles
Chatpter.4 - リストをセットに手動で変換してみた

手動で値を一意にする

  • セットがどれだけ楽な機能なのか、目で見て確認してみましょう。

    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    
    new_nums = []
    for num in nums:
        if num not in new_nums:
            new_nums.append(num)
    
    print(new_nums)
    
    [7, 4, 3, 9, 1, 8]

    文で「new_nums の中に num がなかったら new_nums に num を加える」ことをしています。😵‍💫

  • これをセットで書くと、

    nums = [7, 4, 7, 3, 9, 1, 9, 8]
    new_nums = set(nums)
    print(new_nums)
    
    {1, 3, 4, 7, 8, 9}

    の部分を、全てset()関数がやっています。

  • 楽して書けるならどんどん楽をするべきです。

  • プログラムが簡潔になればそれだけバグも避けられますし、読みやすくもなります。

  • 読みやすくなれば、バグがあった時に見つけやすくもなります。

  • 仕組みを知ることも大切ですが、それは後からでもできることです。


  • プログラムはまず「正しく動く」ことが大切です。

  • それから無駄を削ぎ落していくと「速く動く」ことに繋がります。

  • 無駄が少なくなると、今度は修正や改変がしやすくなります。

  • 仕事やスポーツと同じですね。


  • プログラミングは哲学です。

  • プログラムの数だけ哲学があるから、プログラミングは面白いよ。😻