聊天視窗

數據之鏡:從資料洞察到決策智慧 - 第 3 章

3. 資料清理與特徵工程

發布於 2026-02-25 18:19

# 3. 資料清理與特徵工程 在前一章,我們已經完成了資料的採集與 ETL 流程,確保資料能夠以結構化方式存放於資料倉儲中。接下來的關鍵步驟是 **資料清理**(Data Cleaning)與 **特徵工程**(Feature Engineering)。這兩個環節不僅能提升模型的精準度,還能降低維護成本與解讀難度。 ## 3.1 缺失值處理 ### 3.1.1 缺失值的來源 - **資料收集時遺漏**:設備停止、網路斷線、使用者未填寫。 - **ETL 過程中的錯誤**:API 回傳 404、爬蟲解析失敗。 - **資料轉換時的規則**:某些字段只在特定條件下才會出現。 ### 3.1.2 處理策略 | 策略 | 適用情境 | 代碼範例 | |------|----------|----------| | 刪除 | 缺失比例 < 5% 且不影響樣本分佈 | `df.dropna(axis=0, thresh=int(0.95*len(df)))` | | 觀測值填補 | 連續型數值 | `df['age'].fillna(df['age'].median(), inplace=True)` | | 分類值填補 | 類別型 | `df['gender'].fillna(df['gender'].mode()[0], inplace=True)` | | 先進填補 | 需要保持結構 | `SimpleImputer(strategy='mean')` | | 標記缺失 | 以便模型學習缺失模式 | `df['age_is_missing'] = df['age'].isna().astype(int)` | > **實務提醒**:在填補前請先使用 **缺失值可視化** 了解缺失分佈,避免「填補後假象」造成模型偏差。 ### 3.1.3 案例:線上零售交易資料 ```python import pandas as pd from sklearn.impute import SimpleImputer # 讀取交易資料 trades = pd.read_csv('sales_transactions.csv') # 查看缺失比例 print(trades.isnull().mean()) # 針對數值型填補中位數,類別型填補最常見值 numeric_imputer = SimpleImputer(strategy='median') categorical_imputer = SimpleImputer(strategy='most_frequent') trades[numeric_cols] = numeric_imputer.fit_transform(trades[numeric_cols]) trades[categorical_cols] = categorical_imputer.fit_transform(trades[categorical_cols]) ``` ## 3.2 離群值檢測 ### 3.2.1 離群值定義 離群值(Outlier)指在資料分布中明顯偏離大多數觀測值的數據點,可能是測量誤差、異常事件或真實極端情況。 ### 3.2.2 檢測方法 | 方法 | 描述 | 代碼範例 | |------|------|----------| | 箱型圖(Boxplot) | 使用四分位數識別 | `sns.boxplot(x=df['price'])` | | 標準差法 | | `z = (x - x.mean())/x.std(); df[z>3]` | | IQR 法 | | `Q1, Q3 = df.quantile([0.25, 0.75]); IQR = Q3-Q1; df[(df< Q1-1.5*IQR)|(df>Q3+1.5*IQR)]` | | Z-Score + Mahalanobis | 多變量 | `mahal = np.linalg.norm((X - X.mean(axis=0)) @ np.linalg.inv(cov), axis=1)` | | 機器學習 | Isolation Forest、One-Class SVM | `IsolationForest().fit(X)` | ### 3.2.3 處理策略 - **刪除**:當離群值佔比低且不影響模型表現。 - **轉換**:對數、Box-Cox 轉換減少極端值影響。 - **標記**:保留但以二元欄位標示,讓模型學習離群模式。 - **更正**:若是資料輸入錯誤,進行手動更正或使用校正值。 > **案例**:金融風險模型中的「交易金額」離群值往往代表大額異常交易,處理時需保留並加上風險因子。 ## 3.3 編碼技巧 ### 3.3.1 類別編碼 | 編碼類型 | 適用場景 | 優點 | 缺點 | |----------|----------|------|------| | One-Hot | 低基數 | 無序假設 | 高維度、稀疏 | | Ordinal | 自然序 | 低維度 | 需要實際序值 | | Target / Mean | 文字分類 | 捕捉目標相關性 | 可能導致資料洩漏 | | Frequency | 欄位頻率 | 代表普遍度 | 可能忽略特定關係 | | Target Encoding | 大基數 | 降低維度 | 必須分層訓練避免洩漏 | ### 3.3.2 連續型轉換 - **標準化**(Z-Score) ```python from sklearn.preprocessing import StandardScaler scaler = StandardScaler(); X_scaled = scaler.fit_transform(X) ``` - **最小-最大縮放** ```python from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(); X_norm = scaler.fit_transform(X) ``` - **Log / Box-Cox**:解決偏態分布。 ### 3.3.3 數值化技巧 | 技巧 | 目的 | 代碼 | |------|------|------| | 文本分詞 + TF-IDF | 提取關鍵詞 | `TfidfVectorizer()` | | 詞嵌入(Word2Vec) | 捕捉語義 | `gensim.models.Word2Vec()` | | 位置編碼 | 地理資訊 | `latitude/longitude` 轉成經緯度圓餅或球面坐標 | ## 3.4 特徵選擇 ### 3.4.1 為何需要特徵選擇 - **減少維度**:降低計算成本與過擬合風險。 - **提升可解釋性**:讓業務人員更易理解模型。 - **提升模型表現**:去除噪聲與不相關特徵。 ### 3.4.2 方法 | 方法 | 步驟 | 代碼 | |------|------|------| | 方差過濾(Variance Threshold) | 低方差特徵剔除 | `VarianceThreshold(threshold=0.01)` | | 卡方檢驗 | 分類特徵相關性 | `SelectKBest(chi2, k=10)` | | ANOVA F-test | 連續特徵相關性 | `SelectKBest(f_classif, k=10)` | | 互信息(Mutual Information) | 非線性關係 | `mutual_info_classif` | | L1 正則化 | 內建特徵選擇 | `LogisticRegression(penalty='l1')` | | Tree-based 重要性 | 連續/類別 | `RandomForestClassifier().feature_importances_` | | Recursive Feature Elimination (RFE) | 系統性剔除 | `RFE(estimator=RandomForestClassifier(), n_features_to_select=10)` | ### 3.4.3 實務流程示例 ```python from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.feature_selection import SelectKBest, chi2 from sklearn.ensemble import RandomForestClassifier from sklearn.pipeline import Pipeline # 1. 數據分割 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 2. 特徵標準化 scaler = StandardScaler(); X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) # 3. 特徵重要性評估 rf = RandomForestClassifier(random_state=42) rf.fit(X_train, y_train) importances = pd.Series(rf.feature_importances_, index=X.columns).sort_values(ascending=False) print(importances.head(15)) # 4. 選擇 top 15 X_train_selected = X_train[:, importances.nlargest(15).index] X_test_selected = X_test[:, importances.nlargest(15).index] ``` ## 3.5 典型工作流圖 > **圖 3.1**:資料清理與特徵工程的端到端流程 ``` +-----------------+ +-----------------+ +------------------+ | 來源資料庫 | --> | 缺失值處理 | --> | 離群值檢測/處理 | +-----------------+ +-----------------+ +------------------+ | | v v +-----------------+ +------------------+ | 類別編碼/標準化 | --> | 特徵選擇 | +-----------------+ +------------------+ | | v v +-----------------+ +------------------+ | 轉換後資料輸入 | --> | 模型訓練/評估 | +-----------------+ +------------------+ ``` ## 3.6 小結 1. **先確定缺失與離群值分佈**,再決定是否刪除或填補。 2. **選擇合適的編碼策略**,尤其對高基數類別特徵可考慮 **target encoding** 或 **hashing trick**。 3. **標準化/縮放** 為機器學習模型的預處理基礎。 4. **特徵選擇** 是提升模型可解釋性與效能的關鍵。可以採用 **樹模型重要性** 或 **L1 正則化** 的雙重檢驗。 > 透過上述步驟,我們不僅能將資料轉化為「乾淨」且「有意義」的數值向量,更能為後續的機器學習模型奠定堅實基礎。