本文是一篇實用且面向生產環境的指南,說明如何在 Python 數據管道中偵測與處理遺漏值與離群值。文章結合統計直覺、具體程式範例與部署實務,確保你的模型訓練與線上推論保持穩定、可追蹤與可重現。
為什麼遺漏值與離群值在管道中重要
數據管道支撐分析與機器學習,而預處理階段的細微錯誤可能造成模型偏差、性能下降,甚至推論失效。
遺漏值與離群值不是瑣事,而是潛在的故障來源,理由如下:
- 模型敏感性:許多模型假設輸入符合特定分布,離群值可能支配損失函數、扭曲梯度、導致不穩定。
- 偏差與代表性:刪除有遺漏值的資料列可能會移除特定群體,造成偏頗的模型。
- 運行失敗:線上推論中出現 NaN 或極端值可能導致特徵轉換或下游服務崩潰。
- 可稽核性:任何修補都應可重現;臨時填補若未追蹤版本,會使除錯困難。
一項原則:把遺漏與離群視為資訊。有時「缺失」本身具有預測力;離群值也可能代表罕見但真實的行為。
類型與機制
在修補前,先確定問題性質。遺漏資料常見三種類型:
- MCAR(完全隨機缺失):與任何變數無關。
- MAR(隨機缺失):與觀測變數有關。
- MNAR(非隨機缺失):取決於未觀測值本身。
離群值也可分為:
- 全域離群:在整體資料中明顯偏離。
- 情境離群:僅在特定情境下異常,例如午夜的高溫。
- 集體離群:一連串異常值,常見於時間序列。
理解原因有助選擇補救方式。把 MNAR 當作 MCAR 處理,常會導致偏差。
偵測遺漏值與離群值
偵測可分為探索性與自動化。先做整體概覽,再建立能在管道內自動運行的檢查機制。
遺漏診斷
- 每欄缺失比例與可視化矩陣。
- 缺失指標與其他特徵的相關性。
- 隨時間變化的缺失模式。
離群偵測方法
- IQR / Tukey Fence:以分位距界定。
- Z-score:適用接近常態分布的資料。
- Mahalanobis 距離:多變量協方差考量。
- 密度與鄰域:LOF、DBSCAN。
- 樹狀模型:Isolation Forest 對混合尺度數據有效。
- 殘差模型:時間序列中視預測誤差為異常。
可混合使用多種技術,如先用單變量篩選,再以 IsolationForest 偵測細微異常。
填補遺漏值的策略
填補方法各有取捨,從簡單到高階如下:
- 刪除資料:僅在缺失比例極小時可接受。
- 固定值填補:以 0、-1 或 “missing” 類別補足,樹模型能學習此差異。
- 平均/中位/眾數:成本低但有效,偏態資料用中位數更穩。
時間序列可使用前後填補、線性或樣條插值。
- KNN 填補:取鄰近樣本平均。
- 回歸填補:用其他變數預測缺失值。
- MICE:多重鏈式填補,反映不確定性。
- MissForest:隨機森林填補法,對表格數據穩健。
建議加上布林指標 feature_is_missing,讓模型感知缺失信號。
在高風險情境中,使用多重填補能反映估計的不確定性。
處理離群值的方法
離群值可能是錯誤,也可能是罕見但真實的訊號。
- 裁切:將值限制在特定分位。
- 轉為 NaN:再進行填補。
- 穩健轉換:如對數或秩轉換。
- 模型耐受性:選擇樹模型或 Huber 損失。
串流資料應使用滾動偵測與暫存隔離,批次資料則應先調查原因。
整合進生產管道
清理與填補應設為可版本化的流程步驟,常見設計包括:
- 驗證層:過濾異常輸入。
- 偵測層:計算缺失與異常分數。
- 轉換層:應用訓練階段的填補模型。
- 特徵儲存:保存轉換後特徵與中繼資料。
訓練時學得的統計值應序列化保存,以確保推論一致。
所有填補決策與異常分數應記錄,方便稽核與除錯。
評估與監控
應結合離線實驗與線上監控:
- 遮罩實驗:測試填補準確率。
- 下游影響:比較模型效能差異。
- 子群分析:檢測是否引入偏差。
- 監控缺失與異常率變化。
良好的監控能及早發現上游故障。
實用程式碼範例
1) 使用 pandas 產生缺失報告
import pandas as pd
def missing_report(df: pd.DataFrame):
report = pd.DataFrame({
'missing_count': df.isnull().sum(),
'missing_pct': df.isnull().mean()
})
report['dtype'] = df.dtypes
return report.sort_values('missing_pct', ascending=False)
# 用法
# df = pd.read_csv('data.csv')
# print(missing_report(df))
2) 安全填補類別(scikit-learn 樣式)
from sklearn.base import TransformerMixin, BaseEstimator
import numpy as np
import pandas as pd
class SafeImputer(BaseEstimator, TransformerMixin):
def __init__(self, strategy='median', add_indicator=True):
self.strategy = strategy
self.add_indicator = add_indicator
self.statistics_ = {}
def fit(self, X, y=None):
X = pd.DataFrame(X)
for col in X.columns:
if self.strategy == 'median':
self.statistics_[col] = X[col].median()
elif self.strategy == 'mean':
self.statistics_[col] = X[col].mean()
elif self.strategy == 'mode':
self.statistics_[col] = X[col].mode().iloc[0] if not X[col].mode().empty else np.nan
else:
raise ValueError('Unsupported strategy')
return self
def transform(self, X):
X = pd.DataFrame(X).copy()
for col, val in self.statistics_.items():
if self.add_indicator:
X[col + '_is_missing'] = X[col].isnull().astype(int)
X[col] = X[col].fillna(val)
return X
3) 將離群值轉為 NaN 再進行填補
import numpy as np
import pandas as pd
def iqr_outlier_to_nan(series: pd.Series, k=1.5):
q1 = series.quantile(0.25)
q3 = series.quantile(0.75)
iqr = q3 - q1
lower = q1 - k * iqr
upper = q3 + k * iqr
mask = (series < lower) | (series > upper)
out = series.copy()
out[mask] = np.nan
return out
# 用法:
# df['feature_clean'] = iqr_outlier_to_nan(df['feature']).pipe(imputer.transform)
4) 使用 IsolationForest 偵測多變量異常
from sklearn.ensemble import IsolationForest
import pandas as pd
def fit_isolation_forest(X: pd.DataFrame, random_state=42):
iso = IsolationForest(n_estimators=200, contamination=0.01, random_state=random_state)
iso.fit(X)
X['outlier_score'] = iso.decision_function(X)
X['is_outlier'] = iso.predict(X) == -1
return X, iso
實用建議與常見陷阱
- 切勿在測試資料上重新計算填補統計量。
- 針對時間序列,僅以歷史資料填補。
- 將填補模型與主模型版本化。
- 對影響大的變數,考慮多重填補反映不確定性。
- 監控異常率與缺失率的變化。
延伸閱讀與結語
這些處理步驟是資料工程的基礎能力。推薦閱讀:
- Little & Rubin, Statistical Analysis with Missing Data
- van Buuren, Flexible Imputation of Missing Data
- sklearn.impute 官方文件
- Pandas 與 PyOD 套件說明
保持可重現、可監控的處理流程,是可靠數據基礎的關鍵。正如統計學家 George Box 所說:「所有模型都是錯的,但有些是有用的。」——而那些有用的模型,都始於乾淨、一致的數據。


