從零開始學習分析棒球之路 (1): Pandas DataFrame 查看資料與缺失值處理
處理缺失值(NaN, Not a Number)是在數據分析和清理過程中很常見的任務之一,目的是確保數據的完整性和準確性。
本文流程:
- 查看資料 (jump to section)
- 分析缺失值 (jump to section)
- 處理缺失值 (jump to section)
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)。
留言
張貼留言