pandas.DataFrame, Seriesの重複した行を抽出・削除
pandasでDataFrameやSeriesから重複した要素を含む行を検出・抽出するには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()
DataFrameやSeriesから重複した要素を含む行を検出・抽出するにはduplicated()メソッドを使う。
- pandas.DataFrame.duplicated — pandas 2.1.4 documentation
- pandas.Series.duplicated — pandas 2.1.4 documentation
基本的な使い方
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()で得られたSeriesのTrueをカウントすると、重複した行の数が確認できる。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()
DataFrameやSeriesから重複した要素を含む行を削除するにはdrop_duplicates()メソッドを使う。
- pandas.DataFrame.drop_duplicates — pandas 2.1.4 documentation
- pandas.Series.drop_duplicates — pandas 2.1.4 documentation
基本的な使い方
デフォルトでは、すべての列の要素が一致している行が重複しているとみなされる。重複行の最初の行が残り、それ以外の行が削除される。
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_indexをTrueとすると、行名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が返されるが、引数inplaceをTrueとすると、元の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列の重複する要素ごとに数値列ageとpointの値の平均を算出している。
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の要素とした場合の処理については以下の記事を参照。
- 関連記事: pandasの要素としてリストを格納し処理