Pandasでデータ分析を行う際、重複データの処理は避けて通れない重要な前処理作業です。データの品質を保ち、正確な分析結果を得るためには、重複行の適切な処理が不可欠です。
この記事では、Pandasにおける重複行の削除とカウントの全手法を、実務で即戦力となるコード例とともに詳しく解説します。初心者から上級者まで、レベルに応じて活用できる内容となっています。
目次
サンプルデータの準備
まず、重複行を含むサンプルデータを作成して、各手法を実際に試してみましょう。
import pandas as pd
# 重複行を含むサンプルデータを作成
df = pd.DataFrame({
'ID': [1, 2, 3, 2, 4, 4, 5],
'Name': ['田中', '佐藤', '鈴木', '佐藤', '高橋', '高橋', '山田'],
'Age': [25, 30, 35, 30, 28, 28, 32],
'Department': ['営業', '開発', '総務', '開発', '営業', '営業', '総務']
})
print("元のデータ:")
print(df)
ID Name Age Department
0 1 田中 25 営業
1 2 佐藤 30 開発
2 3 鈴木 35 総務
3 2 佐藤 30 開発 # 1行目と重複
4 4 高橋 28 営業
5 4 高橋 28 営業 # 4行目と重複
6 5 山田 32 総務
1. 重複行の削除(drop_duplicates)
基本的な削除方法
drop_duplicates()
メソッドを使用して、重複行を削除します。デフォルトでは、すべての列が同じ値を持つ行を重複とみなします。
# 基本的な重複削除
df_unique = df.drop_duplicates()
print("重複削除後:")
print(df_unique)
print(f"\n元の行数: {len(df)}, 削除後の行数: {len(df_unique)}")
ID Name Age Department
0 1 田中 25 営業
1 2 佐藤 30 開発
2 3 鈴木 35 総務
4 4 高橋 28 営業
6 5 山田 32 総務
元の行数: 7, 削除後の行数: 5
keepパラメータの使い方
keep
パラメータで、重複行のうちどれを残すかを制御できます。
# 最初の行を残す(デフォルト)
df_keep_first = df.drop_duplicates(keep='first')
print("keep='first' (最初の行を残す):")
print(df_keep_first)
# 最後の行を残す
df_keep_last = df.drop_duplicates(keep='last')
print("\nkeep='last' (最後の行を残す):")
print(df_keep_last)
# すべての重複行を削除
df_keep_false = df.drop_duplicates(keep=False)
print("\nkeep=False (重複行をすべて削除):")
print(df_keep_false)
keep='first' (最初の行を残す):
ID Name Age Department
0 1 田中 25 営業
1 2 佐藤 30 開発
2 3 鈴木 35 総務
4 4 高橋 28 営業
6 5 山田 32 総務
keep='last' (最後の行を残す):
ID Name Age Department
0 1 田中 25 営業
2 3 鈴木 35 総務
3 2 佐藤 30 開発
5 4 高橋 28 営業
6 5 山田 32 総務
keep=False (重複行をすべて削除):
ID Name Age Department
0 1 田中 25 営業
2 3 鈴木 35 総務
6 5 山田 32 総務
特定列での削除
subset
パラメータを使用して、特定の列のみを基準に重複を判定できます。
# ID列のみを基準に重複削除
df_unique_id = df.drop_duplicates(subset=['ID'])
print("ID列を基準とした重複削除:")
print(df_unique_id)
# 複数列を基準に重複削除(名前と年齢の組み合わせ)
df_unique_name_age = df.drop_duplicates(subset=['Name', 'Age'])
print("\n名前と年齢を基準とした重複削除:")
print(df_unique_name_age)
ID列を基準とした重複削除:
ID Name Age Department
0 1 田中 25 営業
1 2 佐藤 30 開発
2 3 鈴木 35 総務
4 4 高橋 28 営業
6 5 山田 32 総務
名前と年齢を基準とした重複削除:
ID Name Age Department
0 1 田中 25 営業
1 2 佐藤 30 開発
2 3 鈴木 35 総務
4 4 高橋 28 営業
6 5 山田 32 総務
2. 重複行の判定とカウント(duplicated)
基本的な重複判定
duplicated()
メソッドは、各行が重複しているかをTrue/Falseで返します。
# 重複行の判定
is_duplicate = df.duplicated()
print("重複判定結果:")
print(is_duplicate)
# 重複行のみを表示
print("\n重複している行:")
print(df[is_duplicate])
重複判定結果:
0 False
1 False
2 False
3 True
4 False
5 True
6 False
dtype: bool
重複している行:
ID Name Age Department
3 2 佐藤 30 開発
5 4 高橋 28 営業
重複行のカウント方法
重複行の数を様々な方法でカウントできます。
# 1. 基本的なカウント
total_duplicates = df.duplicated().sum()
print(f"全体の重複行数: {total_duplicates}")
# 2. 列別重複カウント
print("\n列別重複カウント:")
for col in df.columns:
count = df.duplicated(subset=[col]).sum()
print(f"{col}列の重複数: {count}")
# 3. 複数列の組み合わせでカウント
multi_col_count = df.duplicated(subset=['Name', 'Age']).sum()
print(f"\n名前・年齢組み合わせの重複数: {multi_col_count}")
# 4. 重複率の計算
duplicate_rate = (df.duplicated().sum() / len(df)) * 100
print(f"重複率: {duplicate_rate:.2f}%")
# 5. keepパラメータ別のカウント
print(f"\nkeep='first'での重複数: {df.duplicated(keep='first').sum()}")
print(f"keep='last'での重複数: {df.duplicated(keep='last').sum()}")
print(f"keep=Falseでの重複数: {df.duplicated(keep=False).sum()}")
全体の重複行数: 2
列別重複カウント:
ID列の重複数: 2
Name列の重複数: 2
Age列の重複数: 2
Department列の重複数: 3
名前・年齢組み合わせの重複数: 2
重複率: 28.57%
keep='first'での重複数: 2
keep='last'での重複数: 2
keep=Falseでの重複数: 4
特定列での重複判定
# ID列のみで重複判定
id_duplicates = df.duplicated(subset=['ID'])
print("ID列での重複判定:")
print(df[id_duplicates])
# 部門別の重複をカウント
dept_duplicates = df.groupby('Department').apply(lambda x: x.duplicated().sum())
print("\n部門別重複カウント:")
print(dept_duplicates)
ID列での重複判定:
ID Name Age Department
3 2 佐藤 30 開発
5 4 高橋 28 営業
部門別重複カウント:
Department
営業 1
開発 1
総務 0
dtype: int64
3. drop_duplicatesとduplicatedの違いまとめ
項目 | duplicated() | drop_duplicates() |
---|---|---|
戻り値 | Boolean Series | DataFrame |
用途 | 重複判定・カウント | 重複削除 |
元データ | 変更されない | 変更されない(新しいDFを返す) |
パフォーマンス | 高速 | やや低速 |
主な使用場面 | 重複の確認、統計情報取得 | データクリーニング |
実用例での違い
print("=== duplicated()の場合 ===")
result_duplicated = df.duplicated()
print(f"戻り値の型: {type(result_duplicated)}")
print(f"重複行数: {result_duplicated.sum()}")
print(result_duplicated)
print("\n=== drop_duplicates()の場合 ===")
result_drop = df.drop_duplicates()
print(f"戻り値の型: {type(result_drop)}")
print(f"残った行数: {len(result_drop)}")
print(result_drop)
4. 重複行の結合とグループ化
重複データを削除する代わりに、グループ化して集計することも可能です。
groupbyを使った集計
# 重複するIDをグループ化して集計
grouped_basic = df.groupby('ID').agg({
'Name': 'first', # 最初の名前を取得
'Age': 'mean', # 年齢の平均
'Department': lambda x: ', '.join(x.unique()) # 部門の一意な値を結合
}).reset_index()
print("ID別グループ集計:")
print(grouped_basic)
# より複雑な集計
grouped_advanced = df.groupby(['Name', 'Age']).agg({
'ID': ['first', 'count'],
'Department': lambda x: ', '.join(x.unique())
}).reset_index()
print("\n名前・年齢別グループ集計:")
print(grouped_advanced)
重複フラグの追加
# 重複行をフラグとしてマーク
df_with_flag = df.copy()
df_with_flag['is_duplicate'] = df.duplicated()
df_with_flag['duplicate_count'] = df.groupby(df.columns.tolist()).cumcount() + 1
print("重複フラグ付きデータ:")
print(df_with_flag)
5. 実務でのベストプラクティス
推奨ワークフロー
def analyze_duplicates(df):
"""重複データの分析を行う関数"""
print("=== 重複データ分析レポート ===")
# 1. 基本統計
total_rows = len(df)
duplicate_rows = df.duplicated().sum()
unique_rows = total_rows - duplicate_rows
duplicate_rate = (duplicate_rows / total_rows) * 100
print(f"総行数: {total_rows}")
print(f"重複行数: {duplicate_rows}")
print(f"ユニーク行数: {unique_rows}")
print(f"重複率: {duplicate_rate:.2f}%")
# 2. 列別重複分析
print("\n=== 列別重複分析 ===")
for col in df.columns:
col_duplicates = df.duplicated(subset=[col]).sum()
col_unique = df[col].nunique()
print(f"{col}: 重複{col_duplicates}行, ユニーク値{col_unique}個")
# 3. 重複パターン分析
if duplicate_rows > 0:
print("\n=== 重複パターン ===")
duplicate_patterns = df[df.duplicated(keep=False)].groupby(df.columns.tolist()).size()
print(duplicate_patterns)
return {
'total_rows': total_rows,
'duplicate_rows': duplicate_rows,
'duplicate_rate': duplicate_rate
}
# 使用例
analysis_result = analyze_duplicates(df)
条件付き重複処理
def conditional_dedup(df, condition_col, condition_value, subset_cols=None):
"""条件を満たすデータのみ重複削除を行う"""
# 条件を満たす行のマスク
mask = df[condition_col] == condition_value
# 条件を満たす行で重複削除
if subset_cols:
duplicate_mask = mask & df.duplicated(subset=subset_cols)
else:
duplicate_mask = mask & df.duplicated()
# 重複行を除いたデータフレームを返す
return df[~duplicate_mask]
# 使用例:営業部のデータのみID重複削除
df_cleaned = conditional_dedup(df, 'Department', '営業', ['ID'])
print("営業部のID重複削除後:")
print(df_cleaned)
パフォーマンス最適化
# 大規模データでのパフォーマンス最適化
def efficient_dedup(df, subset_cols=None):
"""効率的な重複削除"""
if subset_cols is None:
subset_cols = df.columns.tolist()
# メモリ使用量を最小化
return df.drop_duplicates(subset=subset_cols).reset_index(drop=True)
# データ型最適化と組み合わせ
def optimize_and_dedup(df):
"""データ型最適化と重複削除を同時実行"""
# 数値列の最適化
for col in df.select_dtypes(include=['int64']).columns:
df[col] = pd.to_numeric(df[col], downcast='integer')
# 文字列列の最適化
for col in df.select_dtypes(include=['object']).columns:
df[col] = df[col].astype('category')
# 重複削除
return df.drop_duplicates().reset_index(drop=True)
6. よくある問題と解決方法(FAQ)
Q1. drop_duplicatesが効かない場合
# 問題:NaNが含まれる場合の対処法
df_with_nan = pd.DataFrame({
'A': [1, 2, None, 2, None],
'B': [1, 2, 3, 2, 3]
})
print("NaN含みデータ:")
print(df_with_nan)
# 解決策1: NaNも重複として扱う
result1 = df_with_nan.drop_duplicates()
print("\nNaNを考慮した重複削除:")
print(result1)
# 解決策2: NaNを特定の値で置換してから重複削除
result2 = df_with_nan.fillna('MISSING').drop_duplicates()
print("\nNaN置換後の重複削除:")
print(result2)
Q2. データ型が異なる場合の問題
# 問題:データ型が混在している場合
df_mixed = pd.DataFrame({
'ID': ['1', 1, '2', 2], # 文字列と数値が混在
'Value': [100, 100, 200, 200]
})
print("データ型混在:")
print(df_mixed.dtypes)
print(df_mixed)
# 解決策:データ型を統一してから処理
df_mixed['ID'] = df_mixed['ID'].astype(str)
result = df_mixed.drop_duplicates()
print("\nデータ型統一後:")
print(result)
Q3. 特定の条件でのみ重複削除したい
# 解決策:条件付き重複削除
df_sample = pd.DataFrame({
'Category': ['A', 'A', 'B', 'B', 'C'],
'Value': [1, 1, 2, 3, 4],
'Status': ['active', 'active', 'inactive', 'active', 'active']
})
# Activeなデータのみで重複削除
active_mask = df_sample['Status'] == 'active'
duplicate_mask = active_mask & df_sample.duplicated(subset=['Category', 'Value'])
result = df_sample[~duplicate_mask]
print("条件付き重複削除結果:")
print(result)
Q4. 重複判定が正しく動作しない場合
# 問題:浮動小数点の精度問題
df_float = pd.DataFrame({
'A': [1.0, 1.0000000001, 2.0],
'B': [1, 1, 2]
})
print("浮動小数点精度問題:")
print(df_float.duplicated())
# 解決策:四捨五入してから比較
df_rounded = df_float.round(5)
print("\n四捨五入後の重複判定:")
print(df_rounded.duplicated())
重要なポイント
重複処理を行う前に、必ずデータの特性とビジネスロジックを確認しましょう。機械的に重複削除を行うのではなく、なぜ重複が発生しているのか、その重複が意味のあるデータなのかを検討することが重要です。
まとめ
Pandasにおける重複行処理の全手法を解説しました。重要なポイントを再確認しましょう:
主要メソッドの使い分け
duplicated()
: 重複の確認・カウント・統計分析に使用drop_duplicates()
: 実際の重複削除・データクリーニングに使用
実務での推奨手順
- データ探索:
df.duplicated().sum()
で重複行数を確認 - 分析: 列別重複パターンを調査
- 判断: ビジネスロジックに基づき処理方針を決定
- 実行: 適切なパラメータで重複削除
- 検証: 処理後のデータ品質を確認
パフォーマンス向上のコツ
- 大規模データでは
subset
パラメータで対象列を限定 - データ型の最適化と組み合わせ実行
- 条件付き処理で不要な計算を削減
実践への活用
これらのテクニックをマスターすることで、データクリーニング作業が大幅に効率化され、より信頼性の高いデータ分析が可能になります。実際のプロジェクトでぜひ活用してください。

最新情報をチェックしよう!