pandas.DataFrame, Seriesの重複した行を抽出・削除
pandas.DataFrame
, pandas.Series
から重複した要素を含む行を検出・抽出するにはduplicated()
、削除するにはdrop_duplicates()
を使う。
- pandas.DataFrame.duplicated — pandas 0.22.0 documentation
- pandas.DataFrame.drop_duplicates — pandas 0.22.0 documentation
また、重複した要素をもとに値を集約するgroupby()
についても最後に簡単に触れる。
以下の内容について説明する。
- 重複した行を抽出:
duplicated()
- 残す行を選択: 引数
keep
- 重複を判定する列を指定: 引数
subset
- 重複した行の数をカウント
- 残す行を選択: 引数
- 重複した行を削除:
drop_duplicates()
- 引数
keep
,subset
- 引数
inplace
- 引数
- 重複した要素をもとに集約:
groupby()
例として以下のデータを使用する。重複したデータを追加している。
import pandas as pd
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
df = df.append({'name': 'Dave', 'age': 68, 'state': 'TX', 'point': 70}, ignore_index=True)
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
サンプルのcsvファイルはコチラ。
例はpandas.DataFrame
だが、pandas.Series
でも同様。
重複した行を抽出: duplicated()
duplicated()
メソッドを使うと、重複した行をTrue
としたブール値のpandas.Series
が得られる。デフォルトでは、すべての列の要素が一致しているときに重複行であるとみなされる。
print(df.duplicated())
# 0 False
# 1 False
# 2 False
# 3 False
# 4 False
# 5 False
# 6 True
# dtype: bool
得られたpandas.Series
を使って、元のpandas.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(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
上述のように、デフォルトではすべての列の要素が一致しているときに重複行であるとみなされる。
引数subset
で判定する列を指定することができる。
print(df.duplicated(subset='state'))
# 0 False
# 1 False
# 2 True
# 3 False
# 4 True
# 5 True
# 6 True
# dtype: bool
リストで複数の列を指定することも可能。指定したすべての列の要素が一致しているときに重複したとみなされる。
print(df.duplicated(subset=['state', 'point']))
# 0 False
# 1 False
# 2 False
# 3 False
# 4 False
# 5 False
# 6 True
# dtype: bool
重複した行の数をカウント
duplicated()
で得られたpandas.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
# dtype: int64
当然ながら、引数keep
などによって結果が変わるので注意。目的によって使い分ける。
print(df.duplicated(keep=False).value_counts())
# False 5
# True 2
# dtype: int64
重複した行を削除: drop_duplicates()
duplicated()
と論理否定演算子~
を使って、重複した行を削除したDataFrame
を取得できる。
print(df[~df.duplicated()])
# 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
同様の処理を行う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, subset
drop_duplicates()
でも、duplicated()
と同様に引数keep
, subset
を設定できる。
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
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
引数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()
を使う。
平均や合計、最大、最小などのメソッドが用意されている。平均(mean()
)は数値列にのみ適用される。
以下の例では、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())
# 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': 'mean'}))
# name age point
# state
# CA Bob,Charlie,Ellen 28 83.333333
# NY Alice,Frank 27 60.500000
# TX Dave 68 70.000000
print(df.groupby('state').agg(
{'name': list,
'age': 'mean',
'point': 'mean'}))
# name age point
# state
# CA [Bob, Charlie, Ellen] 28 83.333333
# NY [Alice, Frank] 27 60.500000
# TX [Dave] 68 70.000000
groupby()
の詳細は以下の記事を参照。
連結には無名関数(ラムダ式)で文字列のメソッドjoin()
を適用している。
リスト化は組み込み関数list()
を適用。リストをpandas.DataFrame
の要素とした場合の処理については以下の記事を参照。
- 関連記事: pandasの要素としてリストを格納し処理