404 Not Found

404 Not Found


nginx

重複データの処理【Pandas duplicatedとdrop_duplicates完全ガイド】

データの結合、繰り返し実行される収集処理、システムの不具合など、重複データはどこにでも存在します。1件の顧客レコードが3回登場するだけで、統計は3倍に膨れ上がってしまいます。Pandasのduplicated / drop_duplicatesを使えば、重複を正確に検出してクリーニングできます。さらにsubset + keepパラメータにより、粒度を柔軟に制御できます。本セクションでは、検出から削除までの完全なワークフローを解説します。

⚠️ 注意: 以下のコードはローカルのPython環境で実行してください。

1. このセクションで学ぶこと



2. アリスの重複したコーヒー注文

(1) 問題点:システム不具合による注文の重複

アリスのコーヒーショップで、混雑時にシステムに不具合が発生し、3件の注文が2回送信されてしまいました:

PYTHON
import pandas as pd

orders = pd.DataFrame({
    'order_id': ['O001', 'O001', 'O002', 'O003', 'O003', 'O003', 'O004'],
    'customer': ['Alice', 'Alice', 'Bob', 'Charlie', 'Charlie', 'Charlie', 'Carol'],
    'drink': ['Latte', 'Latte', 'Americano', 'Mocha', 'Mocha', 'Mocha', 'Espresso'],
    'price': [4.50, 4.50, 3.00, 5.50, 5.50, 5.50, 2.50]
})
print(f"総行数: {len(orders)}")
print(f"重複数: {orders.duplicated().sum()}")
# 総行数: 7、重複数: 3
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) 解決策:drop_duplicatesによる1行でのクリーニング

▶ サンプル:基本的な重複排除(難易度 ⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

orders = pd.DataFrame({
    'order_id': ['O001', 'O001', 'O002', 'O003', 'O003', 'O003', 'O004'],
    'customer': ['Alice', 'Alice', 'Bob', 'Charlie', 'Charlie', 'Charlie', 'Carol'],
    'drink': ['Latte', 'Latte', 'Americano', 'Mocha', 'Mocha', 'Mocha', 'Espresso'],
    'price': [4.50, 4.50, 3.00, 5.50, 5.50, 5.50, 2.50]
})

# 行が完全に一致する重複を削除する
clean = orders.drop_duplicates()
print(clean)
#    order_id customer      drink  price
# 0     O001    Alice      Latte    4.5
# 2     O002      Bob  Americano    3.0
# 3     O003  Charlie      Mocha    5.5
# 6     O004    Carol   Espresso    2.5

print(f"処理前: {len(orders)} → 処理後: {len(clean)}")
# 処理前: 7 → 処理後: 4
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。


3. duplicatedによる重複の検出

(1) 基本的な検出

▶ サンプル:duplicatedによる重複の検出(難易度 ⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob'],
    'age': [28, 34, 28, 25, 35]
})

# 重複をマークする(最初の出現 = False、それ以降 = True)
print(df.duplicated())
# 0    False  ← 最初のAlice(保持)
# 1    False  ← 最初のBob(保持)
# 2     True  ← 2番目のAlice(重複!)
# 3    False  ← 最初のCharlie(保持)
# 4     True  ← Bobだがage=35(重複ではない — 行が異なる)

# 重複行だけを抽出して確認する
print(df[df.duplicated()])
#    name  age
# 2 Alice   28

# 重複をカウントする
print(f"重複行: {df.duplicated().sum()}")  # 1
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) keepパラメータがマーキングロジックを制御する

keepの値 動作 最初の重複 最後の重複
'first' 最初の出現を保持 Falseとマーク Trueとマーク
'last' 最後の出現を保持 Trueとマーク Falseとマーク
False すべての重複をマーク Trueとマーク Trueとマーク

▶ サンプル:keepパラメータの比較(難易度 ⭐⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Alice', 'Alice', 'Bob'],
    'score': [85, 92, 85, 85, 92]
})

# keep='first'(デフォルト) — 最初の出現は重複とみなされない
print("keep='first':", df.duplicated(keep='first').tolist())
# [False, False, True, True, True]

# keep='last' — 最後の出現は重複とみなされない
print("keep='last':", df.duplicated(keep='last').tolist())
# [True, True, True, False, False]

# keep=False — すべての重複がTrueとマークされる
print("keep=False:", df.duplicated(keep=False).tolist())
# [True, True, True, True, True]
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。


4. drop_duplicatesによる重複の削除

(1) 基本的な削除

▶ サンプル:drop_duplicatesによる重複の削除(難易度 ⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Alice'],
    'age': [28, 34, 28, 25, 28],
    'city': ['NYC', 'LA', 'NYC', 'Chicago', 'Boston']  # 最後のAliceはcityが異なる
})

# 行全体が一致する重複を削除する
print(df.drop_duplicates())
#    name  age     city
# 0 Alice   28      NYC
# 1   Bob   34       LA
# 3 Charlie   25  Chicago
# 4 Alice   28   Boston  ← 保持(cityが0行目と異なる)

# keepパラメータを指定する場合
print(df.drop_duplicates(keep='last'))
#    name  age     city
# 1   Bob   34       LA
# 3 Charlie   25  Chicago
# 4 Alice   28   Boston  ← 最後のAliceを保持

# すべての重複を削除する(ユニークな行だけを残す)
print(df.drop_duplicates(keep=False))
#    name  age     city
# 1   Bob   34       LA
# 3 Charlie   25  Chicago
# Aliceの行はすべて削除される(複数存在するため)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) inplaceとインデックスのリセット

▶ サンプル:inplaceとreset_index(難易度 ⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Alice', 'Charlie'],
    'score': [85, 92, 85, 78]
})

# 方法1:再代入する(推奨)
df = df.drop_duplicates().reset_index(drop=True)
print(df)
#     name  score
# 0  Alice     85
# 1    Bob     92
# 2 Charlie    78

# 方法2:inplaceを使う
# df.drop_duplicates(inplace=True)
# df.reset_index(inplace=True, drop=True)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。


5. subsetを使った列基準の重複排除

(1) キーカラムだけに着目する

▶ サンプル:subsetを使った列基準の重複排除(難易度 ⭐⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

df = pd.DataFrame({
    'student_id': ['S001', 'S001', 'S002', 'S003', 'S002'],
    'name': ['Alice', 'Alice Smith', 'Bob', 'Charlie', 'Robert Bob'],
    'math_score': [85, 85, 92, 78, 92],
    'attempt': [1, 2, 1, 1, 2]
})

# 行全体の重複:なし(nameカラムが異なる)
print(f"行全体の重複: {df.duplicated().sum()}")  # 0

# しかしstudent_idはユニークであるべき!
print(f"student_idの重複: {df.duplicated(subset='student_id').sum()}")  # 2

# student_idだけを基準に重複を削除する
clean = df.drop_duplicates(subset='student_id', keep='first')
print(clean)
#   student_id         name  math_score  attempt
# 0       S001        Alice          85        1
# 2       S002          Bob          92        1
# 3       S003      Charlie          78        1
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) 複数カラムの組み合わせによる重複排除

▶ サンプル:複数カラムの組み合わせによる重複排除(難易度 ⭐⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

# 同じ学生が複数回テストを受けることがある
# しかし、同じ学生 + 同じ科目はユニークであるべき
df = pd.DataFrame({
    'student_id': ['S001', 'S001', 'S001', 'S002', 'S002'],
    'subject': ['Math', 'English', 'Math', 'Math', 'English'],
    'score': [85, 90, 88, 92, 87],
    'attempt_date': ['2024-01-10', '2024-01-10', '2024-02-15', '2024-01-12', '2024-01-12']
})

# 重複:student_id + subjectの組み合わせ
dupes = df.duplicated(subset=['student_id', 'subject'], keep=False)
print("重複に関与しているすべての行:")
print(df[dupes])
# S001 + Mathは2回出現する(0行目と2行目)

# 学生 + 科目ごとに最新の受験を保持する
clean = df.drop_duplicates(subset=['student_id', 'subject'], keep='last')
print("\n重複排除後(最新の受験を保持):")
print(clean)
#   student_id  subject  score attempt_date
# 0       S001   English     90   2024-01-10
# 2       S001      Math     88   2024-02-15  ← 最新
# 3       S002      Math     92   2024-01-12
# 4       S002   English     87   2024-01-12
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。


6. 重複データを処理する戦略

(1) シナリオ別の判断表

シナリオ 戦略 コード
完全に重複した行 drop_duplicates() df.drop_duplicates()
キーカラムの重複 subsetで重複排除 df.drop_duplicates(subset=['id'])
最新を保持 keep='last' df.drop_duplicates(subset=['id'], keep='last')
手動レビューが必要 削除せずにマーク df[df.duplicated(keep=False)]
削除せず集約 groupby + agg df.groupby('id').agg({'val': 'mean'})

▶ サンプル:削除の代わりに集約する(難易度 ⭐⭐⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

# 同じセンサーに対する複数の測定値 — 削除する代わりに平均を取る
df = pd.DataFrame({
    'sensor_id': ['T01', 'T01', 'T01', 'T02', 'T02'],
    'location': ['Lab A', 'Lab A', 'Lab A', 'Lab B', 'Lab B'],
    'temperature': [22.5, 22.7, 22.3, 18.1, 18.3],
    'humidity': [45, 47, 43, 55, 57]
})

# 削除する代わりに集約する:平均を保持する
agg = df.groupby('sensor_id').agg({
    'location': 'first',
    'temperature': 'mean',
    'humidity': 'mean'
}).reset_index()
print(agg)
#   sensor_id location  temperature  humidity
# 0       T01    Lab A        22.500      45.0
# 1       T02    Lab B        18.200      56.0
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) 重複排除の判断フロー

100%
graph TB
    Q["重複が見つかった?"] -->|いいえ| DONE["✅ データはクリーン"]
    Q -->|はい| TYPE{"行全体?キーカラム?"}
    TYPE -->|行全体| SIMPLE["drop_duplicates()"]
    TYPE -->|キーカラム| SUBSET["drop_duplicates(subset=['id'])"]
    SIMPLE --> WHICH["keep='first'/'last'/False?"]
    SUBSET --> WHICH
    WHICH -->|最初を保持| FIRST["keep='first'"]
    WHICH -->|最後を保持| LAST["keep='last'"]
    WHICH -->|すべてレビューが必要| REVIEW["duplicated(keep=False) → 手動"]
    WHICH -->|値が異なる| AGG["groupby().agg() → 統合"]
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。


7. 完全な例:顧客データの完全な重複排除ワークフロー

▶ サンプル:顧客データの完全な重複排除ワークフロー(難易度 ⭐⭐⭐)

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。
PYTHON
import pandas as pd

# ============================================
# 包括的な例:複数の戦略を使った
# 顧客データの重複排除
# ============================================

# 1. さまざまな重複パターンを含む生データ
df = pd.DataFrame({
    'email': ['alice@example.com', 'alice@example.com', 'bob@example.com',
              'charlie@example.com', 'bob@example.com', 'alice@example.com',
              'david@example.com'],
    'name': ['Alice', 'Alice Smith', 'Bob', 'Charlie', 'Bob Jones',
             'Alice', 'David'],
    'phone': ['555-0100', '555-0100', '555-0200', '555-0300',
              '555-0201', '555-0100', '555-0400'],
    'signup_date': ['2024-01-15', '2024-02-01', '2024-01-20',
                    '2024-03-01', '2024-03-15', '2024-01-15',
                    '2024-04-01']
})

print("=== ステップ1:重複の検出 ===")
print(f"行全体の重複: {df.duplicated().sum()}")
print(f"Emailの重複: {df.duplicated(subset='email').sum()}")
print(f"Email+Phoneの重複: {df.duplicated(subset=['email','phone']).sum()}")

# 2. emailの重複に関与しているすべての行をレビューする
print("\n=== ステップ2:Emailの重複をレビュー ===")
email_dupes = df[df.duplicated(subset='email', keep=False)]
print(email_dupes.sort_values('email'))

# 3. 戦略:emailごとに最新の登録を保持する
df['signup_date'] = pd.to_datetime(df['signup_date'])
clean = df.sort_values('signup_date').drop_duplicates(subset='email', keep='last')
print("\n=== ステップ3:重複排除後(最新を保持) ===")
print(clean[['email', 'name', 'signup_date']])

# 4. 検証する
print(f"\n処理前: {len(df)} 行 → 処理後: {len(clean)} 行")
print(f"ユニークなemail数: {clean['email'].nunique()}")
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバーにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して実行してください。実際の値はpandasのバージョンによって若干異なる場合があります。

❓ よくある質問

Q duplicatedとdrop_duplicatesの違いは何ですか?
A duplicated()は重複行をマークしたブール型のSeriesを返します(削除はしません)。検出やレビューに使います。drop_duplicates()は重複を排除したDataFrameを直接返します(元のデータは変更しません)。重複を調べたいときはduplicated、クリーニングしたいときはdrop_duplicatesを使ってください。
Q keep='first'/'last'/Falseはどのように使い分けますか?
A keep='first'は最初の出現を保持します(デフォルトで、ログ形式のデータに適しています)。keep='last'は最後の出現を保持します(更新で上書きするシナリオに適しています)。keep=Falseはすべての重複行をマークします(レビュー用で、重複は1件も保持しません)。drop_duplicates(keep=False)は、一度も重複しなかった行だけを保持します。
Q 単一カラムと複数カラムのsubsetの違いは何ですか?
A subset=['email']はemailカラムだけが重複しているかをチェックし、他のカラムの違いは無視します。subset=['email','phone']は両方のカラムが同時に一致して初めて重複とみなします。単一カラムの重複排除は正当なデータを誤って削除する可能性があります(同じemailでも連絡先が異なる場合)。一方、複数カラムの組み合わせはより精密ですが、バリエーションのある重複を見逃す可能性があります。
Q 重複排除後にインデックスが乱れた場合はどうすればよいですか?
A drop_duplicatesで行を削除すると、元のインデックスが保持されるため、欠番が生じます。reset_index(drop=True)を使って連続したインデックスにリセットしてください。drop=Trueを指定すると古いインデックスは破棄されます。指定しないと、古いインデックスが'index'という名前の新しいカラムになります。推奨されるチェーン形式はdf.drop_duplicates().reset_index(drop=True)です。
Q 重複データは削除すべきですか、それとも集約すべきですか?
A ビジネス上の意味によります。重複がエラーの場合(システムの不具合など)は削除します。重複が繰り返しの測定値の場合は集約(mean/max/last)します。判断がつかない場合は、まずマークしてレビューし、直接削除しないでください。原則として、削除は不可逆ですが、集約は情報を保持します。
Q drop_duplicatesは元のDataFrameを変更しますか?
A いいえ。drop_duplicatesは新しいDataFrameを返し、元のデータは変更されません。inplace=Trueパラメータを使えばその場で変更できますが、Pandas 3.xのCopy-on-Writeモードでは代入形式が推奨されます:df = df.drop_duplicates()。こちらの方がセマンティクスが明確です。
Q 「ほぼ重複」した行はどうやって見つけますか?
A 完全な重複にはduplicatedを使いますが、「Alice」と「Alice Smith」は完全な重複ではありません。戦略としては、まず正規化してから比較します。strip()で空白を除去し、lower()で大文字小文字を統一するか、emailのプレフィックスを部分文字列としてマッチさせます。あいまいマッチングが必要な場合は、recordlinkageやdedupeライブラリを使って確率的に重複排除を行います。

📖 まとめ


📝 練習問題

  1. 基礎(難易度 ⭐):完全に重複した行を3行含むDataFrame(合計8行)を作成してください。duplicated().sum()でカウントし、drop_duplicates()でクリーニングして、処理前後の行数を比較してください。
  2. 中級(難易度 ⭐⭐):学生の成績DataFrame(同じ学生が同じ科目に複数回登場する)を作成してください。subset=['student_id','subject']で重複排除し、keep='last'を使って最新の成績を保持してください。
  3. チャレンジ(難易度 ⭐⭐⭐):顧客の登録データ(email/name/phone/signup_date)をシミュレートしてください。emailが重複しているがname/phoneが異なるケースも含めてください。次の処理を完成させてください:emailの重複を検出 → 重複行をレビュー → signup_dateでソートしてからkeep='last'で重複排除 → ユニークなemail数を集計して検証。

← 前へ:欠損値の処理 · 次へ:データの変換 →

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%