一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐

加入好友
加入社群
一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王

在 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()] 的等價、差異與最佳實踐 - 儲蓄保險王

兩者的差異點與何時使用

  • 語意層次:
    • 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()] 的等價、差異與最佳實踐 - 儲蓄保險王

實用技巧

  • 只在某欄忽略大小寫或空白去重:
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()] 的等價、差異與最佳實踐 - 儲蓄保險王

對數值做「近似」去重(四捨五入後判斷):

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()] 的等價、差異與最佳實踐 - 儲蓄保險王

保留每組的前 N 筆(如每個 name 的前 2 筆):

out = df[df.groupby("name").cumcount() < 2]

輸出:

一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王

常見誤區

  • 把 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()] 的等價、差異與最佳實踐 - 儲蓄保險王
一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王

多欄與整列去重(只有 drop_duplicates 做得到)

一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王

和 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()] 的等價、差異與最佳實踐 - 儲蓄保險王
加入好友
加入社群
一文搞懂Python pandas.DataFrame去重:df.drop_duplicates() 與 df[~df.duplicated()] 的等價、差異與最佳實踐 - 儲蓄保險王

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *