返回目錄
A
金融數據科學實戰:從原始數據到智能投資 - 第 2 章
第2章 數據清洗與前處理
發布於 2026-03-06 11:44
# 第2章 數據清洗與前處理
金融數據科學的核心在於**資料品質**。即使模型再高級,若基礎資料含有錯誤、缺失或格式不一致,最終洞察亦可能失真。本章將帶領讀者掌握從原始金融時序資料到可直接輸入模型的完整流程:
1. **缺失值處理**
2. **異常值偵測與處理**
3. **時間戳對齊與頻率統一**
4. **特徵工程**:標準化、差分、滾動指標
5. **實務最佳實踐**
## 2.1 缺失值處理
在金融資料庫中,缺失值常見於以下情境:
| 情境 | 例子 |
|------|------|
| 股票停牌 | 交易日缺失收盤價 |
| 期貨交割 | 交割日缺失成交量 |
| 宏觀經濟 | 新公布數值尚未填入 |
### 2.1.1 缺失值檢測
python
import pandas as pd
# 讀取日頻股票數據
df = pd.read_csv('stock_prices.csv', parse_dates=['date'])
# 觀察缺失率
missing_rate = df.isna().mean()
print(missing_rate)
> **技巧**:對於時間序列資料,使用 `df['price'].isna().sum()` 直觀判斷缺失量;若缺失比例 > 5%,應先檢查數據源是否有遺漏。
### 2.1.2 常見填補方法
| 方法 | 說明 | 適用場景 |
|------|------|----------|
| 直前值填補(forward fill) | `df.fillna(method='ffill')` | 股票停牌、連續缺失期 |
| 直後值填補(backward fill) | `df.fillna(method='bfill')` | 短期數據恢復 |
| 均值/中位數填補 | `df['price'].fillna(df['price'].mean())` | 隨機缺失、數值分布較為對稱 |
| 差分填補 | `df['price_diff'].fillna(0)` | 變化量為主的特徵 |
| 插值(linear / cubic) | `df.interpolate(method='linear')` | 連續缺失較多的情況 |
> **實務建議**:對於「交易日缺失」常採用 forward fill;若缺失跨越停牌,則以上一個有效值填補並標記為「停牌」。
## 2.2 異常值偵測與處理
異常值會嚴重扭曲模型學習。以下提供兩種常用方法:
### 2.2.1 基於統計量
python
# 以收盤價為例
price = df['close']
q1, q3 = price.quantile([0.25, 0.75])
iqr = q3 - q1
lower, upper = q1 - 1.5 * iqr, q3 + 1.5 * iqr
mask = (price < lower) | (price > upper)
print('異常數量:', mask.sum())
### 2.2.2 基於時間序列模型
利用 ARIMA 或 Prophet 對每個股票進行短期預測,將預測值與實際值的殘差絕對值與 `3σ` 閾值比較。
python
from prophet import Prophet
model = Prophet()
model.fit(df[['date', 'close']].rename(columns={'date':'ds', 'close':'y'}))
forecast = model.predict(df[['date']].rename(columns={'date':'ds'}))
residual = df['close'] - forecast['yhat']
threshold = residual.abs().quantile(0.995)
mask = residual.abs() > threshold
print('異常數量:', mask.sum())
> **處理策略**:
> - **刪除**:若異常數量極少且不影響序列完整性。
> - **修正**:以滑動窗口平均或插值方法修復。
> - **標記**:保留原值並新增 `is_outlier` 欄位,便於模型辨識。
## 2.3 時間戳對齊與頻率統一
金融市場資料頻率多樣:
| 資料類型 | 頻率 | 範例 |
|--------|------|------|
| 股票 | 日 | 收盤價 |
| 期貨 | 15 分鐘 | 成交量 |
| 宏觀經濟 | 月 | CPI |
### 2.3.1 時間索引設定
python
# 以日頻股票資料為例
df.set_index('date', inplace=True)
# 確保索引為 Pandas DatetimeIndex
df.index = pd.to_datetime(df.index)
### 2.3.2 重新取樣(Resample)
python
# 取 15 分鐘頻率並計算平均成交價
df_15m = df.resample('15T').agg({'price': 'mean', 'volume': 'sum'})
# 填補 NaN(因週末停盤)
df_15m.ffill(inplace=True)
> **注意**:重取樣時要考慮**非交易時段**與**交易日曆**,可使用 `pandas.tseries.offsets.CustomBusinessDay` 或專業交易日曆庫 `pandas_market_calendars`。
## 2.4 特徵工程
特徵工程是將原始資料轉化為模型友好的表達方式。以下列出三種核心技巧。
### 2.4.1 標準化(Standardization)
python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# 假設已處理好缺失與異常值
X_scaled = scaler.fit_transform(df[['close', 'volume']])
> **說明**:標準化可使不同量綱的特徵共處於同一尺度,對大部分機器學習演算法(尤其線性模型、SVM)有明顯提升。
### 2.4.2 差分(Differencing)
差分可消除趨勢,轉為平穩序列,亦常用於技術指標。
python
# 一階差分
df['close_diff'] = df['close'].diff()
# 移除 NaN
df.dropna(subset=['close_diff'], inplace=True)
> **用途**:預測收益率、波動率、技術指標(RSI、MACD)。
### 2.4.3 滾動指標(Rolling Statistics)
python
# 20 日簡單移動平均
df['sma20'] = df['close'].rolling(window=20).mean()
# 20 日標準差
df['std20'] = df['close'].rolling(window=20).std()
# 生成 Bollinger Bands
df['bb_upper'] = df['sma20'] + 2 * df['std20']
df['bb_lower'] = df['sma20'] - 2 * df['std20']
> **說明**:滾動指標是金融市場的「技術分析」核心,可捕捉短期波動、趨勢轉折。
## 2.5 實務最佳實踐
| 原則 | 具體做法 |
|------|----------|
| **可追溯性** | 建立 ETL 日誌,記錄每次清洗腳本、時間、版本。
| **資料版本控制** | 以 S3、GCS 或資料庫分區存儲,命名方式含日期與版本號。
| **自動化** | 使用 Airflow / Prefect 將清洗流程排程,失敗自動通知。
| **效能** | 在 Spark / Dask 上處理 > 10M 條記錄,或使用 Pandas `categorical` 優化內存。
| **安全** | 加密敏感欄位(如客戶 ID),對於合規需符合 GDPR / 個資法。
## 2.6 小結
- **缺失值**:根據缺失機制選擇適當填補方法;避免盲目使用全域均值。
- **異常值**:結合統計與模型判斷,並留存 `is_outlier` 標記。
- **時間對齊**:使用專業交易日曆,正確重取樣,避免非交易時段造成噪聲。
- **特徵工程**:標準化、差分與滾動指標為金融數據的三大基礎。
- **流程管理**:建立可追溯、版本化與自動化的清洗流水線,確保資料品質。
接下來,我們將進入**描述性統計與探索性分析**,透過圖形與相關係數矩陣,發掘市場情緒與潛在套利機會。