在 pandas 中,以下兩種寫法在預設參數下是等價的(保留每組重複中的第一筆):
- df.drop_duplicates()
- df[~df.duplicated()]
本文帶你理解它們的對應關係、常見參數、實戰範例與實務選擇建議。
快速結論
- 等價關係(帶參數時也對應相同語意):
- df.drop_duplicates(subset=…, keep=…) 等同於 df[~df.duplicated(subset=…, keep=…)]
- 預設 keep=’first’:保留每組重複的第一筆。
- keep=’last’:保留最後一筆。
- keep=False:只保留「唯一值」(所有重複全部移除)。
- NaN 在這兩個方法中被視為可比對的值(NaN 視為相等),行為一致。
- drop_duplicates 可用 ignore_index=True 重排索引;duplicated 沒有這個參數。
- 想重用布林遮罩或做進一步統計時,用 duplicated;只需要去重時,用 drop_duplicates 更直覺。
基本範例
import pandas as pd
df = pd.DataFrame({
"name": ["A", "B", "A", "C", "B"],
"age": [20, 21, 20, 22, 21]
})
# 1) 預設:保留第一筆
df1 = df.drop_duplicates()
df2 = df[~df.duplicated()]
# df1 與 df2 等價
# 2) 指定欄位去重(以 name 判斷)
df3 = df.drop_duplicates(subset=["name"], keep="first")
df4 = df[~df.duplicated(subset=["name"], keep="first")]
# 3) 保留最後一筆
df5 = df.drop_duplicates(subset=["name"], keep="last")
df6 = df[~df.duplicated(subset=["name"], keep="last")]
# 4) 只保留唯一(所有重複全部移除)
df7 = df.drop_duplicates(subset=["name"], keep=False)
df8 = df[~df.duplicated(subset=["name"], keep=False)]
# 5) 去重同時重排索引
df9 = df.drop_duplicates(subset=["name"], keep="first", ignore_index=True)
# 6) 原地修改(不回傳新 DataFrame)
df_copy = df.copy()
df_copy.drop_duplicates(subset=["name"], inplace=True)
輸出結果:
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808202701_0_66f9bc.png)
兩者的差異點與何時使用
- 語意層次:
- duplicated 產生布林遮罩(True 表示「這列是重複項」);你可以重複使用這個遮罩做更多操作。
- drop_duplicates 直接回傳去重後的 DataFrame。
- 參數差異:
- drop_duplicates 支援 ignore_index=True 直接重排索引;duplicated 不支援此參數。
- 兩者都支援 subset 與 keep。
- 效能與記憶體:
- 兩者底層演算法相近;用布林遮罩會多建立一個 Series(佔用些許記憶體),但可重複利用。
- 單純去重 → 用 drop_duplicates 更簡潔;需要遮罩做統計/定位 → 先 duplicated 再操作更靈活。
NaN、排序與穩定性
- NaN 被視作可配對值:多筆 NaN 在相同 subset 下會互相視為重複。
- 去重是「穩定的」:保留的順序就是原始順序中第一(或最後)次出現的順序;兩種寫法行為一致。
df = pd.DataFrame({"x": [1, 1, None, None, 2]})
df.drop_duplicates() # 保留第一個 1、第一個 None、以及 2
df[~df.duplicated()] # 同上
df.drop_duplicates(keep=False) # 只保留唯一(此例只剩 2)
輸出結果:
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808203517_0_a21972.png)
實用技巧
- 只在某欄忽略大小寫或空白去重:
df["name_norm"] = df["name"].str.strip().str.lower()
out = df.drop_duplicates(subset=["name_norm"]).drop(columns=["name_norm"])
輸出:
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808203950_0_6a07ad.png)
對數值做「近似」去重(四捨五入後判斷):
out = df.loc[~df["score"].round(2).duplicated()]
先標記重複,再篩出重複群組查看:
mask_dup = df.duplicated(subset=["name"], keep=False)
dup_groups = df[mask_dup].sort_values("name")
輸出:
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808204524_0_7a4e26.png)
保留每組的前 N 筆(如每個 name 的前 2 筆):
out = df[df.groupby("name").cumcount() < 2]
輸出:
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808204844_0_09df6e.png)
常見誤區
- 把 keep=False 理解錯:
- duplicated(keep=False) → 將每一組重複的所有成員都標記為 True。
- 因此 ~duplicated(keep=False) 只會保留「完全不重複的列」。
- drop_duplicates(keep=False) 與上述遮罩語意一致:最後只剩唯一值。
- 以多欄判斷重複時,要用 subset 列出所有欄位;未列入的欄位不影響重複判斷。
- duplicated 的結果是布林 Series;若要看「第一筆留下、其餘重複」的位置,查看 duplicated(keep=’first’) 為 True 的列即可。
小結
- 基本等價:df.drop_duplicates(subset, keep) ≡ df[~df.duplicated(subset, keep)]
- 只想快速去重 → drop_duplicates。
- 想重用遮罩做更多分析 → duplicated 再配合篩選。
- 善用 subset、keep、ignore_index,以及文本/數值的前處理,能更準確地控制去重行為。
推薦hahow線上學習python: https://igrape.net/30afN
單純「去重」且不打算重用遮罩時,優先用 drop_duplicates() 更簡潔、可讀性更好。
為什麼常用 drop_duplicates
- 語意直覺:一眼看出「在做去重」。
- 少一個中間布林 Series,程式更短。
- 內建實用參數:ignore_index=True 重排索引、inplace=True 原地修改。
- 與 subset、keep 的語意與 duplicated 完全對齊。
什麼情況改用 duplicated 遮罩更合適
- 你需要「重複的位置/索引」來做後續操作或統計:
- mask = df.duplicated(subset=…, keep=False)
- df[mask] 只看重複者;df[~mask] 只看唯一者。
- 需要把「是否重複」存成欄位,供後續分析或標記:
- df[“is_dup”] = df.duplicated(subset=…, keep=”first”)
- 要與其他條件一起組合篩選:
- df[~df.duplicated(“name”) & (df[“score”] > 80)]
- 需要更複雜的保留規則(例如每組保留前 N 筆):
- df[df.groupby(“key”).cumcount() < N]
效能提示
- 兩者底層開銷相近;遮罩會多一個布林 Series,一般規模差異可忽略。超大表時可按需基準測試。
實務結論
- 只想得到「去重後的表」→ 用 df.drop_duplicates(…)。
- 需要重複的「位置/標記/組合條件/進一步分析」→ 用 df.duplicated(…) 產生 mask。
推薦hahow線上學習python: https://igrape.net/30afN
.unique() 跟 drop_duplicates() 不只是「輸出型別不同」,它們的語意也不同。unique 用在一維資料(Series/Index)的「值去重」,而 drop_duplicates 用在 Series 或 DataFrame 的「值或列去重」,而且支援 keep 規則與多欄判斷。
重點對照
- 作用範圍
- Series.unique() / pd.unique(): 只針對「一個欄位」取唯一值,回傳陣列(ndarray 或 ExtensionArray),不保留索引。
- Series.drop_duplicates(): 對一個欄位去重,回傳 Series,保留索引,支援 keep 參數。
- DataFrame.drop_duplicates(): 對「整列」或指定多欄 subset 去重,回傳 DataFrame,支援 keep 與 ignore_index 等參數。
- 去重規則
- unique(): 沒有 keep 選項;保留每個不同值「第一次出現」的順序,各值只出現一次。
- drop_duplicates(keep=’first’|’last’|False):
- ‘first’:保留每組重複的第一次出現(順序即首次出現順序)。
- ‘last’:保留最後一次出現(與 unique 的順序不同)。
- False:把所有重複者整組移除,只留下「完全唯一」的值/列。
- 回傳型別與索引
- unique(): 回傳陣列,沒有索引。
- Series.drop_duplicates(): 回傳 Series,保留原索引(可用 .reset_index(drop=True) 重排)。
- DataFrame.drop_duplicates(): 回傳 DataFrame,可用 ignore_index=True 重排索引。
- NaN 行為
- 兩者都把多個 NaN 視為同一個值:unique 只會保留一個 NaN;drop_duplicates 的 keep 規則也會把多個 NaN 視為一組重複。
- 若 keep=False,任何出現超過一次的值(含 NaN)都會被整組移除。
小範例(Series)
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808205912_0_260f7b.png)
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808205931_0_6b65bd.png)
多欄與整列去重(只有 drop_duplicates 做得到)
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808210056_0_6d35ed.png)
和 numpy.unique 的差別
- pandas 的 unique 會「保留原始出現順序」;
- numpy.unique 預設會排序(可能改變順序)。需要保序時請用 pandas 的 unique。
選擇建議
- 只想拿到某欄的「唯一值清單」用來做篩選/列舉 → 用 Series.unique()(最快、最簡潔)。
- 需要保留索引或套用 keep=’last’/keep=False 規則(或之後還要接其他 Series 操作) → 用 Series.drop_duplicates()。
- 需要對多欄或整列去重、或要 ignore_index=True → 用 DataFrame.drop_duplicates()。
- 想知道「唯一值個數」而非值本身 → 用 nunique();想看頻次 → value_counts()。
因此,.unique() 和 drop_duplicates() 在「只取去重結果」時看起來相似,但並非只有型別不同:keep 規則、是否多欄/整列、索引處理與之後可接的操作,都有本質差異。
- drop_duplicates() 功能更「齊全/靈活」
- 可用在 DataFrame(整列或多欄 subset 去重)與 Series。
- 支援 keep=’first’|’last’|False 的保留規則。
- 可 ignore_index=True 重排索引,或 inplace=True 原地改。
- 回傳同型別(DataFrame/Series),保留索引,便於後續鏈式操作。
- unique() 更「單純/高效」
- 只針對一維(Series/Index/陣列)取唯一值清單。
- 不提供 keep 規則與多欄去重。
- 回傳 ndarray/ExtensionArray,沒有索引,通常更快、更省記憶體。
- 適合只想拿到「某欄的不同值列表」做列舉、下拉選單、集合運算等。
怎麼選
- 取一個欄位的唯一值清單 → unique()(或只要數量用 nunique(),要頻次用 value_counts())。
- 要保留索引、控制保留規則、或做多欄/整列去重 → drop_duplicates()。
推薦hahow線上學習python: https://igrape.net/30afN
小心「NaN 比較」的坑
s.unique() 與 s.drop_duplicates(keep=’first’) 產生的值序列,在邏輯上是相同的(都按「首次出現」的順序:1, 2, 3, NaN)。 但用 numpy.array_equal 比較時,不同版本/設定對 NaN 的處理不同:
- 在較新的 NumPy,用 equal_nan=True 才能把同位置的 NaN 視為相等。
- 直接用 array_equal(a, b) 可能把 NaN 視為不相等,導致 False。
import numpy as np
np.array_equal(s.unique(), s.drop_duplicates().to_numpy(), equal_nan=True) # True
輸出:
![一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王](https://savingking.com.tw/wp-content/uploads/2025/08/20250808215449_0_7fd5ad.png)
近期留言