返回目錄
A
洞察數據:從數據科學家到策略決策者的轉型指南 - 第 3 章
第3章 資料前處理與清洗
發布於 2026-03-01 00:13
# 第3章 資料前處理與清洗
資料前處理是數據科學流程中不可或缺的環節。它不僅是提升模型效能的關鍵,也是確保商業洞察可信度的基石。本章將深入探討缺失值處理、異常偵測與資料轉型三大面向,並提供實際工具與程式範例,協助讀者在真實業務場景中快速落地。
---
## 3.1 缺失值處理
### 3.1.1 缺失值概念
- **Missing Data**:資料在收集或傳輸過程中,因各種原因未被儲存的空缺位置。
- **Types**:
- *Missing Completely at Random (MCAR)*:缺失機率與其他變量無關。
- *Missing at Random (MAR)*:缺失機率只與已觀測變量相關。
- *Missing Not at Random (MNAR)*:缺失機率與未觀測值本身相關。
### 3.1.2 缺失值影響
1. **統計推論偏差**:平均值、標準差等統計量受缺失資料影響。
2. **模型效能下降**:機器學習模型需對缺失進行處理,否則訓練失敗或效能低下。
3. **決策失真**:商業報表可能因缺失而產生錯誤結論。
### 3.1.3 處理策略
| 方法 | 何時適用 | 優缺點 |
|---|---|---|
| **刪除** (Complete Case / Listwise) | 缺失比例低 (<5%) | 簡單快速,保持資料完整性;但會喪失樣本量 |
| **單值代入** (Mean/Median/Mode) | 缺失比例中等 | 易實作;但會降低變異性,影響模型特徵 |
| **預測填補** (KNN, MICE, Regression) | 缺失比例高或資料分布複雜 | 保留資料量,擬合較準確;計算成本較高 |
| **分組填補** (Segment-based) | 變量具有分群特徵 | 可以保留群組內差異 |
### 3.1.4 實作範例(Python / pandas)
```python
import pandas as pd
import numpy as np
from sklearn.impute import SimpleImputer, KNNImputer
# 讀取範例資料
df = pd.read_csv('sample_data.csv')
# 1. 刪除缺失行
clean_df = df.dropna(subset=['target']) # 只保留目標欄完整的樣本
# 2. 單值代入
imputer_mean = SimpleImputer(strategy='mean')
df['numeric_col'] = imputer_mean.fit_transform(df[['numeric_col']])
# 3. KNN 代入
knn_imputer = KNNImputer(n_neighbors=5)
df[['col1','col2','col3']] = knn_imputer.fit_transform(df[['col1','col2','col3']])
```
> **實務提醒**:在進行填補前,先利用 `df.isnull().sum()` 觀察缺失比例,並根據業務邏輯決定是否需要保留缺失值(例如客戶流失預測中,缺失的「最近一次交易時間」可能代表活躍度低)。
---
## 3.2 異常偵測(Outlier Detection)
### 3.2.1 異常定義
- **Outlier**:在資料分布中,與大多數資料差距過大的觀測值。
- **Types**:
- *Point outlier*:單一變量值偏離。
- *Contextual outlier*:同一變量在不同上下文(時間、地點)異常。
- *Collective outlier*:整個樣本或群組異常。
### 3.2.2 為何重要
1. **模型過擬合**:異常可能被模型過度學習,影響泛化能力。
2. **數據品質**:異常往往代表輸入錯誤或測量問題。
3. **商業洞察**:某些異常是重要業務信號(例如金融詐騙、設備故障)。
### 3.2.3 常見方法
| 方法 | 原理 | 適用情境 |
|---|---|---|
| **統計方法** (Z-score, IQR) | 以分佈統計量判定閾值 | 數值型、近正態分佈 |
| **距離方法** (Mahalanobis, kNN distance) | 觀測值與鄰近點距離 | 低維數據、聚類前期 |
| **密度方法** (DBSCAN, LOF) | 以點密度判斷離群 | 高維、非凸分佈 |
| **模型方法** (Isolation Forest, One-Class SVM) | 以異常分離原理 | 大規模、非線性關係 |
### 3.2.4 實作範例(Python / scikit‑learn)
```python
from sklearn.ensemble import IsolationForest
import pandas as pd
# 讀取資料
X = pd.read_csv('sales_data.csv')[['amount', 'discount']] # 兩個數值特徵
# Isolation Forest
iso = IsolationForest(contamination=0.05, random_state=42)
X['anomaly'] = iso.fit_predict(X)
X['anomaly'] = X['anomaly'].map({1:0, -1:1}) # 0:正常, 1:異常
# 篩選異常
anomalies = X[X['anomaly']==1]
print('偵測到', anomalies.shape[0], '筆異常資料')
```
> **實務技巧**:在異常偵測前,先做資料正規化或標準化,避免尺度差異影響距離或密度計算。
---
## 3.3 資料轉型技巧與工具
### 3.3.1 轉型需求
1. **資料一致性**:同一欄位使用統一格式(日期、貨幣、文字大小寫)。
2. **特徵工程**:建立有利於模型表現的新特徵(如對數轉換、交互項)。
3. **數據型別轉換**:將字串轉為類別型、數值型,或將數值型轉為分桶型。
### 3.3.2 常用轉型操作
| 操作 | 說明 | 範例 |
|---|---|---|
| **正規化** (Min‑Max, Z‑Score) | 將數值縮放到相同範圍 | `scaled = (x - x.min()) / (x.max() - x.min())` |
| **離散化** (分箱) | 將連續變量轉為離散類別 | `pd.cut(df['age'], bins=[0,18,35,60,100], labels=['青少年','青年','中年','老年'])` |
| **特徵衍生** | 生成新的有用特徵 | `df['total_spent'] = df['price'] * df['quantity']` |
| **日期拆分** | 轉成年、月、日、週、季 | `df['year'] = df['date'].dt.year` |
| **文本向量化** | 轉成數值向量 | `TfidfVectorizer`、`CountVectorizer` |
| **類別編碼** | One-Hot、Label、Target Encoding | `pd.get_dummies(df['color'])` |
### 3.3.3 轉型工具與框架
| 工具 | 優勢 | 適用範圍 |
|---|---|---|
| **pandas** | 易用、靈活 | 小到中等規模資料 |
| **PySpark** | 分散式運算、處理大數據 | 大規模資料、雲端批次作業 |
| **scikit‑learn** (Pipeline, ColumnTransformer) | 內建特徵轉換器、可連結至模型 | 機器學習工作流 |
| **Featuretools** | 自動化特徵工程 | 時間序列、關聯式資料 |
| **Databricks** | 整合Spark + MLflow | 雲端資料湖、模型追蹤 |
### 3.3.4 轉型實作案例(Pipeline)
```python
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.pipeline import Pipeline
import pandas as pd
# 範例資料
X = pd.read_csv('customer_data.csv')
numeric_features = ['age', 'income', 'score']
categorical_features = ['gender', 'region']
numeric_transformer = Pipeline([('scaler', StandardScaler())])
categorical_transformer = Pipeline([('onehot', OneHotEncoder(handle_unknown='ignore'))])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
# 整合進模型(以隨機森林為例)
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
clf = Pipeline([
('preprocess', preprocessor),
('model', RandomForestClassifier(n_estimators=200, random_state=42))
])
# 拆分訓練/測試
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X.drop('target', axis=1), X['target'], test_size=0.2, random_state=42)
clf.fit(X_train, y_train)
print('測試準確率:', clf.score(X_test, y_test))
```
> **實務建議**:在建立 Pipeline 前,先對每個欄位做 **EDA**,確定數值型 vs 類別型,避免誤用編碼方式。
---
## 3.4 綜合實戰:從原始資料到乾淨特徵
### 3.4.1 預處理工作流程
1. **資料蒐集**:從資料湖/倉庫或外部 API 抽取。
2. **初步探索**:使用 `df.describe()`、`df.info()` 觀察結構與缺失。
3. **缺失處理**:根據缺失機制選擇合適方法。
4. **異常偵測**:剔除或標記異常值。
5. **轉型**:標準化、編碼、衍生特徵。
6. **資料分割**:訓練/驗證/測試。
7. **版本控制**:使用 Data Version Control (DVC) 或 MLflow 追蹤。
### 3.4.2 範例:零售客戶流失預測
```python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import roc_auc_score
# 讀取原始資料
raw = pd.read_csv('retail_customer.csv')
# 1. 缺失處理
numeric_features = ['age', 'income', 'purchase_amount']
categorical_features = ['gender', 'membership_level']
numeric_transformer = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
categorical_transformer = Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
preprocessor = ColumnTransformer([
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
# 2. 模型
clf = Pipeline([
('preprocess', preprocessor),
('model', GradientBoostingClassifier(n_estimators=300, learning_rate=0.05, max_depth=3, random_state=42))
])
# 3. 分割
X = raw.drop('churn', axis=1)
y = raw['churn']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
# 4. 訓練
clf.fit(X_train, y_train)
# 5. 評估
pred_proba = clf.predict_proba(X_test)[:,1]
print('AUC:', roc_auc_score(y_test, pred_proba))
```
> **關鍵點**:在此流程中,所有轉型步驟都被封裝於 Pipeline,確保 **資料處理一致性**,並能在不同資料集(如 A/B 測試、上線環境)保持相同操作。
---
## 3.5 小結
1. **缺失值處理**:根據缺失機制選擇刪除、單值代入或預測填補;使用 pandas、scikit‑learn 等工具快速實作。
2. **異常偵測**:Z‑score、IQR、Isolation Forest 等方法可根據資料分佈與業務需求選擇;異常往往既是噪音也可能是商業機會。
3. **資料轉型**:標準化、離散化、特徵衍生與類別編碼是提升模型表現的關鍵;Pipeline 方式可確保一致性與重現性。
4. **實務流程**:從資料蒐集、探索、處理、轉型到模型,形成一個可重複、可追蹤的工作流;結合版本控制與雲端資料湖可提升團隊協作效率。
> **未來展望**:隨著資料量與多樣性不斷擴大,**自動化特徵工程**(Featuretools)與**資料治理**工具(Great Expectations)將成為前線開發者的常用武器。掌握本章所述方法,您將能在任何業務場景中,將原始雜亂的資料轉化為乾淨、可被機器學習模型所理解的特徵,為後續建模與策略決策奠定堅實基礎。