今回は、PythonでListから重複する要素を削除する方法について説明します。
この問題に取り組む方法は複数ありますが、そのうちのいくつかを紹介します。
リストから重複する要素を削除する方法 – Python
1. 反復処理の利用
PythonでListから重複する要素を削除するには、手動でListを繰り返し、要素がなければ新しいListに追加することができます。
そうでない場合は、その要素をスキップします。
以下にそのコードを示します。
a = [ 2 , 3 , 3 , 2 , 5 , 4 , 4 , 6 ]
b = []
for i in a:
# Add to the new list
# only if not present
if i not in b:
b.append(i)
print (b)
|
結果は以下の通りです。
[ 2 , 3 , 5 , 4 , 6 ]
|
同じコードをリスト理解を使って書くと、コードの行数を減らすことができますが、基本的には前と同じです。
a = [ 2 3 , 4 , 2 , 5 , 4 , 4 , 6 ]
b = []
[b.append(i) for i in a if i not in b]
print (b)
|
この方法の問題点は、新しいリストの各要素に対して比較が行われ、その間に元のリストが繰り返し処理されるため、少し時間がかかることです。
これは計算コストが高く、この問題に対処するための他の方法があります。
この方法は、リストのサイズがそれほど大きくない場合にのみ使用する必要があります。
そうでなければ、他のメソッドを参照してください。
2. set()の使用
Python でリストから重複する要素を取り除く簡単で高速な方法は、Python の組み込みメソッド set()
を使ってリストの要素を一意のセットに変換し、その後、重複する要素をすべて取り除いたリストに変換することです。
first_list = [ 1 , 2 , 2 , 3 , 3 , 3 , 4 , 5 , 5 , 6 ]
# Convert to a set first set_list = set (first_list)
# Now convert the set into a List print ( list (set_list))
second_list = [ 2 , 3 , 3 , 2 , 5 , 4 , 4 , 6 ]
# Does the same as above, in a single line print ( list ( set (second_list)))
|
結果は以下の通りです。
[ 1 , 2 , 3 , 4 , 5 , 6 ]
[ 2 , 3 , 4 , 5 , 6 ]
|
この方法の問題点は、順番が決まっていないセットから新しいリストを作成するため、2番目のリストの場合と同様に元のリストの順番が維持されないことです。
したがって、相対的な順番を維持したい場合は、この方法を避けなければなりません。
3. 順序を守る OrderedDictの使用
PythonでListから重複する要素を削除する際に、順序を保持したい場合は、collectionsモジュールのOrderedDictクラスを使用します。
具体的には、OrderedDict.fromkeys(list)
を使って、順序を保ちつつ重複要素を取り除いた辞書を取得することができます。
そして、list()
メソッドを使って簡単にリストに変換することができます。
from collections import OrderedDict
a = [ 2 , 3 , 3 , 2 , 5 , 4 , 4 , 6 ]
b = list (OrderedDict.fromkeys(a))
print (b)
|
結果は以下の通りです。
[ 2 , 3 , 5 , 4 , 6 ]
|
NOTE: Python 3.7以降であれば、ビルトインの dict.fromkeys(list)
を代わりに使用することができます。
これは順序も保証します。
見てわかるように、順序は確かに維持されているので、最初のメソッドと同じ出力が得られます。
しかし、この方がずっと速いのです! これは、この問題に対する推奨される解決策です。
しかし、説明のために、PythonでListから重複する要素を削除するための他のいくつかのアプローチを紹介します。
4. list.count()の使用法
list.count()メソッドは、値が何回出現したかを返します。
これをremove()` メソッドと一緒に使うことで、重複した要素を排除することができます。
しかし、この場合も順序は保持されません。
このメソッドは入力リストをその場で変更するので、変更内容自体がそこに反映されることに注意してください。
a = [ 0 , 1 , 2 , 3 , 4 , 1 , 2 , 3 , 5 ]
for i in a:
if a.count(i) > 1 :
a.remove(i)
print (a)
|
結果は以下の通りです。
[ 0 , 4 , 1 , 2 , 3 , 5 ]
|
すべてうまくいっているように見えますね?
しかし、上のコードにはちょっとした問題があります。
forループを使ってリストを反復処理し、同時に要素を削除しているとき、イテレータは1つの要素をスキップしてしまうのです。
そのため、コードの出力はリストの要素に依存し、運が良ければこの問題は発生しません。
このシナリオを簡単なコードで理解しましょう。
a = [ 1 , 2 , 3 , 2 , 5 ]
for i in a:
if a.count(i) > 1 :
a.remove(i)
print (a, i)
print (a)
|
結果は以下の通りです。
[ 1 , 2 , 3 , 2 , 5 ] 1
[ 1 , 3 , 2 , 5 ] 2
[ 1 , 3 , 2 , 5 ] 2
[ 1 , 3 , 2 , 5 ] 5
[ 1 , 3 , 2 , 5 ]
|
forループが4回だけ実行され、remove()呼び出しの次の要素である3がスキップされているのがわかります。
入力リストを [1, 1, 1, 1] とすると、最終的なリストは [1, 1] となります。
では、何か回避策はないのでしょうか?
もちろん、回避策はあります。
リストのコピーをforループで使うが、メインのリストから要素を削除します。
リストのコピーを作成する簡単な方法は、スライスすることです。
以下は、すべてのケースでうまく動作する更新コードです。
a = [ 1 , 1 , 1 , 1 ]
for i in a[:]: # using list copy for iteration
if a.count(i) > 1 :
a.remove(i)
print (a, i)
print (a)
|
結果は以下の通りです。
[ 1 , 1 , 1 ] 1
[ 1 , 1 ] 1
[ 1 ] 1
[ 1 ] 1
[ 1 ]
|
5. sort()の使用
sort()メソッドを使うと、2.で得た集合をソートすることができます。
これは、順序を維持したまま重複を削除することもできますが、dict.fromkeys()` のアプローチよりも時間がかかります。
a = [ 0 , 1 , 2 , 3 , 4 , 1 , 2 , 3 , 5 ]
b = list ( set (a))
b.sort(key = a.index)
print (b)
|
結果は以下の通りです。
[ 0 , 1 , 2 , 3 , 4 , 5 ]
|
6. Pandasモジュールの利用
Pandasモジュールを使っている場合、pandas.drop_duplicates()
メソッドを使って重複を削除し、順序を保ったままListに変換することが可能です。
import pandas as pd
a = [ 0 , 1 , 2 , 3 , 4 , 1 , 2 , 3 , 5 ]
pd.Series(a).drop_duplicates().tolist() |
結果は以下の通りです。
[ 0 , 1 , 2 , 3 , 4 , 5 ]
|