概要(drop_duplicatesは「重複行を安全に取り除いて、件数や集計を正しくする」)
pandasのdrop_duplicatesは、DataFrameやSeriesから“重複したデータ”を削除するメソッドです。アンケートの二重回答や取り込みの重複行をそのまま集計すると、件数・平均・割合が歪みます。分析前に「どの列で重複判定するか」「どちらの行を残すか」を決めて、確実に重複を掃除するのが定石です。
基本の使い方(全列での重複判定と特定列での判定)
全列が同じ行をまとめて削除(最小例)
import pandas as pd
df = pd.DataFrame({
"id": [1, 1, 2, 2, 2],
"name": ["Taro", "Taro", "Hanako", "Hanako", "Hanako"]
})
out = df.drop_duplicates() # 全列が完全一致する行を削除
print(out)
# id name
# 0 1 Taro
# 2 2 Hanako
Python「行全体が完全一致するもの」だけを重複とみなします。最初の出現を残して、後の重複を落とすのがデフォルトです。
特定の列だけで重複判定(subset)
df = pd.DataFrame({
"id": [1, 1, 2, 2],
"name": ["Taro", "Taro", "Hanako", "Hanako"],
"age": [20, 21, 25, 25]
})
# nameだけで重複判定(年齢が違っても「同じ名前を1人」とみなす)
out = df.drop_duplicates(subset=["name"])
print(out)
# id name age
# 0 1 Taro 20
# 2 2 Hanako 25
Python「何を重複とみなすか」は列の選び方で決まります。業務ルールに合わせてsubsetを設計しましょう。
重要オプションの深掘り(keep・ignore_index・inplace)
keep(どちらの重複を残すか)
df = pd.DataFrame({"name": ["Taro", "Taro", "Taro"], "age": [20, 21, 22]})
df_first = df.drop_duplicates(subset=["name"], keep="first") # 最初を残す(デフォルト)
df_last = df.drop_duplicates(subset=["name"], keep="last") # 最後を残す
df_none = df.drop_duplicates(subset=["name"], keep=False) # すべて削除(完全排除)
print(df_first)
# name age
# 0 Taro 20
print(df_last)
# name age
# 2 Taro 22
print(df_none)
# 空(全て重複扱いで削除)
Pythonポイント: 「最新を残したいか、最初を残したいか、そもそも重複は全部消したいか」を明確に決めてkeepで表現します。
ignore_index(削除後に連番インデックスへ)
df = pd.DataFrame({"x": [1, 1, 2, 2, 3]})
out = df.drop_duplicates(ignore_index=True)
print(out)
# x
# 0 1
# 1 2
# 2 3
Python重複削除後はインデックスが飛び飛びになります。連番に振り直したい時はignore_index=Trueが便利です。
inplace(直接更新するか)
df.drop_duplicates(subset=["name"], keep="first", inplace=True) # dfを直接書き換え
Python注意: inplaceは副作用が見えにくく、テストやデバッグで混乱しがちです。基本は「代入で受ける」書き方(df = df.drop_duplicates(…))を推奨します。
重複の確認と前処理(duplicatedと併用、キー正規化)
どの行が重複かを確認(duplicated)
df = pd.DataFrame({"name": ["Taro", "Taro", "Hanako", "Hanako"], "age": [20, 20, 25, 26]})
mask = df.duplicated(subset=["name", "age"], keep="first")
dups = df[mask]
print(dups)
# 「同じname×ageで2回目以降」の行が抽出される
Python重複の可視化→削除の順で進めると、意図しない消しすぎを防げます。
キーの前処理(空白・大小・型の揃え)
df = pd.DataFrame({"email": [" Taro@Example.com ", "taro@example.com", None]})
df["email"] = df["email"].fillna("").str.strip().str.lower()
unique_emails = df.drop_duplicates(subset=["email"], keep="first")
Python重複判定の列は、空白除去・小文字化・型統一を先にやるのが鉄則です。揃えずに判定すると、同じものを別物として残してしまいます。
実践例(マスタ付与前の重複掃除・最新レコードの選別・複合キー)
マスタ付与前に重複ユーザーを一意化
users = pd.DataFrame({
"user_id": ["001", "001", "002"],
"name": ["Taro", "Taro", "Hanako"]
})
users = users.drop_duplicates(subset=["user_id"], keep="first")
Python外部データ結合(merge)前にキーの一意性を確保しておくと、意図しない多対多結合で行が爆発するのを防げます。
最新レコードだけを残す(keep=”last”)
history = pd.DataFrame({
"user_id": ["001", "001", "001"],
"status": ["new", "active", "suspended"],
"ts": ["2025-01-01", "2025-02-01", "2025-03-01"]
})
# 時系列で並べ、user_idごとに最後を残す
history = history.sort_values("ts")
latest = history.drop_duplicates(subset=["user_id"], keep="last")
print(latest)
Python「時系列で最後の状態だけ欲しい」場面は、sort→drop_duplicates(keep=”last”)の型がシンプルです。
複合キーで誤結合を防ぐ(store×dateなど)
sales = pd.DataFrame({
"store": ["A", "A", "A", "B"],
"date": ["2025-01-01", "2025-01-01", "2025-01-02", "2025-01-01"],
"qty": [10, 10, 12, 8]
})
clean = sales.drop_duplicates(subset=["store", "date"], keep="first")
Python単一列より「複合キー」を使うと、文脈に即した重複判定ができます。
つまずき対策(“行全体”と“列 subset”の違い・NaN扱い・順序)
誤解しやすいポイント
- 列を指定しないと、行全体が一致したものだけが重複扱い。 列の一部だけで重複判定したいならsubsetを必ず使います。
- NaNはNaN同士でも“同値”として重複判定される(DataFrameでは) ため、NaNを含む行の重複削除に注意が必要です。必要なら先にfillnaで正規化します。
- 残したい行の基準は順序に依存。 keep=”first”/”last”は現在の並び順で決まるため、意図がある順序(sort)に整えてからdrop_duplicatesします。
安全な進め方
- 確認→削除: duplicatedで重複行を確認してからdrop_duplicates。
- 正規化→判定: キー列はstrip/lower/astypeで揃えてからsubset判定。
- 連番化: ignore_index=Trueでインデックスをリセットし、後工程の参照を安定化。
まとめ(subsetで“何を重複とみなすか”を決め、keepと順序で“どれを残すか”を制御)
drop_duplicatesは、重複行を削除して分析を正しく進めるための基本メソッドです。まずsubsetで「重複判定の軸」を決め、keepで「残す基準(最初・最後・完全排除)」を選ぶ。順序が意味を持つならsortしてから実行し、ignore_indexで連番化する。duplicatedで可視化してから削除に進み、キー列は正規化して誤判定を防ぐ。これらを型として徹底すれば、初心者でも“過不足なく、意図どおりに”重複を掃除できます。
