從零開始學習分析棒球之路 (1): Pandas DataFrame 查看資料與缺失值處理

處理缺失值(NaN, Not a Number)是在數據分析和清理過程中很常見的任務之一,目的是確保數據的完整性和準確性。

本文流程:


1. 查看資料:

本次使用的 CSV 資料,為各隊球員的打擊紀錄:

Player,Team,Games_Played,Home_Runs,Batting_Average
Mike Trout,Los Angeles Angels,135,39,0.303
Mookie Betts,Los Angeles Dodgers,145,,0.290
Aaron Judge,New York Yankees,120,31,0.285
,,140,25,0.278
Freddie Freeman,Atlanta Braves,150,38,0.305
Nolan Arenado,St. Louis Cardinals,,32,0.275
Christian Yelich,Milwaukee Brewers,125,,0.290
,,135,20,

欄位說明:

  • Player: 球員姓名
  • Team: 球隊名
  • Games_Played: 出賽場數
  • Home_Runs: 全壘打數
  • Batting_Average: 打擊率 (安打數/打擊次數)

使用貓熊 (pandas),讀取 CSV 資料:

import pandas as pd
player_df = pd.read_csv("player.csv")

Dump 資料並觀察一下資料的結構:

# to display the first few rows with .head() method
# player_df.head()

# to display all data
player_df

這邊讀者們會發現我們資料有很多的 NaN,通常資料中都有缺失的情形。

下面我們會介紹如何處理缺失值的問題。


2. 分析缺失值:

一般處理缺失值有兩種做法:

  • 刪除包含缺失值的 row: 若缺失值相小於整體佔比較小 (~1%) 或是缺失值造成該欄位沒有分析價值,會建議將該 row 刪除。
  • 依照經驗 (domain knowledge)去補值: 如果我們很清楚資料特性的話可以利用經驗去估計缺失值。例如某選手的出賽數為缺失值,但一般選手健康出賽數約在 120~140 場,我們可以將出賽數的缺失值轉換為 130 場。

Dataframe 的 shape 可以告訴我們資料的 row 及 column 的數量:

player_df.shape # output

執行結果為 (8, 5) 告訴我們資料有 8 rows 及 5 columns。


透過 Dataframe 的 columns 可以查看 column 的資訊:

player_df.columns

columns 會回傳關於欄位名稱的資訊 (ex. 'Player', 'Team', 'Games_Played', 'Home_Runs', 'Batting_Average')。


我們可以使用 Dataframe 的 isna() 和 sum() 來計算各欄位缺失值的數量:

nan_count = player_df.isna().sum()
nan_count

除了計算缺失值的數量外我們也可以將各欄的缺失數除以 row 的總數來觀察缺失值的比例:

# DataFrame 的長度即為 row 的數量
total_row_count = len(player_df)

nan_ratio = nan_count / total_row_count
nan_ratio

3. 處理缺失值:

接下來處理缺失值的部分

  • 刪除 NaN 值: 使用 dropna() 方法可以刪除包含 NaN 值的行或列
  • 填充 NaN 值: 使用 fillna() 方法可以填充 NaN 值,可以使用指定的值(如 0), 平均值, 中位數等

我們可以使用 dropna() 清除欄位值為空的 row,透過 subset 可以指定特定的欄位名稱:

named_player_df = player_df.dropna(subset=['Player'])
named_player_df

使用 fillna() 填充缺失值之前先簡單解釋一下中位數, 平均數, 眾數 (詳細可參考 Mean, Median, and Mode in Statistics):

  • 中位數 (median): 數據由小排到大排序時,數據集中間位置的值。 以 Games_Played 來說排序後為 120, 125, 135, 135, 140, 145, 150,中間的值為 135 即為中位數。
  • 平均數 (mean): 數據加總後除以總數,以 Games_Played 來說平均數為 (120 + 125 + 135 + 135 + 140 + 145 + 150) ~= 135.71。
  • 眾數 (mode): 眾數是數據集中出現次數最多的值,以 Games_Played 來說眾數為 135 (出現兩次)。

使用中位數替換缺失值,可以讓數據集不受少數極大值或少數極小值的影響而產生誤差。

# 計算 Games_Played 的中位數
median_games_played = player_df['Games_Played'].median()

# 將 Games_Played 的 NaN 以中位數代替
new_player_df = named_player_df.fillna({"Games_Played": median_games_played})
new_player_df

我們也可以使用 0 去進行填充,以 Home_Runs 為例我們可以將 NaN 替換為 0:

new_player_df = new_player_df.fillna({"Home_Runs": 0})
new_player_df

需要注意的是在處理缺失值的時候還是要盡量結合 domain knowledge。 Christian Yelich 在 18, 19 賽季分別有 36 及 44 發全壘打。直接將Yelich 的 Home_Runs NaN 設為 0 可能會造成統計的瑕庛及誤判 (Christian Stephen Yelich-mlb)。


留言

熱門文章