返回目錄
A
掌握時序預測:Python 與統計學的實務指南 - 第 2 章
第 2 章:Python 時序資料工具
發布於 2026-02-21 11:21
# 第 2 章:Python 時序資料工具
> 本章將深入探討 Python 生態系中處理時序資料的核心工具:**Pandas**、**NumPy**、**Matplotlib** 與 **Seaborn**。透過實際範例與最佳實踐,讀者將能快速將原始時間序列資料轉為可供分析、可視化與建模的結構。
---
## 2.1 Pandas 時序資料結構與操作
### 2.1.1 `DatetimeIndex`、`PeriodIndex` 與 `TimedeltaIndex`
| 物件 | 主要用途 | 典型範例 |
|------|----------|----------|
| `DatetimeIndex` | 以秒級(或更高)為基礎的時間點索引 | `pd.date_range(start='2020-01-01', periods=100, freq='D')` |
| `PeriodIndex` | 以「期間」為基礎(如月、季) | `pd.period_range(start='2020-01', periods=12, freq='M')` |
| `TimedeltaIndex` | 表示兩個時間點之差 | `pd.timedelta_range(start='0 days', periods=5, freq='H')` |
> **注意**:`DatetimeIndex` 允許對時間進行「對齊(align)」、「重採樣(resample)」與「頻率轉換」;`PeriodIndex` 更適合處理「月度報表」或「季度財報」;`TimedeltaIndex` 常用於「滯後特徵」與「時間差」的計算。
### 2.1.2 建立時序資料集
```python
import pandas as pd
import numpy as np
# 1. 產生日序列
dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
# 2. 產生對應的隨機數值(例如每日銷售額)
np.random.seed(42)
values = np.random.poisson(lam=200, size=len(dates))
# 3. 建立 DataFrame
sales = pd.DataFrame({'date': dates, 'sales': values})
# 將 date 設為索引
sales.set_index('date', inplace=True)
print(sales.head())
```
輸出示例:
```
sales
date
2023-01-01 179
2023-01-02 213
2023-01-03 195
2023-01-04 210
2023-01-05 197
```
### 2.1.3 常用操作
| 操作 | 目的 | 範例 |
|------|------|------|
| `df.resample()` | 重新採樣(例如將日資料轉為週資料) | `weekly = sales.resample('W').sum()` |
| `df.shift()` | 計算滯後值 | `sales['lag1'] = sales['sales'].shift(1)` |
| `df.asfreq()` | 填補缺失頻率 | `sales = sales.asfreq('D')` |
| `df.between_time()` | 擷取指定時間段 | `morning = df.between_time('08:00', '12:00')` |
| `df.tz_localize()/tz_convert()` | 時區處理 | `df.tz_localize('UTC').tz_convert('Asia/Taipei')` |
### 2.1.4 索引重設與切片
```python
# 重設索引為整數
sales_reset = sales.reset_index()
print(sales_reset.head())
# 切片操作
# 1) 取 2023-03-01 到 2023-03-31 的資料
march_sales = sales.loc['2023-03-01':'2023-03-31']
# 2) 取 2023 年前 100 天
first_100 = sales.iloc[:100]
```
### 2.1.5 合併與連接
| 方法 | 何時使用 | 範例 |
|------|----------|------|
| `pd.concat()` | 垂直或水平拼接 | `pd.concat([df1, df2])` |
| `df.merge()` | SQL join | `df1.merge(df2, on='id', how='left')` |
| `df.join()` | 基於索引的簡易 join | `df1.join(df2)` |
> **實務提醒**:在合併多個時序資料時,務必先確認索引的頻率與時區,避免「跨時區重疊」造成資料錯位。
---
## 2.2 NumPy:基礎數值運算與時間序列
### 2.2.1 時間序列數值化
```python
# 產生 1 年每日日期
np_dates = np.arange('2023-01-01', '2024-01-01', dtype='datetime64[D]')
print(np_dates[:5]) # 2023-01-01, 2023-01-02, ...
# 轉為秒數(自 1970-01-01 以來的秒)
seconds = np_dates.astype('datetime64[s]').astype(int)
print(seconds[:5])
```
### 2.2.2 週期性函式
```python
# 生成 2 週的正弦波,週期為 14 天
period = 14
days = np.arange(0, 30)
sin_wave = np.sin(2 * np.pi * days / period)
print(sin_wave)
```
> **應用**:`numpy.sin()` 與 `numpy.cos()` 可直接用於建立「季節性滯後特徵」或「波形預測」時的基礎函式。
---
## 2.3 Matplotlib 與 Seaborn:時間序列可視化技巧
### 2.3.1 基本折線圖
```python
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 4))
sales.plot(kind='line', title='Daily Sales (2023)')
plt.ylabel('Sales')
plt.xlabel('Date')
plt.tight_layout()
plt.show()
```
### 2.3.2 重新採樣視覺化
```python
# 週總銷售額
weekly_sum = sales.resample('W').sum()
plt.figure(figsize=(10, 3))
weekly_sum.plot(title='Weekly Total Sales')
plt.show()
```
### 2.3.3 季節性子圖(Seasonal Decomposition)
```python
from pandas.plotting import register_matplotlib_converters
register_matplotlib_converters() # 兼容 pandas 時間索引
# 以月為單位的季節性圖
monthly = sales.resample('M').sum()
fig, ax = plt.subplots(3, 4, figsize=(15, 8), sharex=True)
for i, month in enumerate(monthly.index):
ax[i//4, i%4].bar(month, monthly['sales'], width=1)
ax[i//4, i%4].set_title(str(month))
ax[i//4, i%4].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
```
### 2.3.4 自動相關圖 (ACF)
```python
from pandas.plotting import lag_plot
lag_plot(sales['sales'], lag=1, title='Lag‑1 Plot')
plt.show()
```
> **解釋**:ACF(自動相關函式)可視化資料在不同滯後下的相關性,對於檢查「隨機游走」或「季節性」特徵尤為重要。
### 2.3.5 Seaborn 風格化折線圖
```python
import seaborn as sns
sns.set_style('whitegrid')
# 以 Seaborn 的 FacetGrid 對不同月份的折線進行子圖顯示
sales_reset = sales.reset_index()
sales_reset['month'] = sales_reset['date'].dt.month
sns.lineplot(data=sales_reset, x='date', y='sales', hue='month')
plt.title('Monthly Sales Trend with Seaborn')
plt.show()
```
---
## 2.4 進階可視化:從原始資料到分析洞察
### 2.4.1 熱度圖(Heatmap) - 週/日時間結構
```python
# 建立一個 7 天 × 24 小時的數據矩陣(假設每小時銷售)
hours = np.arange(24)
weeks = np.arange(7)
heat_data = np.random.poisson(lam=10, size=(7, 24))
plt.figure(figsize=(8, 5))
sns.heatmap(heat_data, cmap='viridis', xticklabels=hours, yticklabels=['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'])
plt.title('Weekly Hourly Sales Heatmap')
plt.xlabel('Hour of Day')
plt.ylabel('Day of Week')
plt.show()
```
> **洞察**:熱度圖能快速揭露「高峰時段」與「週末效應」。
### 2.4.2 分布圖(Distribution)與箱型圖(Boxplot)
```python
# 資料分布
sns.histplot(sales['sales'], bins=30, kde=True)
plt.title('Sales Distribution')
plt.xlabel('Sales')
plt.show()
# 箱型圖:各月銷售箱型圖
monthly_sales = sales.resample('M').sum()
monthly_sales.plot(kind='box', title='Monthly Sales Boxplot')
plt.show()
```
### 2.4.3 互補函式:`pandas.plotting` 模組
```python
from pandas.plotting import autocorrelation_plot
autocorrelation_plot(sales['sales'])
plt.title('Autocorrelation of Sales')
plt.show()
```
---
## 2.5 版本與環境建議
| 套件 | 建議版本 | 安裝指令 |
|------|----------|----------|
| Pandas | ≥ 2.0 | `pip install pandas>=2.0` |
| NumPy | ≥ 1.26 | `pip install numpy>=1.26` |
| Matplotlib | ≥ 3.8 | `pip install matplotlib>=3.8` |
| Seaborn | ≥ 0.13 | `pip install seaborn>=0.13` |
> 建議使用 **Python 3.10+** 以確保 `datetime64` 的完整功能,並使用 **JupyterLab** 或 **VSCode + Jupyter** 進行交互式開發。
---
## 2.6 小結
在本章中,我們掌握了以下關鍵能力:
1. **Pandas**:建立、索引、重採樣、合併與特徵工程;
2. **NumPy**:數值化時間、生成週期性波形;
3. **Matplotlib & Seaborn**:創建折線、子圖、熱度圖與自動相關圖。
這些工具為後續章節(模型建立、參數調整、模型評估)奠定了堅實的基礎。
---
> **實作練習**:
>
> 1. 下載 Kaggle `daily-sales` 資料集,重建 `DatetimeIndex`,並使用 `resample('W')` 生成每週銷售總額。
> 2. 利用 `statsmodels.tsa.stattools.adfuller` 進行單位根檢定前,先將資料做 7 天滑動平均。
> 3. 使用 Seaborn 的 `pairplot` 對 `sales` 與滯後特徵做可視化探索。
>
> (提示:可使用 `pip install -U statsmodels` 取得最新 ADF 版本。)