パンダの重複行のリストを取得するにはどうすればよいですか?

Aug 21 2020

358個のサンプルに10万を超える変数(行)があるビッグデータフレームがあります。

すべてのサンプルでどの変数が同一(重複)であるかを知りたいです。

サンプルデータフレームは次のようなものです。

         Sample1 Sample2 Sample3 Sample4 Sample5
1000084  0.0     0.0     0.0     0.0     0.0    
1000092  0.0     0.0     0.0     0.0     0.0
1000096  0.0     0.0     1.0     0.0     0.0
1000110  0.0     0.0     1.0     0.0     0.0
1000116  0.0     0.0     0.0     0.0     0.0

必要な結果は次のようになります:または同一の行のリストのリスト

 {1000084:[1000092, 1000116], 1000096:[1000110]}

パンダから複製方法を試しましたが、一意のアイテムまたは一意のアイテムと最初または最後の複製のみが残ります。

私はこのコードで試しましたが、時間がかかっています:

duplicated_index = set()
duplicates = {}

for i, pos in enumerate(df.index, 0):
    #check if the row has marked as duplicate, if so, ignore it
    if i in duplicated_index:
        continue
    for j in range(i+1, df.shape[0]):
        if all(df.iloc[i] == df.iloc[j]):
             duplicated_index.add(j)
             tmp = duplicates.setdefault(pos, [])
             duplicates[pos].append(df.iloc[j].name)

このリストを取得し、どの行が他の行と同一であるかを識別するためのより適切な方法はありますか?

回答

3 wwii Aug 21 2020 at 21:43

すべての列でグループ化します。複数のアイテムを持つグループを見つけて、それらをリストに入れます。forループを使用します。

>>> gb = df.groupby(df.columns.to_list())
>>> d = {}
>>> for a,b in gb:
...     if len(b) > 1:
...         d[b.index[0]] = b.index[1:].to_list()

        
>>> d
{1000084: [1000092, 1000116], 1000096: [1000110]}
>>>

上記と同じgroupbyを使用して、グループのインデックスを返す関数を記述し、aggregateメソッドを使用して辞書を作成します。

def f(thing):
    return thing.index.to_list()

>>> {key:val for key,*val in gb.aggregate(f) if val}
{1000084: [1000092, 1000116], 1000096: [1000110]}

この実行時間は、列行の数(アイテムの数)に比例して変化するように見えます。


これがテスト用の大きなDataFrameです。残念ながら、重複する行を生成することは望んでいません-おそらくそれはgroupbyにとって最悪のケースであり、それから繰り返しますか?

import itertools,string
import numpy as np
nrows,ncols = 100000,300

a = np.random.randint(1,3,(nrows,ncols))
# or using the new random stuff
#from numpy.random import default_rng
#rng = default_rng()
#a = rng.integers(1,3,(nrows,ncols))

index = np.arange(1000000,1000000+nrows,dtype=np.int64)
cols = [''.join(thing) for thing in itertools.combinations(string.ascii_letters,3)]
df2 = pd.DataFrame(data=a,index=index,columns=cols[:ncols])
2 BENY Aug 21 2020 at 21:37

reset_index次にgroupby追加しますagg

l = df.reset_index().groupby(list(df))['index'].agg(list).tolist()
Out[291]: [[1000084, 1000092, 1000116], [1000096, 1000110]]
1 gunsodo Aug 21 2020 at 21:29

pandasduplicated()重複したすべての行を返す独自の関数があります。

duplicated_rows = df[df.duplicated(subset=['col1', 'col2', 'col3'], keep=False)]

ドキュメントによると、

  • subset重複をチェックする必要がある選択した列のリストにすることができます。デフォルトでは、すべての列を使用します。
  • keepすべての発生Falseを保持するように設定されています

結果をリストのリストとして表示したい場合は、上記でドラフトしたコードを少し変更するだけで問題が解決する可能性があります。

1 anky Aug 21 2020 at 22:33

pd.factorizeおよびで別のアプローチを追加するIndex.groupby

idx = pd.factorize(list(map(tuple,df.to_numpy().tolist())))[0]

d = {g[0]: [*g[1:]] for _,g in df.index.groupby(idx).items() if len(g)>1}

{1000084: [1000092, 1000116], 1000096: [1000110]}

またはを使用しますdf.to_records()が、前の方法よりも遅くなる可能性があります。

idx = pd.factorize(df.to_records(index=False))[0]
d = {g[0]: [*g[1:]] for _,g in df.index.groupby(idx).items() if len(g)>1}