Python リストを元の順序を保持したままセットに変換する方法

PythonBeginner
オンラインで実践に進む

はじめに

Python の組み込みデータ構造は、データの管理と操作に柔軟な方法を提供します。このチュートリアルでは、Python のリストを、要素の元の順序を保持しながらセットに変換する方法を探求します。このテクニックは、リストから重複を削除しつつ、各ユニーク要素の最初の出現順序を維持する必要がある場合に特に役立ちます。

このチュートリアルの終わりには、Python におけるリストとセットの違いを理解し、要素の元の順序を維持しながらリストをセットに変換するための複数のテクニックを習得できます。

Python のリストとセットの理解

リストをセットに変換する前に、Python におけるこれら 2 つのデータ構造の基本的な特性を理解しましょう。

Python のリスト

Python のリストは、異なるデータ型の要素を格納できる順序付きのコレクションです。重複した値を許可し、要素の挿入順序を保持します。

リストをデモンストレーションするために、簡単な Python ファイルを作成しましょう。コードエディタを開き、/home/labex/project ディレクトリに list_demo.py という名前の新しいファイルを作成します。

## Python のリスト
my_list = [1, 2, 3, 2, 4, 5, 3]

print("Original list:", my_list)
print("Length of list:", len(my_list))
print("First element:", my_list[0])
print("Last element:", my_list[-1])
print("First 3 elements:", my_list[:3])
print("Does list contain duplicates?", len(my_list) != len(set(my_list)))

次に、このファイルをターミナルで実行します。

python3 list_demo.py

次のような出力が表示されるはずです。

Original list: [1, 2, 3, 2, 4, 5, 3]
Length of list: 7
First element: 1
Last element: 3
First 3 elements: [1, 2, 3]
Does list contain duplicates? True

Python のセット

セットは、一意な要素の順序付けられていないコレクションです。リストをセットに変換すると、重複する要素は自動的に削除されますが、元の順序は保持されません。

セットを調べるために、set_demo.py という名前の別のファイルを作成しましょう。

## Python のセット
my_list = [1, 2, 3, 2, 4, 5, 3]
my_set = set(my_list)

print("Original list:", my_list)
print("Converted to set:", my_set)
print("Length of list:", len(my_list))
print("Length of set:", len(my_set))
print("Does set maintain order?", list(my_set) == [1, 2, 3, 4, 5])

このファイルを実行します。

python3 set_demo.py

出力は次のようになります。

Original list: [1, 2, 3, 2, 4, 5, 3]
Converted to set: {1, 2, 3, 4, 5}
Length of list: 7
Length of set: 5
Does set maintain order? False

セットはすべての重複を削除しましたが、順序は元のリストと異なる可能性があることに注意してください。これは、Python のセットが本質的に順序付けられていないためです。

基本的なアプローチ:リストをセットに変換する

リストとセットの違いを理解したところで、リストをセットに変換する方法と、この変換が持つ意味を探求しましょう。

簡単な変換

リストをセットに変換する最も基本的な方法は、組み込みの set() 関数を使用することです。 basic_conversion.py という名前の新しいファイルを作成します。

## リストからセットへの基本的な変換
fruits = ["apple", "banana", "orange", "apple", "pear", "banana"]

## リストをセットに変換 (重複を削除しますが、順序は失われます)
unique_fruits = set(fruits)

print("Original list:", fruits)
print("As a set:", unique_fruits)

## リストに戻す (順序は保持されません)
unique_fruits_list = list(unique_fruits)
print("Back to list:", unique_fruits_list)

このファイルを実行します。

python3 basic_conversion.py

次のような出力が表示されるはずです。

Original list: ['apple', 'banana', 'orange', 'apple', 'pear', 'banana']
As a set: {'orange', 'banana', 'apple', 'pear'}
Back to list: ['orange', 'banana', 'apple', 'pear']

セットはすべての重複を削除しましたが、順序は元のリストと異なることに注意してください。セットをリストに戻すと、順序は元のリストと同じではありません。

順序の問題点

この簡単な変換は、私たちが解決しようとしている問題を示しています。リストをセットに変換すると、要素の元の順序が失われます。元の順序が重要な場合、このアプローチは適切ではありません。

この問題がなぜ問題になるのかを示すために、例を変更しましょう。 order_matters.py という名前のファイルを作成します。

## 順序が重要である理由を示す例
steps = ["Preheat oven", "Mix ingredients", "Pour batter", "Bake", "Mix ingredients"]

## set を使用して重複を削除
unique_steps = list(set(steps))

print("Original cooking steps:", steps)
print("Unique steps (using set):", unique_steps)
print("Is the order preserved?", unique_steps == ["Preheat oven", "Mix ingredients", "Pour batter", "Bake"])

このファイルを実行します。

python3 order_matters.py

出力は次のようになります。

Original cooking steps: ['Preheat oven', 'Mix ingredients', 'Pour batter', 'Bake', 'Mix ingredients']
Unique steps (using set): ['Preheat oven', 'Bake', 'Mix ingredients', 'Pour batter']
Is the order preserved? False

この例では、調理手順の順序が重要です。材料を混ぜる前に焼くと、結果は悲惨なものになります。これは、重複を削除する際に元の順序を保持する方法が必要な理由を示しています。

リストをセットに変換する際に順序を保持する

問題点を理解したところで、要素の元の順序を保持しながらリストをセットに変換する方法を探求しましょう。

方法 1:順序を保持するために辞書を使用する

1 つのアプローチは、要素の順序を追跡するために辞書を使用することです。Python 3.7 以降では、辞書はデフォルトで挿入順序を保持します。

dict_approach.py という名前の新しいファイルを作成します。

## 順序を保持するために辞書を使用する
fruits = ["apple", "banana", "orange", "apple", "pear", "banana"]

## リスト要素をキーとして持つ辞書を作成します
## これにより、順序を保持しながら重複が自動的に削除されます
unique_fruits_dict = dict.fromkeys(fruits)

## 辞書のキーをリストに戻します
unique_fruits = list(unique_fruits_dict)

print("Original list:", fruits)
print("Unique elements (order preserved):", unique_fruits)

このファイルを実行します。

python3 dict_approach.py

次のように表示されるはずです。

Original list: ['apple', 'banana', 'orange', 'apple', 'pear', 'banana']
Unique elements (order preserved): ['apple', 'banana', 'orange', 'pear']

各要素の最初の出現順序が保持されていることに注意してください。

方法 2:OrderedDict を使用する

Python 3.7 より前のバージョンのユーザー向け、または意図をより明確にするために、collections モジュールから OrderedDict を使用できます。

ordered_dict_approach.py という名前の新しいファイルを作成します。

## 順序を保持するために OrderedDict を使用する
from collections import OrderedDict

fruits = ["apple", "banana", "orange", "apple", "pear", "banana"]

## リスト要素をキーとして持つ OrderedDict を作成します
## これにより、順序を保持しながら重複が自動的に削除されます
unique_fruits_ordered = list(OrderedDict.fromkeys(fruits))

print("Original list:", fruits)
print("Unique elements (order preserved):", unique_fruits_ordered)

このファイルを実行します。

python3 ordered_dict_approach.py

出力は次のようになります。

Original list: ['apple', 'banana', 'orange', 'apple', 'pear', 'banana']
Unique elements (order preserved): ['apple', 'banana', 'orange', 'pear']

方法 3:ループとセットを使用してチェックする

別の方法は、ループとセットを使用して、要素を以前に見たことがあるかどうかをチェックすることです。

loop_approach.py という名前の新しいファイルを作成します。

## 順序を保持するためにループとセットを使用する
fruits = ["apple", "banana", "orange", "apple", "pear", "banana"]

unique_fruits = []
seen = set()

for fruit in fruits:
    if fruit not in seen:
        seen.add(fruit)
        unique_fruits.append(fruit)

print("Original list:", fruits)
print("Unique elements (order preserved):", unique_fruits)

このファイルを実行します。

python3 loop_approach.py

出力は次のようになります。

Original list: ['apple', 'banana', 'orange', 'apple', 'pear', 'banana']
Unique elements (order preserved): ['apple', 'banana', 'orange', 'pear']

3 つの方法はすべて、同じ結果を達成します。つまり、各要素の最初の出現順序を保持しながら、重複を削除します。

実用的な例:テキストデータの分析

これまでに学んだことを、現実世界の例に適用してみましょう。つまり、最初の出現順序を保持しながら、テキスト内の単語の頻度を分析します。

テキスト分析ツールの作成

text_analyzer.py という名前の新しいファイルを作成します。

def analyze_text(text):
    """
    テキストを分析して、最初の出現順に一意な単語とその頻度を見つけます。
    """
    ## テキストを単語に分割し、小文字に変換します
    words = text.lower().split()

    ## 単語から句読点を削除します
    clean_words = [word.strip('.,!?:;()[]{}""\'') for word in words]

    ## 順序を保持しながら頻度をカウントします
    word_counts = {}
    unique_words_in_order = []

    for word in clean_words:
        if word and word not in word_counts:
            unique_words_in_order.append(word)
        word_counts[word] = word_counts.get(word, 0) + 1

    return unique_words_in_order, word_counts

## サンプルテキスト
sample_text = """
Python is amazing. Python is also easy to learn.
With Python, you can create web applications, data analysis tools,
machine learning models, and much more. Python has many libraries
that make development faster. Python is versatile!
"""

## テキストを分析します
unique_words, word_frequencies = analyze_text(sample_text)

## 結果を出力します
print("Text sample:")
print(sample_text)
print("\nUnique words in order of first appearance:")
print(unique_words)
print("\nWord frequencies:")
for word in unique_words:
    if word:  ## 空の文字列をスキップします
        print(f"'{word}': {word_frequencies[word]} times")

このファイルを実行します。

python3 text_analyzer.py

出力には、テキストに最初に現れた順序で一意な単語と、その頻度が表示されます。

Text sample:

Python is amazing. Python is also easy to learn.
With Python, you can create web applications, data analysis tools,
machine learning models, and much more. Python has many libraries
that make development faster. Python is versatile!

Unique words in order of first appearance:
['python', 'is', 'amazing', 'also', 'easy', 'to', 'learn', 'with', 'you', 'can', 'create', 'web', 'applications', 'data', 'analysis', 'tools', 'machine', 'learning', 'models', 'and', 'much', 'more', 'has', 'many', 'libraries', 'that', 'make', 'development', 'faster', 'versatile']

Word frequencies:
'python': 5 times
'is': 3 times
'amazing': 1 times
'also': 1 times
...

ツールの改善

より複雑なシナリオを処理するようにテキストアナライザーを強化しましょう。 improved_analyzer.py という名前のファイルを作成します。

from collections import OrderedDict

def analyze_text_improved(text):
    """
    より複雑なシナリオを処理し、より多くの統計を提供する、テキストアナライザーの改良版です。
    """
    ## テキストを単語に分割し、小文字に変換します
    words = text.lower().split()

    ## 単語から句読点を削除します
    clean_words = [word.strip('.,!?:;()[]{}""\'') for word in words]

    ## 順序を保持し、頻度をカウントするために OrderedDict を使用します
    word_counts = OrderedDict()

    for word in clean_words:
        if word:  ## 空の文字列をスキップします
            word_counts[word] = word_counts.get(word, 0) + 1

    ## 統計を取得します
    total_words = sum(word_counts.values())
    unique_words_count = len(word_counts)

    return list(word_counts.keys()), word_counts, total_words, unique_words_count

## サンプルテキスト
sample_text = """
Python is amazing. Python is also easy to learn.
With Python, you can create web applications, data analysis tools,
machine learning models, and much more. Python has many libraries
that make development faster. Python is versatile!
"""

## テキストを分析します
unique_words, word_frequencies, total_count, unique_count = analyze_text_improved(sample_text)

## 結果を出力します
print("Text sample:")
print(sample_text)
print("\nStatistics:")
print(f"Total words: {total_count}")
print(f"Unique words: {unique_count}")
print(f"Uniqueness ratio: {unique_count/total_count:.2%}")

print("\nTop 5 most frequent words:")
sorted_words = sorted(word_frequencies.items(), key=lambda x: x[1], reverse=True)
for word, count in sorted_words[:5]:
    print(f"'{word}': {count} times")

このファイルを実行します。

python3 improved_analyzer.py

追加の統計情報を含む出力が表示されるはずです。

Text sample:

Python is amazing. Python is also easy to learn.
With Python, you can create web applications, data analysis tools,
machine learning models, and much more. Python has many libraries
that make development faster. Python is versatile!

Statistics:
Total words: 38
Unique words: 30
Uniqueness ratio: 78.95%

Top 5 most frequent words:
'python': 5 times
'is': 3 times
'to': 1 times
'learn': 1 times
'with': 1 times

この実用的な例は、テキスト分析などの現実世界のアプリケーションで、重複を削除する際に要素の順序を保持することがいかに役立つかを示しています。

パフォーマンス比較とベストプラクティス

順序を保持しながらリストをセットに変換するいくつかの方法を探求したので、それらのパフォーマンスを比較し、いくつかのベストプラクティスを確立しましょう。

パフォーマンステストの作成

performance_test.py という名前の新しいファイルを作成します。

import time
from collections import OrderedDict

def method1_dict(data):
    """Using dict.fromkeys()"""
    return list(dict.fromkeys(data))

def method2_ordereddict(data):
    """Using OrderedDict.fromkeys()"""
    return list(OrderedDict.fromkeys(data))

def method3_loop(data):
    """Using a loop and a set"""
    result = []
    seen = set()
    for item in data:
        if item not in seen:
            seen.add(item)
            result.append(item)
    return result

def time_function(func, data, runs=100):
    """Measure execution time of a function"""
    start_time = time.time()
    for _ in range(runs):
        func(data)
    end_time = time.time()
    return (end_time - start_time) / runs

## テストデータ
small_list = list(range(100)) + list(range(50))  ## 150 items, 50 duplicates
medium_list = list(range(1000)) + list(range(500))  ## 1500 items, 500 duplicates
large_list = list(range(10000)) + list(range(5000))  ## 15000 items, 5000 duplicates

## テスト結果
print("Performance comparison (average time in seconds over 100 runs):\n")

print("Small list (150 items, 50 duplicates):")
print(f"dict.fromkeys():       {time_function(method1_dict, small_list):.8f}")
print(f"OrderedDict.fromkeys(): {time_function(method2_ordereddict, small_list):.8f}")
print(f"Loop and set:          {time_function(method3_loop, small_list):.8f}")

print("\nMedium list (1,500 items, 500 duplicates):")
print(f"dict.fromkeys():       {time_function(method1_dict, medium_list):.8f}")
print(f"OrderedDict.fromkeys(): {time_function(method2_ordereddict, medium_list):.8f}")
print(f"Loop and set:          {time_function(method3_loop, medium_list):.8f}")

print("\nLarge list (15,000 items, 5,000 duplicates):")
print(f"dict.fromkeys():       {time_function(method1_dict, large_list):.8f}")
print(f"OrderedDict.fromkeys(): {time_function(method2_ordereddict, large_list):.8f}")
print(f"Loop and set:          {time_function(method3_loop, large_list):.8f}")

パフォーマンステストを実行します。

python3 performance_test.py

出力には、さまざまなリストサイズでの各メソッドのパフォーマンスが表示されます。

Performance comparison (average time in seconds over 100 runs):

Small list (150 items, 50 duplicates):
dict.fromkeys():       0.00000334
OrderedDict.fromkeys(): 0.00000453
Loop and set:          0.00000721

Medium list (1,500 items, 500 duplicates):
dict.fromkeys():       0.00003142
OrderedDict.fromkeys(): 0.00004123
Loop and set:          0.00007621

Large list (15,000 items, 5,000 duplicates):
dict.fromkeys():       0.00035210
OrderedDict.fromkeys(): 0.00044567
Loop and set:          0.00081245

実際の数値はシステムによって異なる場合がありますが、いくつかのパターンに気付くはずです。

ベストプラクティス

実験に基づいて、いくつかのベストプラクティスを確立しましょう。 best_practices.py という名前のファイルを作成します。

"""
リストをセットに変換し、順序を保持するためのベストプラクティス
"""

## 例 1:Python 3.7 以降の場合、最高のパフォーマンスを得るには dict.fromkeys() を使用します
def preserve_order_modern(lst):
    """Python 3.7 以降に最適な方法 - dict.fromkeys() を使用"""
    return list(dict.fromkeys(lst))

## 例 2:古い Python バージョンとの互換性のために、OrderedDict を使用します
from collections import OrderedDict

def preserve_order_compatible(lst):
    """すべての Python バージョンと互換性のある方法 - OrderedDict を使用"""
    return list(OrderedDict.fromkeys(lst))

## 例 3:順序を保持しながら要素を処理する必要がある場合
def preserve_order_with_processing(lst):
    """順序を保持しながら要素を処理します"""
    result = []
    seen = set()

    for item in lst:
        ## ここでアイテムを処理するオプション
        processed_item = str(item).lower()  ## 例としての処理

        if processed_item not in seen:
            seen.add(processed_item)
            result.append(item)  ## 結果に元のアイテムを保持します

    return result

## デモ
data = ["Apple", "banana", "Orange", "apple", "Pear", "BANANA"]

print("Original list:", data)
print("Method 1 (Python 3.7+):", preserve_order_modern(data))
print("Method 2 (Compatible):", preserve_order_compatible(data))
print("Method 3 (With processing):", preserve_order_with_processing(data))

このファイルを実行します。

python3 best_practices.py

出力は、各メソッドがデータをどのように処理するかを示しています。

Original list: ['Apple', 'banana', 'Orange', 'apple', 'Pear', 'BANANA']
Method 1 (Python 3.7+): ['Apple', 'banana', 'Orange', 'apple', 'Pear', 'BANANA']
Method 2 (Compatible): ['Apple', 'banana', 'Orange', 'apple', 'Pear', 'BANANA']
Method 3 (With processing): ['Apple', 'Orange', 'Pear']

メソッド 3 は、小文字への処理により、「Apple」と「apple」を同じアイテムと見なしていることに注意してください。

推奨事項

実験に基づいて、いくつかの推奨事項を以下に示します。

  1. Python 3.7 以降では、最高のパフォーマンスを得るために dict.fromkeys() を使用します。
  2. すべての Python バージョンとの互換性のために、OrderedDict.fromkeys() を使用します。
  3. 重複をチェックしながらカスタム処理を実行する必要がある場合は、ループとセットのアプローチを使用します。
  4. 特定の要件に基づいて、大文字と小文字の区別やその他の変換を検討してください。

まとめ

このチュートリアルでは、以下のことを学びました。

  1. Python のリストとセットの基本的な違い

  2. リストをセットに変換すると、通常、順序が失われる理由

  3. 元の順序を保持しながら、リストをセットに変換する複数の方法:

    • Python 3.7 以降で dict.fromkeys() を使用する
    • 古い Python バージョンとの互換性のために OrderedDict.fromkeys() を使用する
    • より複雑な処理のために、セットとループを使用する
  4. テキスト分析などの現実世界の問題にこれらのテクニックを適用する方法

  5. さまざまなシナリオにおけるパフォーマンスに関する考慮事項とベストプラクティス

これらのテクニックは、データのクレンジング、ユーザー入力からの重複の削除、構成オプションの処理、およびその他の多くの一般的なプログラミングタスクに役立ちます。特定の要件に基づいて適切なアプローチを選択することで、よりクリーンで効率的な Python コードを作成できます。