Python与时序数据分析:利用Prophet和Statsmodels库进行时间序列预测
大家好,今天我们要探讨的是利用Python进行时序数据分析和预测。具体来说,我们会深入研究两个强大的Python库:Facebook的Prophet和Statsmodels。时序数据分析在很多领域都至关重要,比如金融、销售预测、气象学等等。掌握这些工具,能帮助我们从历史数据中提取信息,预测未来趋势,并做出更明智的决策。
1. 时序数据分析基础概念
在开始之前,我们先回顾一下时序数据分析的一些基本概念。
-
时间序列: 按时间顺序排列的数据点序列。时间可以是连续的(例如,股票价格的每秒记录),也可以是离散的(例如,每月的销售额)。
-
趋势性 (Trend): 时间序列在长期内呈现的上升或下降的模式。
-
季节性 (Seasonality): 在固定时间间隔内重复出现的模式。例如,冰淇淋的销量通常在夏季达到高峰,而在冬季下降。
-
周期性 (Cyclicity): 类似于季节性,但周期长度不固定,且通常更长。例如,商业周期。
-
残差 (Residuals): 时间序列中去除趋势、季节性和周期性后的剩余部分。理想情况下,残差应该近似于白噪声。
-
平稳性 (Stationarity): 一个时间序列,其统计特性(如均值和方差)不随时间变化。许多时序模型都假设数据是平稳的,或者可以通过转换(如差分)使其平稳。
2. Statsmodels库:经典时序模型
Statsmodels是一个Python库,提供了大量的统计模型,包括各种时序模型。我们先来看几个常用的模型。
2.1. 指数平滑 (Exponential Smoothing)
指数平滑是一类简单但有效的时序预测方法,它通过对过去的数据进行加权平均来进行预测,越近的数据权重越高。Statsmodels提供了多种指数平滑模型,包括:
-
简单指数平滑 (Simple Exponential Smoothing, SES): 适用于没有趋势和季节性的数据。
-
双指数平滑 (Double Exponential Smoothing, DES): 适用于具有趋势但没有季节性的数据。
-
三指数平滑 (Triple Exponential Smoothing, TES) / Holt-Winters’ Seasonal Method: 适用于具有趋势和季节性的数据。
import pandas as pd
from statsmodels.tsa.api import ExponentialSmoothing, SimpleExpSmoothing, Holt
# 创建示例数据
data = [44, 46, 48, 43, 47, 51, 49, 53, 55, 58, 56, 60, 62, 65, 64, 68]
index = pd.date_range(start='2023-01-01', periods=len(data), freq='M') # 月度数据
df = pd.DataFrame({'Sales': data}, index=index)
# 简单指数平滑
fit1 = SimpleExpSmoothing(df['Sales']).fit(smoothing_level=0.2, optimized=False)
fcast1 = fit1.forecast(3) # 预测未来3个月
# Holt's linear trend method (双指数平滑)
fit2 = Holt(df['Sales']).fit(smoothing_level=0.8, smoothing_trend=0.2, optimized=False)
fcast2 = fit2.forecast(3)
# Holt-Winters' seasonal method (三指数平滑) - 这里我们假设季节性周期为4
fit3 = ExponentialSmoothing(df['Sales'], seasonal_periods=4, seasonal='add').fit()
fcast3 = fit3.forecast(3)
print("Simple Exponential Smoothing Forecast:", fcast1)
print("Holt's Linear Trend Forecast:", fcast2)
print("Holt-Winters' Seasonal Forecast:", fcast3)
在这个例子中,我们首先创建了一个包含销售数据的DataFrame。然后,我们分别使用SimpleExpSmoothing
、Holt
和ExponentialSmoothing
来拟合数据,并预测了未来3个月的销售额。 smoothing_level
和 smoothing_trend
是平滑参数,控制了对过去数据的加权程度。seasonal_periods
指定了季节性周期,seasonal='add'
指定了季节性成分是加性的。
2.2. ARIMA模型
ARIMA (Autoregressive Integrated Moving Average) 模型是一类非常强大的时序模型,它可以捕捉数据中的自相关性。ARIMA模型由三个参数指定:(p, d, q)。
-
p: 自回归 (AR) 模型的阶数。表示当前值与过去p个值的相关性。
-
d: 差分 (I) 的阶数。表示为了使数据平稳需要进行的差分次数。
-
q: 移动平均 (MA) 模型的阶数。表示当前值与过去q个误差项的相关性。
from statsmodels.tsa.arima.model import ARIMA
# 创建示例数据 (模拟一个非平稳时间序列)
import numpy as np
np.random.seed(0)
data = np.cumsum(np.random.randn(100)) # 累积和,通常是非平稳的
index = pd.date_range(start='2023-01-01', periods=len(data), freq='D') # 每日数据
df = pd.DataFrame({'Value': data}, index=index)
# 拟合ARIMA模型 (这里假设已经通过ACF/PACF图确定了p, d, q)
# 在实际应用中,需要通过ACF和PACF图分析来确定合适的p, d, q值
# 或者使用AutoARIMA自动搜索最佳参数
model = ARIMA(df['Value'], order=(5, 1, 0)) # AR(5)模型,一阶差分
model_fit = model.fit()
# 预测未来10天
predictions = model_fit.forecast(steps=10)
print("ARIMA Predictions:", predictions)
在这个例子中,我们首先创建了一个非平稳的时间序列数据。然后,我们使用ARIMA
类来拟合模型。order=(5, 1, 0)
表示我们使用了一个AR(5)模型,并进行了一阶差分。 fit()
方法用于训练模型,forecast()
方法用于预测未来10天的数据。 重要的是,在实际应用中,需要通过观察自相关函数 (ACF) 和偏自相关函数 (PACF) 图来确定合适的p、d和q值。 也可以使用 pmdarima
库中的 auto_arima
函数自动搜索最佳参数。
2.3. SARIMA模型
SARIMA (Seasonal ARIMA) 模型是ARIMA模型的扩展,用于处理具有季节性的数据。SARIMA模型由七个参数指定:(p, d, q)(P, D, Q)m。
-
p, d, q: 与ARIMA模型相同。
-
P, D, Q: 季节性部分的ARIMA模型参数。
-
m: 季节性周期。
from statsmodels.tsa.statespace.sarimax import SARIMAX
# 创建示例数据 (模拟一个具有季节性的时间序列)
np.random.seed(0)
data = np.sin(np.linspace(0, 10*np.pi, 100)) + np.random.randn(100)*0.1 # 正弦波 + 噪声
index = pd.date_range(start='2023-01-01', periods=len(data), freq='M') # 月度数据
df = pd.DataFrame({'Value': data}, index=index)
# 拟合SARIMA模型 (这里假设季节性周期为12)
# 在实际应用中,需要通过ACF/PACF图确定合适的p, d, q, P, D, Q值
model = SARIMAX(df['Value'], order=(1, 0, 0), seasonal_order=(1, 1, 0, 12)) # SARIMA(1,0,0)(1,1,0,12)
model_fit = model.fit()
# 预测未来12个月
predictions = model_fit.forecast(steps=12)
print("SARIMA Predictions:", predictions)
在这个例子中,我们创建了一个具有季节性的时间序列数据。然后,我们使用SARIMAX
类来拟合SARIMA模型。order=(1, 0, 0)
指定了非季节性部分的ARIMA模型参数,seasonal_order=(1, 1, 0, 12)
指定了季节性部分的ARIMA模型参数,12
是季节性周期。
使用Statsmodels进行时序分析的关键步骤:
- 数据准备: 将数据转换为 Pandas DataFrame,并确保时间索引正确。
- 数据可视化: 绘制时间序列图,观察趋势、季节性和周期性。
- 平稳性检验: 使用ADF检验等方法检验数据的平稳性。如果数据不平稳,进行差分等转换。
- 参数选择: 通过ACF和PACF图分析,或者使用AutoARIMA等方法选择合适的模型参数。
- 模型拟合: 使用 Statsmodels 中的相应类来拟合模型。
- 模型诊断: 检查残差是否符合白噪声假设。
- 预测: 使用拟合好的模型进行预测。
- 评估: 使用均方误差 (MSE)、均方根误差 (RMSE) 等指标评估预测结果。
3. Prophet库:针对商业时间序列的预测
Prophet是Facebook开发的一个专门用于预测商业时间序列的库。它具有以下优点:
-
易于使用: Prophet提供了一个简单易用的API,即使没有深入的时序分析知识,也可以快速上手。
-
自动处理季节性: Prophet可以自动检测和处理多种类型的季节性,包括年度季节性、每周季节性和每日季节性。
-
处理缺失值和异常值: Prophet可以自动处理缺失值和异常值,无需进行复杂的数据清洗。
-
可解释性强: Prophet可以将预测结果分解为趋势、季节性和节假日效应等成分,方便理解预测的原因。
3.1. 安装Prophet
可以使用pip安装Prophet:
pip install prophet
注意: Prophet依赖于pystan
,因此可能需要先安装pystan
。 如果安装 prophet
失败, 可以尝试先安装 pystan
再安装 prophet
。
3.2. Prophet的基本用法
from prophet import Prophet
import pandas as pd
# 创建示例数据
data = {
'ds': pd.to_datetime(['2023-01-01', '2023-01-08', '2023-01-15', '2023-01-22', '2023-01-29',
'2023-02-05', '2023-02-12', '2023-02-19', '2023-02-26', '2023-03-05']),
'y': [10, 12, 15, 13, 17, 20, 22, 25, 23, 27]
}
df = pd.DataFrame(data)
# 初始化Prophet模型
model = Prophet()
# 拟合模型
model.fit(df)
# 创建未来数据框
future = model.make_future_dataframe(periods=7) # 预测未来7天
# 进行预测
forecast = model.predict(future)
# 打印预测结果
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
# 可视化预测结果 (需要安装matplotlib)
# fig1 = model.plot(forecast)
# fig2 = model.plot_components(forecast)
# plt.show() # 取消注释以显示图形
在这个例子中,我们首先创建了一个包含日期 (ds
) 和值 (y
) 的DataFrame。 注意: Prophet要求DataFrame必须包含名为ds
的日期列和名为y
的值列。
然后,我们初始化了Prophet
模型,并使用fit()
方法来拟合模型。make_future_dataframe()
方法用于创建一个包含未来日期的数据框,periods=7
表示我们想要预测未来7天的数据。predict()
方法用于进行预测,预测结果包含多个列,其中yhat
是预测值,yhat_lower
和 yhat_upper
是预测值的置信区间。
最后,我们打印了预测结果,并使用model.plot()
和 model.plot_components()
方法可视化了预测结果。 model.plot()
显示了预测值和历史数据的对比图,model.plot_components()
显示了趋势、年度季节性和每周季节性等成分。
3.3. Prophet的高级用法
Prophet还提供了一些高级功能,可以进一步提高预测的准确性。
- 指定节假日效应: 如果你的数据受到节假日的影响,可以使用
add_country_holidays()
方法来添加节假日效应。
from prophet import Prophet
import pandas as pd
# 创建示例数据
data = {
'ds': pd.to_datetime(['2023-01-01', '2023-01-08', '2023-01-15', '2023-01-22', '2023-01-29',
'2023-02-05', '2023-02-12', '2023-02-19', '2023-02-26', '2023-03-05']),
'y': [10, 12, 15, 13, 17, 20, 22, 25, 23, 27]
}
df = pd.DataFrame(data)
# 初始化Prophet模型
model = Prophet()
# 添加中国节假日效应
model.add_country_holidays(country_name='CN')
# 拟合模型
model.fit(df)
# 创建未来数据框
future = model.make_future_dataframe(periods=7) # 预测未来7天
# 进行预测
forecast = model.predict(future)
# 打印节假日效应
print(model.train_holiday_names)
# 可视化预测结果 (需要安装matplotlib)
# fig1 = model.plot(forecast)
# fig2 = model.plot_components(forecast)
# plt.show() # 取消注释以显示图形
- 添加自定义季节性: 如果你的数据具有 Prophet 没有自动检测到的季节性,可以使用
add_seasonality()
方法来添加自定义季节性。
from prophet import Prophet
import pandas as pd
import numpy as np
# 创建示例数据
data = {
'ds': pd.to_datetime(['2023-01-01', '2023-01-08', '2023-01-15', '2023-01-22', '2023-01-29',
'2023-02-05', '2023-02-12', '2023-02-19', '2023-02-26', '2023-03-05']),
'y': [10, 12, 15, 13, 17, 20, 22, 25, 23, 27]
}
df = pd.DataFrame(data)
# 初始化Prophet模型
model = Prophet()
# 添加自定义季节性 (例如,周期为14天的季节性)
model.add_seasonality(name='biweekly', period=14, fourier_order=3)
# 拟合模型
model.fit(df)
# 创建未来数据框
future = model.make_future_dataframe(periods=28) # 预测未来28天
# 进行预测
forecast = model.predict(future)
# 可视化预测结果 (需要安装matplotlib)
# fig1 = model.plot(forecast)
# fig2 = model.plot_components(forecast)
# plt.show() # 取消注释以显示图形
- 指定未来回归变量: 如果你的数据受到其他因素的影响,可以使用
add_regressor()
方法来添加未来回归变量。
from prophet import Prophet
import pandas as pd
# 创建示例数据
data = {
'ds': pd.to_datetime(['2023-01-01', '2023-01-08', '2023-01-15', '2023-01-22', '2023-01-29',
'2023-02-05', '2023-02-12', '2023-02-19', '2023-02-26', '2023-03-05']),
'y': [10, 12, 15, 13, 17, 20, 22, 25, 23, 27],
'advertising': [1, 2, 1.5, 1, 2.5, 3, 2.8, 3.5, 3, 4] # 广告投入
}
df = pd.DataFrame(data)
# 创建未来数据框 (包含广告投入的预测值)
future_data = {
'ds': pd.to_datetime(['2023-03-12', '2023-03-19', '2023-03-26']),
'advertising': [3.2, 3.8, 4.2] # 未来广告投入
}
future = pd.DataFrame(future_data)
future = pd.concat([future, df[['ds', 'advertising']]], ignore_index=True) #合并历史和未来数据
future = future.sort_values(by='ds').reset_index(drop=True)
# 初始化Prophet模型
model = Prophet()
# 添加回归变量
model.add_regressor('advertising')
# 拟合模型
model.fit(df)
# 进行预测
forecast = model.predict(future)
# 打印预测结果
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
# 可视化预测结果 (需要安装matplotlib)
# fig1 = model.plot(forecast)
# fig2 = model.plot_components(forecast)
# plt.show() # 取消注释以显示图形
Prophet 使用要点:
- 数据格式: 确保数据包含
ds
(日期) 和y
(数值) 列。 - 节假日: 使用
add_country_holidays
添加国家节假日,或者使用holidays
参数自定义节假日。 - 季节性: Prophet 自动检测年度和每周季节性。 使用
add_seasonality
添加自定义季节性。 - 回归变量: 使用
add_regressor
添加未来已知的回归变量。 - 参数调整: 调整
growth
(趋势增长方式),changepoint_prior_scale
(趋势变化的灵活性),seasonality_prior_scale
(季节性强度) 等参数来优化模型。
4. 实际案例分析:销售额预测
我们来一个更完整的例子,使用Prophet预测一家商店的销售额。
import pandas as pd
from prophet import Prophet
import numpy as np
# 1. 数据加载与预处理
# 假设你有一个名为 sales.csv 的文件,包含 'date' 和 'sales' 两列
try:
df = pd.read_csv('sales.csv', parse_dates=['date'])
except FileNotFoundError:
print("找不到sales.csv文件,创建示例数据...")
# 如果文件不存在,创建示例数据
dates = pd.date_range(start='2020-01-01', end='2023-12-31', freq='D')
sales = 100 + np.sin(np.arange(len(dates)) / 365 * 2 * np.pi) * 50 + np.random.randn(len(dates)) * 10
df = pd.DataFrame({'date': dates, 'sales': sales})
df.to_csv('sales.csv', index=False) # 保存为 sales.csv
df = pd.read_csv('sales.csv', parse_dates=['date'])
df.rename(columns={'date': 'ds', 'sales': 'y'}, inplace=True)
# 2. 数据探索与可视化 (可选)
# 可以使用 matplotlib 或 seaborn 绘制时间序列图,观察趋势和季节性
# 3. 模型训练
model = Prophet()
model.fit(df)
# 4. 创建未来数据框
future = model.make_future_dataframe(periods=365) # 预测未来一年
# 5. 预测
forecast = model.predict(future)
# 6. 结果评估 (使用历史数据作为验证集)
from sklearn.metrics import mean_squared_error
from math import sqrt
# 将历史数据分成训练集和验证集
train_size = int(len(df) * 0.8)
train, test = df[:train_size], df[train_size:]
# 获取验证集的预测结果
test_forecast = forecast[forecast['ds'].isin(test['ds'])]
# 计算 RMSE
rmse = sqrt(mean_squared_error(test['y'], test_forecast['yhat']))
print(f"RMSE: {rmse}")
# 7. 可视化结果
# (需要安装matplotlib)
# fig1 = model.plot(forecast)
# fig2 = model.plot_components(forecast)
# plt.show() # 取消注释以显示图形
这个例子演示了一个完整的销售额预测流程,包括数据加载、预处理、模型训练、预测和评估。
5. Prophet与Statsmodels的对比和选择
特性 | Prophet | Statsmodels |
---|---|---|
模型类型 | 专门为商业时间序列设计 | 提供了广泛的统计模型,包括各种时序模型 |
易用性 | 简单易用,API友好 | 需要一定的统计学和时序分析知识 |
自动化 | 自动处理季节性、节假日效应、缺失值和异常值 | 需要手动处理这些问题 |
可解释性 | 预测结果可分解为趋势、季节性和节假日效应 | 可解释性取决于所使用的模型 |
适用场景 | 具有明显季节性和节假日效应的商业时间序列 | 更通用的时序分析,适用于各种类型的时间序列,需要更精细的控制 |
如何选择:
-
如果你的数据是具有明显季节性和节假日效应的商业时间序列,并且你希望快速得到一个合理的预测结果,那么Prophet是一个不错的选择。
-
如果你需要更精细地控制模型,或者你的数据不符合Prophet的假设,那么Statsmodels可能更适合你。 例如,如果你需要使用ARIMA模型进行预测,或者你需要对数据进行更复杂的转换,那么Statsmodels是更好的选择。
6. 总结与思考:选择合适的工具并深入理解数据
今天我们学习了如何使用Python进行时序数据分析和预测,重点介绍了Prophet和Statsmodels两个库。Statsmodels提供了经典的统计模型,适用于各种类型的时序数据,需要一定的统计学基础。Prophet则专门为商业时间序列设计,易于使用,能够自动处理季节性和节假日效应。
在实际应用中,选择合适的工具取决于你的数据和需求。重要的是要深入理解你的数据,并根据数据的特点选择合适的模型。时序数据分析是一个复杂而有趣的领域,希望今天的讲座能够帮助你入门,并激发你进一步探索的兴趣。