note.nkmk.me

Pythonでリスト(配列)から重複した要素を削除・抽出

Date: 2018-03-25 / tags: Python, リスト

Pythonで、リスト(配列)から、

  • 重複した要素を削除(一意な要素・ユニークな要素のみを抽出)
  • 重複した要素を抽出

して、新たなリストを生成する方法について説明する。

それぞれ、

  • 元のリストの順序を保持しない方法
  • 元のリストの順序を保持する方法

および、

  • 二次元配列(リストのリスト)の場合

についてサンプルコードとともに説明する。

なお、リストではなくタプルの場合も同様の考え方で実現可能。

リストやタプルが重複した要素を持っているかどうかを判定したい場合は以下の記事を参照。

また、一つのリストではなく複数のリスト間で共通する要素や共通しない要素を抽出したい場合は以下の記事を参照。

なお、リストは異なる型のデータを格納可能で、厳密には配列とは異なる。メモリサイズやメモリアドレスを必要とするような処理や大規模なデータの数値計算処理などで配列を扱いたい場合はarray(標準ライブラリ)やNumPyを使う。

スポンサーリンク

重複した要素を削除し、新たなリストを生成

元のリストの順序を保持しない: set()

元のリストの順序を保持する必要がない場合は、集合型setのコンストラクタset()を使う。

set型は重複した要素をもたないデータ型で、コンストラクタset()にリストを渡すと、重複する値は無視されて一意な値のみが要素となるset型のオブジェクトを返す。

これをlist()で再びリストにすればOK。タプルの場合はtuple()でタプルにすればよい。

l = [3, 3, 2, 1, 5, 1, 4, 2, 3]

l_unique = list(set(l))

print(l_unique)
# [1, 2, 3, 4, 5]

集合型setについては以下の記事を参照。

元のリストの順序を保持する: dict.fromkeys(), sorted()

元のリストの順番が重要な場合は、辞書型dictのクラスメソッドfromkeys()、または組み込み関数sorted()を使う。

dict.fromkey()は引数に指定したシーケンス型(リストやタプルなど)をキーとした新たな辞書オブジェクトを生成する。第二引数を省略した場合、値はNone

辞書のキーは重複した要素をもたないので、set()と同じく重複する値は無視される。dict.fromkey()では引数のシーケンスの順序が保持される(Python3.6以降)。

さらに辞書オブジェクトをlist()の引数に渡すと辞書のキーを要素とするリストが得られるので、これを利用する。

print(dict.fromkeys(l))
# {3: None, 2: None, 1: None, 5: None, 4: None}

l_unique_order = list(dict.fromkeys(l))

print(l_unique_order)
# [3, 2, 1, 5, 4]

なお、dict.fromkey()で引数のシーケンスの順序が保持されるのは、Pythonのバージョンが3.6から。

それより前のバージョンでは組み込み関数sorted()を使う。

要素をソートしたリストを返すsortedの引数keyにリスト・タプルのメソッドindex()を指定する。

index()は値のインデックス(リスト中の何番目の要素か)を返すメソッドで、sorted()keyに指定することで、元のリストの順番を基準に並べ替えられる。

引数keyには呼び出し可能(コーラブル)なオブジェクトとして指定するので()は書かない。

l_unique_order = sorted(set(l), key=l.index)

print(l_unique_order)
# [3, 2, 1, 5, 4]

二次元配列(リストのリスト)の場合

二次元配列(リストのリスト)の場合、set()dict.fromkey()を使う方法はエラーTypeErrorになる。

l_2d = [[0], [2], [2], [1], [0], [0]]

print(l_2d)
# [[0], [2], [2], [1], [0], [0]]

# l_2d_unique = list(set(l_2d))
# TypeError: unhashable type: 'list'

# l_2d_unique_order = dict.fromkeys(l_2d)
# TypeError: unhashable type: 'list'

これは、リストなどのミュータブル(更新可能)なオブジェクトはset型の要素やdict型のキーにできないから。

以下のような関数を定義する。

元のリストの順番は保持され、一次元のリストやタプルに対しても動作する。

def get_unique_list(seq):
    seen = []
    return [x for x in seq if x not in seen and not seen.append(x)]

l_2d_unique = get_unique_list(l_2d)

print(l_2d_unique)
# [[0], [2], [1]]

l_unique = get_unique_list(l)

print(l_unique)
# [3, 2, 1, 5, 4]

リスト内包表記を使っている。

ここでは、

  • and演算子のショートサーキット(短絡評価)でX and YXFalseであればYは評価されない(実行されない)こと
  • append()メソッドがNoneを返すこと

を利用している。

元のリストseqの要素がseenに存在しない場合(x not in seenTrue)、seenにその要素を追加(seen.append(x))しnot seen.append(x)Trueとなるため、リスト内包表記の条件式がTrueとなり、最終的に生成されるリストの要素として追加される。

重複した要素を抽出し、新たなリストを生成

元のリストの順序を保持しない

元のリストから重複している要素のみを抽出する場合は、リスト、タプルのメソッドcount()を使う。

count()はリスト・タプルに引数に指定した値の要素が何個あるかを返すメソッド。

リスト内包表記で、set()で得られる一意な要素の中から元のリストに2個以上存在する(=重複している)要素を抽出する。

l = [3, 3, 2, 1, 5, 1, 4, 2, 3]

l_duplicate = [x for x in set(l) if l.count(x) > 1]

print(l_duplicate)
# [1, 2, 3]

リストの要素数のカウントについては以下の記事を参照。

元のリストの順序を保持する

重複した要素を削除する場合と同様に、dict.fromkey()を使うか(Python3.6以降)、sorted() でソートする。

l_duplicate_order = [x for x in dict.fromkeys(l) if l.count(x) > 1]

print(l_duplicate_order)
# [3, 2, 1]

l_duplicate_order = sorted([x for x in set(l) if l.count(x) > 1], key=l.index)

print(l_duplicate_order)
# [3, 2, 1]

二次元配列(リストのリスト)の場合

元のリストの順番を保持しない場合と保持する場合で、それぞれ以下のような関数が考えられる。一次元のリストやタプルに対しても動作する。

l_2d = [[0], [2], [2], [1], [0], [0]]

print(l_2d)
# [[0], [2], [2], [1], [0], [0]]

def get_duplicate_list(seq):
    seen = []
    return [x for x in seq if not seen.append(x) and seen.count(x) == 2]

def get_duplicate_list_order(seq):
    seen = []
    return [x for x in seq if seq.count(x) > 1 and not seen.append(x) and seen.count(x) == 1]

l_2d_duplicate = get_duplicate_list(l_2d)

print(l_2d_duplicate)
# [[2], [0]]

l_2d_duplicate_order = get_duplicate_list_order(l_2d)

print(l_2d_duplicate_order)
# [[0], [2]]

l_duplicate = get_duplicate_list(l)

print(l_duplicate)
# [3, 1, 2]

l_duplicate_order = get_duplicate_list_order(l)

print(l_duplicate_order)
# [3, 2, 1]

もっとスマートな方法があるかも知れない。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事