攝影或3C

🚀 Python Pandas 實戰教學:df.map() vs df.apply() 到底差在哪?從資料清洗實例看懂核心差異

在處理 Pandas DataFrame 時,
我們經常需要對資料進行「轉換」或「清理」。
這時候,新手最常遇到的問題就是:
「我到底該用 map() 還是 apply()?」

簡單來說,這兩者的核心差異在於**「作用的範圍」**:

  • df.map()微觀視角(Element-wise)
    它把表格看成一個個獨立的「儲存格」,
    逐一對每一個格子做事。
  • df.apply()宏觀視角(Axis-wise)
    它把表格看成一條條的
    「直行(Column)」或「橫列(Row)」,
    每次抓出一整行或一整列來做事。

(註:在 Pandas 2.1.0 之前的版本,
df.map() 被稱為 df.applymap()
新版已統一更名為 map()。)

接下來,我們直接透過兩段實用程式碼來深度解析!


實戰案例一:清理表格中的髒資料 (使用 df.map)

當我們從網頁或 PDF 萃取表格時,
格子裡常常會混入多餘的空白(例如 " Apple ")。
我們需要一個函數來清理這些空白,
並把全空的列與欄刪除。

來看看 normalize_table_df 函數:

import pandas as pd

def normalize_table_df(table_df: pd.DataFrame) -> pd.DataFrame:
    normalized_df = table_df.copy()
    
    # 關鍵點 1:使用 .map() 針對每一個儲存格進行字串清理
    normalized_df = normalized_df.map(
        lambda value: value.strip() if isinstance(value, str) else value
    )
    
    # 關鍵點 2:將空字串轉為標準缺失值以便後續 dropna 辨識
    normalized_df = normalized_df.replace("", pd.NA)
    
    # 關鍵點 3:刪除全為 NA 的橫列與直行
    normalized_df = normalized_df.dropna(axis=0, how="all")
    normalized_df = normalized_df.dropna(axis=1, how="all")
    
    return normalized_df.reset_index(drop=True)

💡 為什麼這裡必須用 df.map() 而不是 df.apply()

在這段程式碼中,
我們寫了一個 lambda 條件判斷:
isinstance(value, str)
我們希望程式去檢查:「這個格子裡裝的是不是字串?」

  • ✅ 使用 df.map() 時:
    value 真的就是每一個格子的內容(例如 " Apple "123NaN)。
    所以 isinstance(" Apple ", str) 會回傳 True
    接著順利執行 .strip() 清除空白。
  • ❌ 如果錯用 df.apply() 時:
    apply 預設是一次抓一整欄(Column)。
    這時傳進去的 value 會是一個
    Pandas Series(一整排資料),而不是單一字串。
    如果你問 Python:「這『一整排資料』是不是一個字串?」
    isinstance(Series, str)),答案永遠是 False
    結果就是你的 .strip() 完全不會被執行,
    資料根本沒有被清理到。

結論:當你需要對「每一個獨立的儲存格」
做型別檢查、字串操作或數學運算時,
df.map() 是你的唯一首選。


實戰案例二:判斷表格是否具有意義 (理解 Axis-wise 的概念)

清理完表格後,
有時候我們會得到一些只有 1、2 個字的「破碎表格」。
我們需要一個機制來過濾掉這些垃圾資料。

來看看 is_meaningful_table 函數:

def is_meaningful_table(table_df: pd.DataFrame) -> bool:
    if table_df.empty:
        return False

    # pandas DataFrame  .sum() 行為說明
    # 1. table_df.notna():把整個表格所有的格子變成 True/False (有值為 True, 沒值或 NA  False)
    # 2. 第一次 .sum():逐個直行(column)」 True 當成 1 算總和回傳的是一個 Series例如colA  2 個值, colB  1 個值
    # 3. 第二次 .sum():把剛剛算出來的 Series (每一行的總數) 再全部加總起來得到非空值的總數
    # (Pandas 加總後回傳的型別其實是 numpy.int64但在這做 >= 3 的比較可直接當一般整數使用)
    non_empty_cells = table_df.notna().sum().sum()
    
    # 這裡的邏輯是如果整張表格有值(非空)的格子加起來不到 3 我們就當它是誤判的垃圾碎片
    return non_empty_cells >= 3

💡 這裡的 .sum() 其實就體現了 df.apply() 的精神!

雖然這段程式碼沒有直接寫出 apply()
但 Pandas 內建的 .sum().mean() 等聚合函數,
它們的運作邏輯與 df.apply() 完全一致,
都是 Axis-wise(以軸為單位) 的操作。

讓我們拆解 table_df.notna().sum().sum() 的過程:

  1. table_df.notna():這是一個類似 map 的動作,
    把每個格子變成 True/False。
  2. 第一次 .sum():這就像是 df.apply(sum)
    它預設沿著 axis=0(直行)往下加總。
    它不會一次把整張表加完,而是一欄一欄算
    最後吐出一個 Series(例如:A欄有2個值,B欄有1個值)。
  3. 第二次 .sum():這時候是對剛剛產生的 Series 做加總,
    把 2 + 1 變成 3,得到最終的總數。

如果你硬要用 apply 來重寫第一次的 sum(),它會長這樣:
table_df.notna().apply(lambda col: col.sum())
(當然,直接寫 .sum() 效能更好且更簡潔,這裡僅為觀念對照)。


📝 總結:一秒決定該用誰?

下次寫程式時,只要問自己一個問題:
「我的操作對象是誰?」

透過這兩個函數的完美搭配,
你不僅能將髒亂的表格洗得乾乾淨淨(map 的功勞),
還能精準剔除沒有價值的破碎表格(Axis-wise 聚合的功勞),
這就是 Pandas 資料處理的藝術!

推薦hahow線上學習python: https://igrape.net/30afN

在 Pandas 中,用 01 來代表方向,
對人類的直覺來說真的很容易搞混。
為了解決這個問題,
Pandas 官方非常貼心地提供了
字串別名 (String Aliases)
這也是目前非常推崇的寫法,
因為它能大幅提升程式碼的「可讀性」。

它們的對應關係如下:

  • axis=0 等同於 axis="index"(在某些函數中也可以寫 axis="rows"
    • 好記法:Index(索引)是直直往下排的。
      所以 axis="index" 代表動作是
      **「由上往下,跨越一列一列」**去執行的。
  • axis=1 等同於 axis="columns"
    • 好記法:Columns(欄位)是橫向往右排的。
      所以 axis="columns" 代表動作是
      **「由左往右,跨越一欄一欄」**去執行的。

把這個好習慣套用到你的程式碼中!

回到一開始的 normalize_table_df 函數,
我們把 dropna 的部分換成這個更好懂的寫法:

# ❌ 原本的寫法過幾個月回來看可能會忘記 0  1 是什麼
normalized_df = normalized_df.dropna(axis=0, how="all")
normalized_df = normalized_df.dropna(axis=1, how="all")

# ✅ 升級後的寫法語意超清晰一看就懂!)
normalized_df = normalized_df.dropna(axis="index", how="all")   
# 刪除全部都是 NA 橫列
normalized_df = normalized_df.dropna(axis="columns", how="all") 
# 刪除全部都是 NA 直行

強烈建議:
以後寫 Pandas 時,
只要遇到需要設定 axis 的地方
(像是 .sum(), .mean(), .drop(), .dropna() 等),
都直接改用 "index""columns"
這不僅能少死很多腦細胞,
如果以後有其他同事接手你的程式碼,
他們一定會非常感謝你!

推薦hahow線上學習python: https://igrape.net/30afN

儲蓄保險王

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