Learning Objectives
- 用 isna 找出缺值並數出每一欄缺幾筆,知道 NaN 不能用 == 比較
- 說出 mean、std 等統計量預設略過缺值,以及 count 與 len 的差別
- 用 dropna、fillna、ffill、interpolate 處理缺值,並說出補平均對標準差與分布形狀的影響
- 分辨缺值的原因(隨機或和數值本身有關),說明為什麼原因會讓結果偏誤
資料裡的空格
真實的資料幾乎都有空格:學生缺考、問卷有一題沒填、電表某天沒回報。pandas 把這些空格記成 NaN(Not a Number,缺值,missing value)。 缺值本身不難處理,難的是選哪一種處理方法:每一種方法都會改變平均、標準差或分布的樣子,有時還會讓結論偏掉。
這一課用兩份資料,都是程式產生的模擬資料,不是任何學校的真實資料:
| 檔案 | 缺值 |
|---|---|
grades.csv(600 筆修課成績) |
期中考有人缺考 |
dorm_power.csv(5 棟宿舍 × 122 天的用電) |
有幾天電表沒有回報 |
逐步看:找出缺值、數一數
看 absent 在記憶體區的表格:期中那一欄的值是 NaN。
刪除或補值:每一種都有代價
常見的處理方法:
| 方法 | 寫法 | 代價 |
|---|---|---|
| 不處理(略過缺值) | s.mean() |
平均、標準差等統計量預設跳過 NaN;但要記得實際用了幾筆(count()) |
| 刪除 | s.dropna()、df.dropna(subset=[...]) |
筆數變少;整列刪除時,那一列其他欄的資料也一起丟掉 |
| 補平均、中位數 | s.fillna(s.mean()) |
補上的值都在正中間:標準差變小,分布多出一根很高的柱子 |
| 補一個固定值 | s.fillna(0) |
把「不知道」寫成一個真的數字;補 0 會把平均拉低 |
補平均讓平均不變,看起來很安全,但補上的每一筆離平均的距離都是 0,所以資料「看起來」比實際更集中。 之後要算標準差、信賴區間或做檢定時,這會讓你對結果過度有信心。
逐步看:五種方法的平均與標準差
迴圈每一圈處理一種方法,s 是處理後的期中成績。注意最後一行:補平均之後,正好等於平均的值從 0 筆變成 13 筆。
有先後順序的資料:ffill 與 interpolate
每日用電、每小時溫度這種依時間排列的資料,相鄰的值通常很接近,可以用附近的值估計缺值:
s.ffill()(forward fill):用前一筆的值補。適合「狀態持續到下次改變」的資料,例如設定值、價格。s.interpolate():用前後兩筆連成直線,取中間的值。適合連續變化的量,例如溫度、用電。
兩種方法都要求資料已經依時間排好;開頭的缺值前面沒有值可以參考,所以補不到。 相對地,期中成績這種沒有先後順序的資料,「前一筆」只是剛好排在旁邊的另一位學生,用 ffill 沒有道理。
逐步看:C 棟用電的三種補法
最後一步會畫出整季的用電與補上的點。紅色的 × 是補整季平均的位置,和前後幾天比一比。
為什麼缺?原因會讓結果偏掉
處理方法之外,更要問資料為什麼缺。統計學常用下面三種情況來描述,其中「隨機遺漏」的概念出自 Rubin(1976):
| 情況 | 意思 | 例子 |
|---|---|---|
| 完全隨機遺漏(MCAR) | 缺不缺和任何事都無關 | 電表隨機故障 |
| 隨機遺漏(MAR) | 缺不缺和其他已知的欄位有關 | 某一棟的電表比較舊,比較常沒回報 |
| 非隨機遺漏(MNAR) | 缺不缺和缺掉的值本身有關 | 準備不足、可能考低分的人比較常缺考 |
只有完全隨機遺漏時,略過或刪除缺值才不會讓平均偏掉。如果低分的人比較常缺考,剩下的人本來就比較高分, 不管是略過缺值或補平均,算出來的平均都會偏高;這不是補值方法能修正的。下一個範例用模擬實驗看這件事。
參考:Rubin, D. B. (1976). Inference and missing data. Biometrika, 63(3), 581–592.
逐步看:缺考的原因不同,平均偏差不同
因為是模擬,我們知道每個人「真正」的分數。比較兩種缺考情況下,略過缺值算出的平均和真正的平均差多少。
實作:E 棟的用電缺值
讀入 dorm_power.csv,取出 E 棟(building 是 "E")的用電 kwh,存成 e(資料已經依日期排好)。
用內插補上缺值存成 filled,最後兩行會印出補值前後的缺值數與平均。
小測驗:缺值
下面程式會印出什麼?
import numpy as np
import pandas as pd
s = pd.Series([10, np.nan, 20, np.nan])
print(s.count(), len(s), s.mean())
下面程式會印出什麼?
import numpy as np
import pandas as pd
s = pd.Series([1, np.nan, 3])
print((s == np.nan).sum(), s.isna().sum())
一欄成績有 30% 是缺值,你用平均補上。之後算出的標準差會怎樣?
下面程式會印出什麼?
import numpy as np
import pandas as pd
s = pd.Series([np.nan, 4, np.nan, 8])
print(s.ffill().tolist())
問卷的「每月收入」一題,收入高的人比較常不填。只用有填的人算平均,結果會怎樣?
重點整理
- 缺值是 NaN;找缺值用
isna()(NaN 不等於自己),isna().sum()數出每一欄缺幾筆。 mean、std等統計量預設略過缺值;count()是有值的筆數,len()是長度。dropna()刪除、fillna()補值、時間序列用ffill()或interpolate();每一種都有代價,補平均會讓標準差變小、分布變尖。- 缺值的原因比處理方法更重要:和缺掉的值本身有關時,任何補法都修正不了偏差。報告裡要寫清楚缺了幾筆、為什麼缺、怎麼處理。
- 下一課處理另一種髒資料:寫法不一致、型別錯誤、重複的列與超出範圍的值。