pandas.DataFrame, Seriesの重複した行を抽出・削除

Modified: | Tags: Python, pandas

pandasでDataFrameSeriesから重複した要素を含む行を検出・抽出するにはduplicated()、削除するにはdrop_duplicates()を使う。

重複した要素をもとに値を集約するgroupby()についても最後に簡単に触れる。

本記事のサンプルコードのpandasのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。例として以下のデータを使用する。重複した行を追加している。

import pandas as pd

print(pd.__version__)
# 2.1.4

df = pd.read_csv('data/src/sample_pandas_normal.csv')
df.loc[6] = ['Dave', 68, 'TX', 70]
print(df)
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57
# 6     Dave   68    TX     70

例はDataFrameだが、Seriesでも使い方は同じ。

重複した行を抽出: duplicated()

DataFrameSeriesから重複した要素を含む行を検出・抽出するにはduplicated()メソッドを使う。

基本的な使い方

duplicated()メソッドは重複した行をTrueとするブール値のSeriesを返す。デフォルトでは、すべての列の要素が一致していると重複行と判定される。

print(df.duplicated())
# 0    False
# 1    False
# 2    False
# 3    False
# 4    False
# 5    False
# 6     True
# dtype: bool

得られたSeriesを使って、元のDataFrameから重複した行のデータを抽出できる。

print(df[df.duplicated()])
#    name  age state  point
# 6  Dave   68    TX     70

残す行を選択: 引数keep

デフォルトは引数keep='first'で、重複した最初の行はFalseになる。最初(first)の行がkeepされるイメージ。

keep='last'とすると、重複した最後の行がFalseになる。最後(last)の行がkeepされるイメージ。

print(df.duplicated())
# 0    False
# 1    False
# 2    False
# 3    False
# 4    False
# 5    False
# 6     True
# dtype: bool

print(df.duplicated(keep='last'))
# 0    False
# 1    False
# 2    False
# 3     True
# 4    False
# 5    False
# 6    False
# dtype: bool

keep=Falseとすると、重複した行すべてがTrueとなる。

print(df.duplicated(keep=False))
# 0    False
# 1    False
# 2    False
# 3     True
# 4    False
# 5    False
# 6     True
# dtype: bool

重複を判定する列を指定: 引数subset

デフォルトではすべての列の要素が一致している行が重複行と判定されTrueとなる。

引数subsetで判定する列を指定できる。

print(df.duplicated(subset='state'))
# 0    False
# 1    False
# 2     True
# 3    False
# 4     True
# 5     True
# 6     True
# dtype: bool

リストで複数の列を指定することも可能。指定したすべての列の要素が一致している行がTrueとなる。

print(df.duplicated(subset=['state', 'point']))
# 0    False
# 1    False
# 2    False
# 3    False
# 4    False
# 5    False
# 6     True
# dtype: bool

重複した行の数をカウント

duplicated()で得られたSeriesTrueをカウントすると、重複した行の数が確認できる。Trueの数はsum()メソッドでカウントできる。

print(df.duplicated().sum())
# 1

Falseの数(重複していない行の数)をカウントしたい場合は、否定~で反転してからsum()でカウントすればよい。

print(~df.duplicated())
# 0     True
# 1     True
# 2     True
# 3     True
# 4     True
# 5     True
# 6    False
# dtype: bool

print((~df.duplicated()).sum())
# 6

value_counts()でまとめてカウントすることもできる。

print(df.duplicated().value_counts())
# False    6
# True     1
# Name: count, dtype: int64

引数keepによって結果が変わるので注意。目的によって使い分ける。

print(df.duplicated(keep=False).value_counts())
# False    5
# True     2
# Name: count, dtype: int64

重複した行を削除: drop_duplicates()

DataFrameSeriesから重複した要素を含む行を削除するにはdrop_duplicates()メソッドを使う。

基本的な使い方

デフォルトでは、すべての列の要素が一致している行が重複しているとみなされる。重複行の最初の行が残り、それ以外の行が削除される。

print(df.drop_duplicates())
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57

残す行を選択: 引数keep

duplicated()と同様に引数keepを指定できる。デフォルトは'first'で重複行の最初の行が残る。

引数keep'last'とすると最後の行が残り、Falseとするとすべての重複行が削除される。

print(df.drop_duplicates(keep='last'))
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57
# 6     Dave   68    TX     70

print(df.drop_duplicates(keep=False))
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57

重複を判定する列を指定: 引数subset

duplicated()と同様に引数subsetを指定できる。引数subsetに指定した列で重複が判定される。

print(df.drop_duplicates(subset='state'))
#     name  age state  point
# 0  Alice   24    NY     64
# 1    Bob   42    CA     92
# 3   Dave   68    TX     70

print(df.drop_duplicates(subset=['state', 'point']))
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57

インデックスを振り直し: 引数ignore_index

引数ignore_indexTrueとすると、行名indexが0始まりの連番に振り直される。

print(df.drop_duplicates(subset='state', keep='last'))
#     name  age state  point
# 4  Ellen   24    CA     88
# 5  Frank   30    NY     57
# 6   Dave   68    TX     70

print(df.drop_duplicates(subset='state', keep='last', ignore_index=True))
#     name  age state  point
# 0  Ellen   24    CA     88
# 1  Frank   30    NY     57
# 2   Dave   68    TX     70

元のオブジェクトを変更: 引数inplace

デフォルトでは重複した行が削除された新たなDataFrameが返されるが、引数inplaceTrueとすると、元のDataFrameから重複した行が削除される。

df.drop_duplicates(subset='state', keep='last', inplace=True)
print(df)
#     name  age state  point
# 4  Ellen   24    CA     88
# 5  Frank   30    NY     57
# 6   Dave   68    TX     70

重複した要素をもとに集約: groupby()

重複した要素をもとに値を集約するにはgroupby()を使う。

以下の例では、state列の重複する要素ごとに数値列agepointの値の平均を算出している。

df = pd.read_csv('data/src/sample_pandas_normal.csv')
print(df)
#       name  age state  point
# 0    Alice   24    NY     64
# 1      Bob   42    CA     92
# 2  Charlie   18    CA     70
# 3     Dave   68    TX     70
# 4    Ellen   24    CA     88
# 5    Frank   30    NY     57

print(df.groupby('state').mean(numeric_only=True))
#         age      point
# state                 
# CA     28.0  83.333333
# NY     27.0  60.500000
# TX     68.0  70.000000

文字列を連結したりリスト化したりすることも可能。

print(
    df.groupby('state').agg(
        {'name': lambda x: ','.join(x), 'age': 'mean', 'point': 'sum'}
    )
)
#                     name   age  point
# state                                
# CA     Bob,Charlie,Ellen  28.0    250
# NY           Alice,Frank  27.0    121
# TX                  Dave  68.0     70

print(df.groupby('state').agg({'name': list, 'age': 'mean', 'point': 'sum'}))
#                         name   age  point
# state                                    
# CA     [Bob, Charlie, Ellen]  28.0    250
# NY            [Alice, Frank]  27.0    121
# TX                    [Dave]  68.0     70

groupby()の詳細は以下の記事を参照。

連結には無名関数(ラムダ式)で文字列のメソッドjoin()を適用している。

リスト化は組み込み関数list()を適用。リストをDataFrameの要素とした場合の処理については以下の記事を参照。

関連カテゴリー

関連記事