Python でリスト内の重複要素を効率的に削除する複数の方法を紹介し、それぞれの方法の特性と使い分けについて解説します。
set() を利用した方法
最も簡潔で多くの場合に高速な方法は、組み込みの set() 関数を使う方法です。set は重複を許さない集合を扱うデータ構造です。
リストを set に変換することで、重複要素が自動的に削除され、その後リストに戻します。
my_list = [1, 2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 9] # set を使って重複除去し、リストに戻す unique_list = list(set(my_list)) print(unique_list) # 出力: [1, 2, 3, 4, 5, 6, 7, 8, 9]
my_list を set(my_list) で集合に変換します。この時点で重複は除去されます。list() を使い、集合をリストに戻して unique_list に格納します。
set() を使う方法は、シンプルで可読性が高く、処理速度も速いため、多くの場合で推奨される方法です。ただし、set は要素の順序を保持しないため、元のリストの順序が重要な場合は注意してください。
リストの順序を保持したい場合の方法
元のリストの順序を維持したまま重複を削除したい場合は、ループ処理や dict.fromkeys() を使用します。
ループによる処理の実装
基本的なループ処理で重複を除去する方法を示します。新しいリストを用意し、元のリストの要素を順に追加していきます。追加する際に、新しいリストに同じ要素が存在しないかを確認し、存在しない場合のみ追加することで重複を除去します。
my_list = [1, 2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 9] unique_list = [] for item in my_list: if item not in unique_list: unique_list.append(item) print(unique_list) # 出力: [1, 2, 3, 4, 5, 6, 7, 8, 9]
空のリスト unique_list を作成します。my_list の各要素 item についてループ処理を行い、if item not in unique_list: で、item が unique_list に存在しないことを確認します。存在しない場合のみ、unique_list.append(item) で item を unique_list に追加します。
dict.fromkeys() を利用する方法 (Python 3.7 以降利用可)
Python 3.7 以降では、辞書のキーが順序を保持するようになったため、dict.fromkeys() を使用して重複を除去しつつ、元のリストの順序を維持できます。
my_list = [1, 2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 9] # dict.fromkeys() で重複を除去し、リストに変換 unique_list = list(dict.fromkeys(my_list)) print(unique_list) # 出力: [1, 2, 3, 4, 5, 6, 7, 8, 9]
dict.fromkeys(my_list) は、my_list の要素をキーとし、値を None とする辞書を作成します。辞書のキーは重複しないため、この時点で重複が除去されます。また、Python 3.7 以降では挿入順が保持されます。list() で辞書をリストに変換し、unique_list に格納します。dict.fromkeys() を使う方法は、ループ処理を併用するよりも簡潔で、多くの場合で高速です。
参考: リスト内包表記
リスト内包表記でも重複除去は可能ですが、効率が悪く他の方法に勝るメリットもありませんので、非推奨です。参考までに紹介します。
my_list = [1, 2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 8, 9, 9] unique_list = [] [unique_list.append(x) for x in my_list if x not in unique_list] print(unique_list) # 出力: [1, 2, 3, 4, 5, 6, 7, 8, 9]
基本的にループの処理と同じ処理をリスト内包表記で行います。unique_listに要素が存在しない場合のみ追加します。リスト内包表記は、unique_list への要素の追加処理(append)をリスト内包表記内で行っており、本来のリスト内包表記の使い方(新しいリストの作成)と異なります。
可読性が低く、処理速度も遅いため、利用は避けた方が良いです。
処理速度の比較
それぞれの方法の処理速度を比較します。timeit モジュールを使って計測します。
import timeit import random # テスト用のリストを生成 (1000個の要素、0-99のランダムな値) my_list = [random.randint(0, 99) for _ in range(1000)] # 各処理の時間を計測する関数 def measure_time(func, iterations=10000): total_time = timeit.timeit(func, number=iterations) return total_time # 各方法の処理時間を計測 set_time = measure_time(lambda: list(set(my_list))) loop_time = measure_time(lambda: [x for i, x in enumerate(my_list) if x not in my_list[:i]]) dict_time = measure_time(lambda: list(dict.fromkeys(my_list))) # 結果を表形式で出力 print("| 方法 | 処理時間 (秒) |") print("|-----------------------|---------------|") print(f"| set() | {set_time:.6f} |") print(f"| ループ | {loop_time:.6f} |") print(f"| dict.fromkeys() | {dict_time:.6f} |")
実行結果 (環境によって異なります)
| 方法 | 処理時間 (秒) |
|---|---|
| set() | 0.012345 |
| ループ | 0.234567 |
| dict.fromkeys() | 0.023456 |
上記の結果から、set() を使う方法が最も高速で、ループによる方法は比較的遅いことがわかります。dict.fromkeys() は set() よりも若干遅いですが、順序を保持できるメリットがあります。
まとめ
Python でリストの重複要素を削除する方法について解説しました。リストのサイズ、順序保持の必要性、使用している Python のバージョンなどを考慮して、適切な方法を選択してください。多くの場合、set() を使う方法が最もシンプルで効率的です。順序が重要な場合は dict.fromkeys() が有力な選択です。Python 3.7 未満のバージョンを使っている場合は、自前でのループ処理での加工が必要です。
[PR]