import pandas as pd
df = pd.DataFrame({"a": [1, 1, 2], "b": [2, 2, 3], "c": [1, 2, 3]})
df| a | b | c | |
|---|---|---|---|
| 0 | 1 | 2 | 1 |
| 1 | 1 | 2 | 2 |
| 2 | 2 | 3 | 3 |
Ryo Nakagami
2026-03-18
2026-04-27
Exercise 1
(a, b) の組が重複する行を1行に集約したい. ただし c カラムには行ごとに異なる情報が入っており,どの行を残すかは別途決められるようにしたい.
| a | b | c | |
|---|---|---|---|
| 0 | 1 | 2 | 1 |
| 1 | 1 | 2 | 2 |
| 2 | 2 | 3 | 3 |
期待する出力 ((a, b) で最初に出現した行を残す)
| a | b | c |
|---|---|---|
| 1 | 2 | 1 |
| 2 | 3 | 3 |
ポイント
(a, b) をキーとして扱い,それ以外のカラムは副次情報として持ち越すfirst / last / 全削除) は明示する.デフォルト挙動に依存するとライブラリ間で結果が変わるDataFrame.drop_duplicates(subset=...) が王道.subset を渡さないと全カラムで完全一致した行のみが重複扱いされる点に注意.
keep で残す行を選ぶ
keep=False を渡すと重複した行をすべて削除してユニークな行だけ残せる.
NaN == NaN を真として重複判定する.SQL の NULL 比較とは挙動が違うので,BigQuery 移植時にズレるsubset のカラム順序は判定に影響しない (集合として扱われる).ただし出力の行順は元の DataFrame の順序が保たれるc のうち最大値を残したい」のような条件付きの場合は,drop_duplicates ではなく df.sort_values(["a","b","c"]).drop_duplicates(["a","b"], keep="last") のように並び替えてから keep を使うpolars ではdrop_duplicates という名前のメソッドはなく,unique(subset=...) が正準.
| a | b | c |
|---|---|---|
| i64 | i64 | i64 |
| 1 | 2 | 1 |
| 2 | 3 | 3 |
maintain_order=True を必ず付けるのが実務上のポイント.polars はデフォルトでは並列処理の都合上行順を保証しないため,付け忘れると毎回違う行が残ったように見える事故が起きる.
keep のオプション
keep |
残る行 | pandas 対応 |
|---|---|---|
"first" |
最初に出現した行 | keep="first" |
"last" |
最後に出現した行 | keep="last" |
"any" |
どれか1行 (順序保証なし・最速) | (該当なし) |
"none" |
重複しているグループは全削除 | keep=False |
pandas との対応関係
| 操作 | pandas | polars |
|---|---|---|
| サブセット指定で重複削除 | df.drop_duplicates(subset=["a","b"]) |
df.unique(subset=["a","b"], maintain_order=True) |
| 最後の行を残す | keep="last" |
keep="last" |
| 重複行をすべて削除 | keep=False |
keep="none" |
| 順序より速度優先 | (相当なし) | keep="any" (順序非保証だが最速) |
| 全カラムでユニーク化 | df.drop_duplicates() |
df.unique(maintain_order=True) |
注意点 !
unique はnull を一意な値として扱う (= 同じ null 同士は重複扱い).pandas の NaN 挙動と一致するので移植性は高いが,SQL の NULL とは違うkeep="any" の方がハッシュテーブル走査が打ち切れて速い.順序が要らない集計の前段では使い分けるとよいlazy() 経由なら unique も Plan 最適化の対象になり,後続の select と統合されるBigQuery で「sub-set 指定の重複削除」を一発で書く正準は QUALIFY ROW_NUMBER(). SELECT DISTINCT は全カラム一致しか扱えないので別問題になる.
QUALIFY パターン
ポイントはPARTITION BY がキー,ORDER BY が「どの行を残すか」. ORDER BY c なら c 最小の行が残り,ORDER BY c DESC なら最大.ORDER BY を省くと結果が非決定的になるので必ず指定する.
全削除 (keep=False 相当)
COUNT(*) OVER で重複群を識別してから絞り込む.
NULL はNULL = NULL が UNKNOWNで,PARTITION BY ではグループ化キーとして扱われる (= 同じ NULL 同士は同じグループ).ただし WHERE a = b 系の比較では一致扱いされない.pandas / polars との違いに注意QUALIFY はOVER 句のあるクエリ専用.通常の WHERE と違い,SELECT リストに窓関数を出していなくても使えるORDER BY のキーが同値で並ぶケースに備えて,タイブレーク用の列 (updated_at, id 等) を追加しておくと結果が安定するdrop_duplicates(subset=..., keep=...) の一行で完結.条件付き keep はsort_values → drop_duplicates(keep="last") の組合せが定石unique.maintain_order=True を忘れない.順序が不要な前処理段では keep="any" で速度を取りに行けるQUALIFY ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) = 1 が万能.ORDER BY の決定性を必ず確保する