返回目錄
A
數據洞察:從原始資料到策略決策的全流程分析 - 第 3 章
第 3 章 資料探索與前處理技巧
發布於 2026-02-24 17:02
# 第 3 章 資料探索與前處理技巧
資料探索(Exploratory Data Analysis, EDA)與前處理是數據科學流程中的「前哨」與「鋪路」。透過可視化、統計描述、缺失值與異常值處理,以及特徵工程,我們能把原始資料轉化為可被機器學習模型「理解」的結構化訊息。
---
## 3.1 數據可視化
| 目的 | 常用圖表 | 使用情境 |
|------|----------|----------|
| 探索分布 | Histogram、Kernel Density Estimation (KDE) | 檢查是否為正態分布、重複值 |
| 比較分組 | Boxplot、Violin plot | 分析各類別之差異 |
| 時序趨勢 | Line plot、Seasonal decomposition | 觀察季節性、長期趨勢 |
| 相關性 | Heatmap、Scatter plot matrix | 評估特徵間線性或非線性關聯 |
python
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
df = pd.read_csv('data.csv')
# Histogram
sns.histplot(df['age'], kde=True)
plt.title('年齡分布')
plt.show()
# Boxplot for categorical variable
sns.boxplot(x='gender', y='income', data=df)
plt.title('收入對比:男女')
plt.show()
# Correlation heatmap
corr = df.corr()
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('特徵相關矩陣')
plt.show()
> **實務提醒**:交互式可視化(如 Plotly、Bokeh)適用於大型資料集或多維分析,可提升探索速度。
---
## 3.2 統計描述
| 量度 | 公式/說明 | 何時使用 |
|------|-----------|----------|
| 均值、媒值、眾數 | `mean`, `median`, `mode` | 中心趨勢 |
| 標準差、變異數 | `std`, `var` | 散布幅度 |
| 四分位數、IQR | `quantile` | 判斷離群 |
| Skewness、Kurtosis | `skew`, `kurtosis` | 檢測分布形狀 |
python
summary = df.describe().T
summary['skew'] = df.skew()
summary['kurtosis'] = df.kurtosis()
print(summary)
> **小技巧**:對非連續型變數可直接用 `value_counts()` 觀察頻率分布。
---
## 3.3 缺失值處理
### 3.3.1 缺失機制
| 機制 | 例子 |
|------|------|
| MCAR (Missing Completely At Random) | 隨機抽樣漏填 | 可直接刪除 |
| MAR (Missing At Random) | 受觀測變數影響 | 需使用多變量補值 |
| MNAR (Missing Not At Random) | 與未觀測變數相關 | 需要專業判斷 |
### 3.3.2 補值方法
| 方法 | 主要工具 | 何時選擇 |
|------|----------|----------|
| 均值/媒值/眾數 | `SimpleImputer` | 連續/類別、缺失比例低 |
| 前向/後向填充 | `ffill`, `bfill` | 時序資料、缺失連續段 |
| KNN 補值 | `KNNImputer` | 高維度、非線性關聯 |
| 多重插補 (MICE) | `IterativeImputer` | 多變量、缺失比例中等 |
| 生成式模型 | `BayesianRidge`, `DNN` | 需求高精度補值 |
python
from sklearn.impute import SimpleImputer, KNNImputer
# 均值補值
imp_mean = SimpleImputer(strategy='mean')
df['salary'] = imp_mean.fit_transform(df[['salary']])
# KNN 補值
imp_knn = KNNImputer(n_neighbors=5)
X_imp = imp_knn.fit_transform(df[['age', 'salary', 'experience']])
> **注意**:補值前請先視覺化缺失模式,避免盲目補值導致偏差。
---
## 3.4 異常值檢測
| 方法 | 計算方式 | 典型用途 |
|------|----------|----------|
| IQR | 1.5*IQR | 連續型離群 |
| Z-Score | (x-μ)/σ | 連續型、正態分布 |
| DBSCAN | 密度聚類 | 高維、非線性離群 |
python
from scipy import stats
z_scores = stats.zscore(df['income'])
abs_z = np.abs(z_scores)
outliers = df[abs_z > 3]
print('離群數量:', outliers.shape[0])
> **實務**:異常值有時是真實業務事件(如欺詐),不可盲目剔除;先分析其來源再決策。
---
## 3.5 特徵工程
### 3.5.1 轉換與標準化
| 目的 | 方法 | 實例 |
|------|------|------|
| 消除偏態 | `np.log1p`, `np.sqrt`, `boxcox` | `log_income = np.log1p(df['income'])` |
| 量綱統一 | `StandardScaler`, `MinMaxScaler` | `X_scaled = scaler.fit_transform(X)` |
### 3.5.2 派生特徵
| 類型 | 技巧 | 典型例子 |
|------|------|----------|
| 多項式 | `PolynomialFeatures` | `age^2`, `age*experience` |
| 時間特徵 | `pd.to_datetime` | `hour`, `day_of_week`, `month` |
| 交叉特徵 | `Interaction` | `price*quantity` |
| 組合類別 | `Category Encoders` | `gender_age_group` |
python
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(df[['age', 'experience']])
### 3.5.3 編碼類別變數
| 編碼方式 | 何時適用 | 示例 |
|-----------|----------|------|
| One-Hot | 低基數 | `pd.get_dummies(df['city'])` |
| Label | 有序 | `LabelEncoder().fit_transform(df['grade'])` |
| Target | `target_encoder` | 根據目標變數的平均值編碼 |
| 嵌入 | `Embedding` (NN) | `user_id_embed = EmbeddingLayer(...)` |
> **最佳實踐**:將編碼放入 `ColumnTransformer`,確保同一套轉換可在測試集上重複。
---
## 3.6 資料分割
python
from sklearn.model_selection import train_test_split, StratifiedShuffleSplit
# 常規分割
X_train, X_temp, y_train, y_temp = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y)
# 最終測試集
X_valid, X_test, y_valid, y_test = train_test_split(
X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp)
> **時序數據**:使用 `TimeSeriesSplit` 或手動 `df.iloc[:int(len(df)*0.7)]` 等方法,保持時間順序。
---
## 3.6 資料管道(Pipeline)
python
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
numeric_features = ['age', 'income']
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler()),
])
categorical_features = ['city', 'gender']
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore')),
])
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features),
])
model_pipe = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', LogisticRegression())
])
model_pipe.fit(X_train, y_train)
> **好處**:Pipeline 保證 **所有** 步驟在訓練與測試期間一致,減少資料泄漏風險。
---
## 3.7 小結
| 步驟 | 目的 | 取捨要點 |
|------|------|----------|
| EDA | 初步判斷資料品質 | 先可視化,再做數值摘要 |
| 缺失/異常 | 消除噪音 | 補值前先探查模式 |
| 特徵工程 | 產生高維度訊息 | 依業務意義與模型需求平衡 |
| Pipeline | 一致性、可重複 | 讓開發流程可被版本控制 |
透過上述技巧,我們能把「雜亂」的原始資料,轉變為 **乾淨、可量化、可解釋** 的特徵集合,為後續的模型訓練與評估奠定堅實基礎。