pandasのduplicated()とdrop_duplicates()の使い方|重複行の抽出・削除とkeep・subsetの違い

pandasで重複行を調べるにはdf.duplicated()、消すにはdf.drop_duplicates()を使います。どちらもsubsetで判定に使う列を、keepで「最初・最後・すべて」のどれを重複扱いにするかを指定します。

この記事では、duplicated()で重複を見つける方法、keep=Falseで重複グループを丸ごと確認する方法、drop_duplicates()で削除する方法を、実行結果つきで説明します。コードはpandas 3.0.6(2026年10月15日確認の最新版)で動かしています。

duplicated()とdrop_duplicates()の違い

2つのメソッドは判定のルールが同じで、返すものだけが違います。

メソッド 返すもの 主な用途
duplicated() 行ごとのTrue/False(bool型のSeries) 重複している行を探す・数える・抽出する
drop_duplicates() 重複行を取り除いたDataFrame 重複を消して1件ずつにする

引数は共通で、公式リファレンス(DataFrame.duplicated)とDataFrame.drop_duplicatesに書かれているとおり次の意味です。

引数 既定値 意味
subset すべての列 重複の判定に使う列(1列なら文字列、複数ならリスト)
keep 'first' 'first':最初の1行を残す(重複扱いしない)/'last':最後の1行を残す/False:重複している行をすべて対象にする
ignore_index False (drop_duplicatesのみ)結果のインデックスを0から振り直す
inplace False (drop_duplicatesのみ)元のDataFrameを書き換える

duplicated()で重複している行を調べる

例として、会員の購入記録を使います。0行目と2行目は全列が同じ値です。

import pandas as pd

df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102, 104],
    "名前": ["佐藤", "鈴木", "佐藤", "高橋", "鈴木", "佐藤"],
    "購入日": ["10/01", "10/01", "10/01", "10/02", "10/05", "10/05"],
})
print(df)
print(df.duplicated())
   会員ID  名前    購入日
0   101  佐藤  10/01
1   102  鈴木  10/01
2   101  佐藤  10/01
3   103  高橋  10/02
4   102  鈴木  10/05
5   104  佐藤  10/05
0    False
1    False
2     True
3    False
4    False
5    False
dtype: bool

既定のkeep='first'では、同じ内容の行のうち最初の行(0行目)はFalse、2回目以降(2行目)がTrueになります。全列が一致している行だけが重複と判定されるため、会員IDが同じでも購入日が違う4行目はFalseです。

重複行だけを抽出する・件数を数える

結果はbool型のSeriesなので、そのままdf[...]に渡せば重複行だけを取り出せます。sum()するとTrueの数、つまり重複行の件数になります。

import pandas as pd
df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102, 104],
    "名前": ["佐藤", "鈴木", "佐藤", "高橋", "鈴木", "佐藤"],
    "購入日": ["10/01", "10/01", "10/01", "10/02", "10/05", "10/05"],
})
print(df[df.duplicated()])
print("重複行の数:", df.duplicated().sum())
   会員ID  名前    購入日
2   101  佐藤  10/01
重複行の数: 1

keepの3つの値を並べて比べる

subset="会員ID"として会員IDだけで判定し、keepの違いを1つの表にしました。

import pandas as pd
df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102, 104],
    "名前": ["佐藤", "鈴木", "佐藤", "高橋", "鈴木", "佐藤"],
    "購入日": ["10/01", "10/01", "10/01", "10/02", "10/05", "10/05"],
})
result = pd.DataFrame({
    "first": df.duplicated(subset="会員ID"),
    "last": df.duplicated(subset="会員ID", keep="last"),
    "False": df.duplicated(subset="会員ID", keep=False),
})
print(result)
   first   last  False
0  False   True   True
1  False   True   True
2   True  False   True
3  False  False  False
4   True  False   True
5  False  False  False
  • first:会員101・102の2回目(2行目・4行目)がTrue
  • last:反対に1回目(0行目・1行目)がTrue。最後の行を「本物」とみなす
  • False:重複している行はすべてTrue。一度しか出てこない103・104だけがFalse

keep=Falseで重複グループを丸ごと確認する

重複を消す前に「どの行とどの行がかぶっているのか」を目で確認したいときは、keep=Falseで抽出して並べ替えるのが便利です。df.duplicated(keep=False)で検索してくる人が多いのも、この使い方のためだと思われます。

import pandas as pd
df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102, 104],
    "名前": ["佐藤", "鈴木", "佐藤", "高橋", "鈴木", "佐藤"],
    "購入日": ["10/01", "10/01", "10/01", "10/02", "10/05", "10/05"],
})
# 会員IDが重複している行を、最初の出現も含めてすべて表示
print(df[df.duplicated(subset="会員ID", keep=False)].sort_values("会員ID"))
   会員ID  名前    購入日
0   101  佐藤  10/01
2   101  佐藤  10/01
1   102  鈴木  10/01
4   102  鈴木  10/05

会員102は購入日が違うので、同じ人が2回買っただけかもしれません。重複を消すかどうかは、こうして中身を見てから決めます。

drop_duplicates()で重複行を削除する

drop_duplicates()は、duplicated()がTrueになる行を取り除いたDataFrameを返します。keepの意味もそのままです。

import pandas as pd
df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102, 104],
    "名前": ["佐藤", "鈴木", "佐藤", "高橋", "鈴木", "佐藤"],
    "購入日": ["10/01", "10/01", "10/01", "10/02", "10/05", "10/05"],
})
print(df.drop_duplicates())
print(df.drop_duplicates(subset="会員ID", keep="last"))
print(df.drop_duplicates(subset="会員ID", keep=False))
   会員ID  名前    購入日
0   101  佐藤  10/01
1   102  鈴木  10/01
3   103  高橋  10/02
4   102  鈴木  10/05
5   104  佐藤  10/05
   会員ID  名前    購入日
2   101  佐藤  10/01
3   103  高橋  10/02
4   102  鈴木  10/05
5   104  佐藤  10/05
   会員ID  名前    購入日
3   103  高橋  10/02
5   104  佐藤  10/05

1つ目は全列が一致した2行目だけが消えています。2つ目は会員IDごとに最後の行を残し、3つ目(keep=False)は会員IDが重複している行をすべて消して、一度しか出てこない会員だけを残しています。

複数の列で判定し、インデックスを振り直す

subsetにリストを渡すと「名前と購入日の組み合わせ」のように複数列で判定できます。削除するとインデックスが飛び飛びになるので、ignore_index=Trueで0から振り直しておくと後の処理が楽です。

import pandas as pd
df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102, 104],
    "名前": ["佐藤", "鈴木", "佐藤", "高橋", "鈴木", "佐藤"],
    "購入日": ["10/01", "10/01", "10/01", "10/02", "10/05", "10/05"],
})
print(df.drop_duplicates(subset=["名前", "購入日"], ignore_index=True))
   会員ID  名前    購入日
0   101  佐藤  10/01
1   102  鈴木  10/01
2   103  高橋  10/02
3   102  鈴木  10/05
4   104  佐藤  10/05

実務の定番:キーごとに最新の1行だけ残す

「会員ごとに最新の購入だけ残したい」という処理は、日付で並べ替えてからkeep='last'で消すと1行で書けます。日付を文字列のまま並べると「10/1」と「9/30」の順序がおかしくなるので、pd.to_datetime()で日付型にしておきます。

import pandas as pd
df = pd.DataFrame({
    "会員ID": [101, 102, 101, 103, 102],
    "購入日": pd.to_datetime(["2026-10-01", "2026-10-03", "2026-10-09", "2026-10-02", "2026-09-28"]),
})
# 会員ごとに「最新の購入日」の行だけ残す
latest = df.sort_values("購入日").drop_duplicates(subset="会員ID", keep="last")
print(latest.sort_values("会員ID"))
   会員ID        購入日
2   101 2026-10-09
1   102 2026-10-03
3   103 2026-10-02

Seriesの重複を扱う

Seriesにも同じduplicated()とdrop_duplicates()があります。値の種類だけ欲しいならunique()、出現回数も見たいならvalue_counts()が向いています。

import pandas as pd
s = pd.Series(["A", "B", "A", "C", "B"])
print(s.duplicated().tolist())
print(s.drop_duplicates().tolist())
print(s.unique())
print(s.value_counts())
[False, False, True, False, True]
['A', 'B', 'C']
<StringArray>
['A', 'B', 'C']
Length: 3, dtype: str
A    2
B    2
C    1
Name: count, dtype: int64

pandas 3.0からは、文字列の列が既定で専用のstr型になりました(pandas 3.0.0のリリースノート)。そのためunique()の結果はStringArrayと表示されます。リストで欲しいときはdrop_duplicates().tolist()のようにします。ユニーク数を数える方法は「pandas nunique()の使い方」で詳しく扱っています。

重複がうまく見つからない・消えないとき

空白や大文字・小文字の違いは別の値になる

duplicated()は値が完全に一致したときだけ重複とみなします。末尾の空白や全角・半角、大文字・小文字が違うと別の値です。判定用の列を整えてから調べます。

import pandas as pd
df = pd.DataFrame({
    "名前": ["佐藤", "佐藤 ", "さとう", "Sato", "sato"],
})
print(df.duplicated(keep=False).tolist())
key = df["名前"].str.strip().str.lower()
print(key.duplicated(keep=False).tolist())
[False, False, False, False, False]
[True, True, False, True, True]

「佐藤」と「佐藤 」、「Sato」と「sato」が重複として見つかるようになりました。ひらがなの「さとう」は別の値のままです。表記の揺れをどこまでそろえるかは、データの性質に合わせて決めてください。

リストが入った列はTypeErrorになる

公式リファレンスの注記にあるとおり、subsetの列はハッシュ可能な型である必要があります。セルにリストが入っているとエラーになるので、タプルに変換した列で判定します。

import pandas as pd
df = pd.DataFrame({"会員ID": [101, 102], "タグ": [["a", "b"], ["a", "b"]]})
try:
    df.drop_duplicates()
except TypeError as e:
    print("TypeError:", e)
df["タグ_key"] = df["タグ"].apply(tuple)
print(df.drop_duplicates(subset="タグ_key"))
TypeError: unhashable type: 'list'
   会員ID      タグ  タグ_key
0   101  [a, b]  (a, b)

NaN同士は同じ値として扱われる

比較演算ではNaN同士は等しくなりませんが、duplicated()ではNaN同士は一致とみなされます。欠損値の行がまとめて消えるのを防ぎたいときは、先に欠損値を処理しておきます。

import pandas as pd
import numpy as np
df = pd.DataFrame({"会員ID": [101, np.nan, np.nan], "点数": [80, 70, 70]})
print(df.duplicated().tolist())
[False, False, True]

結果を代入しないと元のDataFrameは変わらない

drop_duplicates()は新しいDataFrameを返すだけです。inplace=Trueにすると元が書き換わる代わりに戻り値はNoneになるので、df = df.drop_duplicates(inplace=True)と書くとdfがNoneになってしまいます。通常はdf = df.drop_duplicates()と代入する書き方がわかりやすいです。

import pandas as pd
df = pd.DataFrame({"会員ID": [101, 102, 101], "名前": ["佐藤", "鈴木", "佐藤"]})
df2 = df.drop_duplicates()
print(len(df), len(df2))
r = df.drop_duplicates(inplace=True)
print(r, len(df))
3 2
None 2

よくある質問

Q. duplicated()とdrop_duplicates()はどちらを使えばいいですか?

重複がどこにあるか確認したいときはduplicated()、確認したうえで消すときはdrop_duplicates()です。いきなり消さずに、duplicated(keep=False)で重複グループを一度見てから消すと事故が減ります。

Q. keep=Falseにするとどうなりますか?

duplicated()では重複しているすべての行がTrueになり、drop_duplicates()では重複している行がすべて削除されて、一度しか出てこない行だけが残ります。

Q. 特定の列だけで重複を判定するには?

subset引数に列名を渡します。1列ならsubset=”会員ID”、複数列ならsubset=[“名前”, “購入日”]のようにリストで指定します。

Q. Pythonのリストの重複を消したいときもpandasを使いますか?

リストだけならpandasは不要です。順番を保つならlist(dict.fromkeys(リスト))、順番が不要ならset()で消せます。

リストの重複の扱いは「Pythonでリストの重複を削除する方法」にまとめています。

まとめ

  • 重複を調べるのはduplicated()、消すのはdrop_duplicates()
  • subsetで判定する列、keepで残す行(first・last・False)を決める
  • 消す前にkeep=Falseで重複グループを確認する
  • 「キーごとに最新の1行」は並べ替え+keep='last'
  • 空白・大文字小文字の揺れ、リスト型の列、NaNの扱いに注意する

pandas|抽出・選択の関連記事

loc・ilocや条件指定で、必要な行・列だけを取り出す操作の記事です。

pandas全体の使い方は「pandasの使い方を初心者向けに解説」でまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら