本文是一篇實用且面向生產環境的指南,說明如何在 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 所說:「所有模型都是錯的,但有些是有用的。」——而那些有用的模型,都始於乾淨、一致的數據。