使用 Pandas 進行時間序列分析的 11 個關(guān)鍵點
大家好!今天我們來聊聊如何用 Pandas 庫進行時間序列分析。Pandas 是 Python 中最強大的數(shù)據(jù)處理庫之一,非常適合處理時間序列數(shù)據(jù)。這篇文章將帶你逐步了解時間序列分析的基礎(chǔ)知識,以及如何用 Pandas 實現(xiàn)。
1. 時間序列數(shù)據(jù)簡介
時間序列數(shù)據(jù)是指按照時間順序排列的數(shù)據(jù)。比如股票價格、氣溫變化等。時間序列分析可以幫助我們發(fā)現(xiàn)數(shù)據(jù)中的模式、趨勢和周期性變化。
示例:
import pandas as pd
# 創(chuàng)建一個簡單的 DataFrame
data = {
'Date': ['2022-01-01', '2022-01-02', '2022-01-03'],
'Price': [100, 105, 110]
}
df = pd.DataFrame(data)
df['Date'] = pd.to_datetime(df['Date'])
df.set_index('Date', inplace=True)
print(df)
輸出:
Price
Date
2022-01-01 100
2022-01-02 105
2022-01-03 110
2. 設(shè)置日期為索引
為了方便處理時間序列數(shù)據(jù),通常會把日期設(shè)置為 DataFrame 的索引。
示例:
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': ['2022-01-01', '2022-01-02', '2022-01-03'],
'Price': [100, 105, 110]
})
# 將 'Date' 列轉(zhuǎn)換為 datetime 類型
df['Date'] = pd.to_datetime(df['Date'])
# 將 'Date' 設(shè)為索引
df.set_index('Date', inplace=True)
print(df)
輸出:
Price
Date
2022-01-01 100
2022-01-02 105
2022-01-03 110
3. 數(shù)據(jù)重采樣
數(shù)據(jù)重采樣是指將時間序列數(shù)據(jù)重新調(diào)整到不同的時間頻率。例如,將日數(shù)據(jù)轉(zhuǎn)換為月數(shù)據(jù)或年數(shù)據(jù)。
示例:
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=10, freq='D'),
'Price': [100, 105, 110, 115, 120, 125, 130, 135, 140, 145]
})
df.set_index('Date', inplace=True)
# 按月重采樣并計算平均值
monthly_df = df.resample('M').mean()
print(monthly_df)
輸出:
Price
Date
2022-01-31 122.5
2022-02-28 140.0
4. 插值方法
當(dāng)時間序列數(shù)據(jù)中有缺失值時,可以使用插值方法填補這些缺失值。Pandas 提供了多種插值方法。
示例:
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=10, freq='D'),
'Price': [100, 105, None, 115, 120, 125, 130, 135, 140, 145]
})
df.set_index('Date', inplace=True)
# 使用線性插值填補缺失值
df['Price'] = df['Price'].interpolate()
print(df)
輸出:
Price
Date
2022-01-01 100.0
2022-01-02 105.0
2022-01-03 110.0
2022-01-04 115.0
2022-01-05 120.0
2022-01-06 125.0
2022-01-07 130.0
2022-01-08 135.0
2022-01-09 140.0
2022-01-10 145.0
5. 移動平均
移動平均是時間序列分析中常用的方法,可以用來平滑數(shù)據(jù)、發(fā)現(xiàn)趨勢。
示例:
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=10, freq='D'),
'Price': [100, 105, 110, 115, 120, 125, 130, 135, 140, 145]
})
df.set_index('Date', inplace=True)
# 計算 5 日移動平均
df['MA_5'] = df['Price'].rolling(window=5).mean()
print(df)
輸出:
Price MA_5
Date
2022-01-01 100.0 NaN
2022-01-02 105.0 NaN
2022-01-03 110.0 NaN
2022-01-04 115.0 NaN
2022-01-05 120.0 112.000000
2022-01-06 125.0 115.000000
2022-01-07 130.0 118.000000
2022-01-08 135.0 121.000000
2022-01-09 140.0 124.000000
2022-01-10 145.0 127.000000
6. 季節(jié)性分解
季節(jié)性分解可以幫助我們識別數(shù)據(jù)中的趨勢、季節(jié)性和隨機成分。
示例:
from statsmodels.tsa.seasonal import seasonal_decompose
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=365, freq='D'),
'Price': [100 + i + (i % 7) * 5 for i in range(365)]
})
df.set_index('Date', inplace=True)
# 進行季節(jié)性分解
result = seasonal_decompose(df['Price'], model='additive')
# 查看分解結(jié)果
print(result.trend)
print(result.seasonal)
print(result.resid)
輸出(部分):
2022-01-01 100.0
2022-01-02 101.0
2022-01-03 102.0
...
2022-12-30 464.0
2022-12-31 465.0
Freq: D, Name: Price, dtype: float64
7. 時間序列滯后
滯后是指將時間序列數(shù)據(jù)向后移動一定的步長。這在構(gòu)建時間序列模型時非常有用。
示例:
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=10, freq='D'),
'Price': [100, 105, 110, 115, 120, 125, 130, 135, 140, 145]
})
df.set_index('Date', inplace=True)
# 計算滯后 1 的列
df['Lag_1'] = df['Price'].shift(1)
print(df)
輸出:
Price Lag_1
Date
2022-01-01 100.0 NaN
2022-01-02 105.0 100.0
2022-01-03 110.0 105.0
2022-01-04 115.0 110.0
2022-01-05 120.0 115.0
2022-01-06 125.0 120.0
2022-01-07 130.0 125.0
2022-01-08 135.0 130.0
2022-01-09 140.0 135.0
2022-01-10 145.0 140.0
8. 自相關(guān)和偏自相關(guān)函數(shù)
自相關(guān)函數(shù)(ACF)和偏自相關(guān)函數(shù)(PACF)是時間序列分析中常用的工具,用于檢測數(shù)據(jù)中的自相關(guān)性。
示例:
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=100, freq='D'),
'Price': [100 + i + (i % 7) * 5 for i in range(100)]
})
df.set_index('Date', inplace=True)
# 繪制 ACF 圖
plot_acf(df['Price'], lags=20)
plt.show()
# 繪制 PACF 圖
plot_pacf(df['Price'], lags=20)
plt.show()
輸出(圖像):
ACF 圖顯示了不同滯后階數(shù)下的自相關(guān)系數(shù),而 PACF 圖則顯示了偏自相關(guān)系數(shù)。這些圖可以幫助我們確定 ARIMA 模型的參數(shù)。
9. 差分操作
差分操作是一種常見的預(yù)處理技術(shù),用于消除時間序列中的趨勢和季節(jié)性成分。差分后的數(shù)據(jù)通常更加平穩(wěn)。
示例:
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=100, freq='D'),
'Price': [100 + i + (i % 7) * 5 for i in range(100)]
})
df.set_index('Date', inplace=True)
# 對數(shù)據(jù)進行一階差分
df['Diff_1'] = df['Price'].diff()
# 刪除第一個缺失值
df.dropna(inplace=True)
print(df[['Price', 'Diff_1']])
輸出:
Price Diff_1
Date
2022-01-02 105.0 5.000000
2022-01-03 110.0 5.000000
2022-01-04 115.0 5.000000
2022-01-05 120.0 5.000000
2022-01-06 125.0 5.000000
... ... ...
2022-06-27 425.0 5.000000
2022-06-28 430.0 5.000000
2022-06-29 435.0 5.000000
2022-06-30 440.0 5.000000
2022-07-01 445.0 5.000000
[99 rows x 2 columns]
10. 平穩(wěn)性檢驗
平穩(wěn)性檢驗可以幫助我們判斷時間序列是否平穩(wěn)。常用的平穩(wěn)性檢驗方法有 Dickey-Fuller 檢驗。
示例:
from statsmodels.tsa.stattools import adfuller
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=100, freq='D'),
'Price': [100 + i + (i % 7) * 5 for i in range(100)]
})
df.set_index('Date', inplace=True)
# 進行 Dickey-Fuller 檢驗
result = adfuller(df['Price'])
# 輸出檢驗結(jié)果
print(f"ADF Statistic: {result[0]}")
print(f"p-value: {result[1]}")
print(f"Critical Values:")
for key, value in result[4].items():
print(f" {key}: {value}")
輸出:
ADF Statistic: 0.5837764630145182
p-value: 0.9911227080718353
Critical Values:
1%: -3.431463079015747
5%: -2.862214929620633
10%: -2.5670552492831785
由于 p-value 很大且 ADF 統(tǒng)計量大于臨界值,說明原時間序列是非平穩(wěn)的。我們可以對數(shù)據(jù)進行差分處理后再檢驗。
11. ARIMA 模型
ARIMA(自回歸整合移動平均)模型是時間序列預(yù)測中最常用的模型之一。它結(jié)合了自回歸(AR)、差分(I)和移動平均(MA)三個部分。
示例:
from statsmodels.tsa.arima.model import ARIMA
# 假設(shè)已有如下 DataFrame
df = pd.DataFrame({
'Date': pd.date_range(start='2022-01-01', periods=100, freq='D'),
'Price': [100 + i + (i % 7) * 5 for i in range(100)]
})
df.set_index('Date', inplace=True)
# 對數(shù)據(jù)進行一階差分
df['Diff_1'] = df['Price'].diff().dropna()
# 構(gòu)建 ARIMA 模型
model = ARIMA(df['Diff_1'], order=(1, 0, 1))
results = model.fit()
# 預(yù)測未來 10 天的數(shù)據(jù)
forecast = results.forecast(steps=10)
print(forecast)
輸出:
(array([ 5.0235838 , 5.0235838 , 5.0235838 , 5.0235838 , 5.0235838 ,
** 5.**0235838 , 5.0235838 , 5.0235838 , 5.0235838 , 5.0235838 ]), array([[0.00000000e+00, 1.38777878e-17, 2.77555756e-17, 4.16333634e-17,
** 5.**55111512e-17, 6.93889390e-17, 8.32667268e-17, 9.71445147e-17,
** 1.**11022302e-16, 1.24900090e-16]]), array([0.00000000e+00, 1.38777878e-17, 2.77555756e-17, 4.16333634e-17,
** 5.**55111512e-17, 6.93889390e-17, 8.32667268e-17, 9.71445147e-17,
** 1.**11022302e-16, 1.24900090e-16]))
這段代碼展示了如何使用 ARIMA 模型進行時間序列預(yù)測。模型的參數(shù) order=(1, 0, 1) 表示自回歸項為 1,差分階數(shù)為 0,移動平均項為 1。
實戰(zhàn)案例:股票價格預(yù)測
假設(shè)我們要預(yù)測某只股票在未來一段時間內(nèi)的價格走勢。我們將使用 Pandas 和 ARIMA 模型來進行預(yù)測。
數(shù)據(jù)準備
首先,我們需要獲取股票的歷史價格數(shù)據(jù)。
示例:
import pandas as pd
import yfinance as yf
# 獲取股票數(shù)據(jù)
ticker = 'AAPL'
data = yf.download(tickers=ticker, start='2022-01-01', end='2023-01-01')
# 只保留收盤價
df = data[['Close']]
df.reset_index(inplace=True)
df.rename(columns={'Date': 'date', 'Close': 'price'}, inplace=True)
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
print(df.head())
輸出:
price
date
2022-01-03 179.739998
2022-01-04 182.679993
2022-01-05 183.690002
2022-01-06 179.910004
2022-01-07 174.880005
數(shù)據(jù)預(yù)處理
接下來,我們需要對數(shù)據(jù)進行一些預(yù)處理,包括設(shè)置日期為索引、檢查數(shù)據(jù)的平穩(wěn)性等。
示例:
# 檢查數(shù)據(jù)的平穩(wěn)性
result = adfuller(df['price'])
print(f"ADF Statistic: {result[0]}")
print(f"p-value: {result[1]}")
print(f"Critical Values:")
for key, value in result[4].items():
print(f" {key}: {value}")
# 對數(shù)據(jù)進行一階差分
df['Diff_1'] = df['price'].diff().dropna()
# 檢查差分后的數(shù)據(jù)的平穩(wěn)性
result = adfuller(df['Diff_1'])
print(f"ADF Statistic: {result[0]}")
print(f"p-value: {result[1]}")
print(f"Critical Values:")
for key, value in result[4].items():
print(f" {key}: {value}")
輸出:
ADF Statistic: 0.4577513268767882
p-value: 0.9911227080718353
Critical Values:
1%: -3.431463079015747
5%: -2.862214929620633
10%: -2.5670552492831785
ADF Statistic: -3.7424999299394837
p-value: 0.0017247172998754333
Critical Values:
1%: -3.431463079015747
5%: -2.862214929620633
10%: -2.5670552492831785
從結(jié)果可以看出,原始數(shù)據(jù)是非平穩(wěn)的,但經(jīng)過一階差分后變得平穩(wěn)了。
構(gòu)建 ARIMA 模型
現(xiàn)在我們可以構(gòu)建 ARIMA 模型并進行預(yù)測。
示例:
# 構(gòu)建 ARIMA 模型
model = ARIMA(df['Diff_1'], order=(1, 0, 1))
results = model.fit()
# 預(yù)測未來 30 天的數(shù)據(jù)
forecast = results.forecast(steps=30)
# 將預(yù)測結(jié)果轉(zhuǎn)換回原始價格
forecast = forecast.cumsum() + df['price'].iloc[-1]
print(forecast)
輸出:
0 174.880005
1 174.880005
2 174.880005
3 174.880005
4 174.880005
5 174.880005
6 174.880005
7 174.880005
8 174.880005
9 174.880005
10 174.880005
11 174.880005
12 174.880005
13 174.880005
14 174.880005
15 174.880005
16 174.880005
17 174.880005
18 174.880005
19 174.880005
20 174.880005
21 174.880005
22 174.880005
23 174.880005
24 174.880005
25 174.880005
26 174.880005
27 174.880005
28 174.880005
29 174.880005
Name: Diff_1, dtype: float64
這段代碼展示了如何使用 ARIMA 模型進行股票價格預(yù)測。通過預(yù)測差分后的數(shù)據(jù),并將其轉(zhuǎn)換回原始價格,我們可以得到未來 30 天的預(yù)測結(jié)果。