使用 Facebook Prophet 进行时间序列预测和缺失值填充
在数据分析和机器学习领域,时间序列预测是一个常见的任务。Facebook 开源的 Prophet 是一个专门用于时间序列预测的工具,它简单易用,能够处理复杂的季节性和趋势变化,同时对缺失值和异常值具有一定的鲁棒性。本文将介绍 Prophet 的基本功能,并通过一个代码示例展示如何使用它来填充时间序列中的缺失值。特别地,我们将深入探讨对数变换和逻辑增长模型的设置,这些步骤是根据具体业务需求进行优化的重要环节。
什么是 Facebook Prophet?
Prophet 是 Facebook 开发的一种时间序列预测工具,旨在帮助数据科学家快速生成高质量的预测结果。它基于加性回归模型,能够捕捉时间序列中的趋势、季节性和节假日效应。Prophet 的主要特点包括:
- 易于使用:Prophet 提供了简洁的 API,用户无需复杂的统计知识即可快速上手。
- 强大的季节性建模:Prophet 能够自动检测并建模年、周、日等不同粒度的季节性。
- 灵活的趋势建模:Prophet 使用分段线性或逻辑增长模型来捕捉趋势变化,并允许用户手动指定变化点。
- 鲁棒性:Prophet 对缺失值和异常值具有一定的鲁棒性,能够处理不规则采样的时间序列数据。
Prophet 的基本使用方法
Prophet 的核心思想是将时间序列分解为趋势、季节性和节假日效应。其基本模型可以表示为:
其中:
- g(t) 是趋势项,捕捉时间序列的整体变化趋势。
- s(t) 是季节性项,捕捉周期性变化(如日、周、年)。
- h(t) 是节假日效应项,捕捉特定日期的特殊影响。
- ϵt 是误差项,表示模型无法解释的随机波动。
在实际使用中,Prophet 会自动拟合这些组件,并生成预测结果。
对数变换:减小预测波动性
在时间序列预测中,数据的波动性可能会对模型的性能产生显著影响。为了减小预测的波动性,我们通常会对目标变量进行对数变换。对数变换可以稳定数据的方差,使数据分布更加接近正态分布,从而提高模型的预测精度。
代码示例:对数变换
python
# 对 y 列进行对数变换
long_df['y'] = np.log(long_df['y'] + 1)
为什么进行对数变换?
- 稳定方差:对数变换可以将乘法关系转化为加法关系,从而稳定数据的方差。
- 减小波动性:对数变换可以压缩数据的范围,减小极端值的影响,使数据更加平滑。
- 提高模型性能:对数变换后的数据更接近正态分布,有助于提高模型的预测性能。
对数变换的效果
假设原始数据 (y) 的分布如下:
y = [1, 2, 3, 10, 100]
经过对数变换后:
log(y + 1) = [0, 0.693, 1.098, 2.302, 4.605]
可以看到,对数变换有效地压缩了数据的范围,减小了极端值的影响。
逻辑增长模型:确保预测值非负
在某些业务场景中,预测值必须是非负的。例如,销售数据、流量数据等都不可能为负值。为了确保预测值非负,我们可以使用逻辑增长模型(Logistic Growth Model)。逻辑增长模型通过设定上限(cap)和下限(floor),限制预测值的范围。
代码示例:逻辑增长模型
python
model = Prophet(
growth='logistic', # 设置为逻辑增长模型
yearly_seasonality=False,
weekly_seasonality=True,
daily_seasonality=True,
changepoint_prior_scale=changepoint_prior_scale, # 调整这个参数以控制变化点的强度
seasonality_prior_scale=seasonality_prior_scale # 调整这个参数以控制季节性强度
)
为什么使用逻辑增长模型?
- 业务需求:某些业务场景中,预测值必须是非负的。
- 模型约束:逻辑增长模型通过设定上限和下限,确保预测值在合理范围内。
- 提高预测精度:通过限制预测值的范围,可以避免不合理的结果,提高模型的预测精度。
逻辑增长模型的效果
逻辑增长模型通过以下公式确保预测值非负:

其中:
- K 是上限(cap)。
- r是增长率。
- t0 是中点时间。
通过设定合理的上限和下限,可以确保预测值始终在合理范围内。
示例:使用 Prophet 填充缺失值
下面是一个使用 Prophet 填充时间序列中缺失值的完整代码示例。
完整代码示例
python
import pandas as pd
import numpy as np
from prophet import Prophet
from typing import Optional
from datetime import datetime, time
def fill_missing_data_prophet(group: pd.DataFrame, missing_times_list: pd.Series,
changepoint_prior_scale: float = 0.05, seasonality_prior_scale: float = 1,
error_data_save_path: Optional[str] = 'main/data/处理流程中的中间数据/异常数据.csv'):
try:
if not missing_times_list:
return group
# 创建一个长格式的数据框
long_df = pd.melt(
group,
id_vars=['date', 'user_id'],
value_vars=time_col_list,
var_name='time',
value_name='value'
)
# 生成完整的 datetime 列
long_df['datetime'] = pd.to_datetime(
long_df['date'].astype(str) + ' ' + long_df['time'],
format='%Y-%m-%d %H:%M'
)
long_df.rename(columns={'datetime': 'ds', 'value': 'y'}, inplace=True)
# 对 y 列进行对数变换
long_df['y'] = np.log(long_df['y'] + 1)
# 获取训练集的最大值和最小值
max_value = long_df['y'].max()
min_value = long_df['y'].min()
if min_value > 0:
min_value = long_df['y'].min()
else:
min_value = 0
if max_value == 0 or not max_value:
max_value = np.log(0.001 + 1)
# 初始化 Prophet 模型
model = Prophet(
growth='logistic', # 设置为逻辑增长模型
yearly_seasonality=False,
weekly_seasonality=True,
daily_seasonality=True,
changepoint_prior_scale=changepoint_prior_scale, # 控制趋势变化点的强度
seasonality_prior_scale=seasonality_prior_scale # 控制季节性强度
)
# 设置上限和下限
long_df['cap'] = max_value
long_df['floor'] = min_value
# 拟合模型
model.fit(long_df)
# 创建未来数据框,用于预测缺失时间段
future = []
for start, end in missing_times_list:
start_datetime = pd.to_datetime(start)
end_datetime = pd.to_datetime(end)
future.extend(pd.date_range(start=start_datetime, end=end_datetime, freq='15T').to_pydatetime().tolist())
future_df = pd.DataFrame(future, columns=['ds'])
# 设置未来数据框的上限和下限
future_df['cap'] = max_value
future_df['floor'] = min_value
# 预测缺失值
forecast = model.predict(future_df)
# 将预测值转换回原始尺度
forecast['yhat'] = np.exp(forecast['yhat']) - 1
forecast['yhat_lower'] = np.exp(forecast['yhat_lower']) - 1
forecast['yhat_upper'] = np.exp(forecast['yhat_upper']) - 1
# 确保预测值非负
forecast['yhat'] = np.maximum(forecast['yhat'], 0)
forecast['yhat_lower'] = np.maximum(forecast['yhat_lower'], 0)
forecast['yhat_upper'] = np.maximum(forecast['yhat_upper'], 0)
# 将预测值填充回原数据框
for i, row in future_df.iterrows():
group.loc[group['date'] == datetime.combine(row['ds'].date(), time(0, 0, 0)),
row['ds'].strftime('%H:%M')] = forecast.loc[i, 'yhat']
return group
except Exception as e:
print("Exception: ", e)
# 持久化存储异常数据
user_id_err = group['user_id'].iloc[0]
error_data_save_path = error_data_save_path.replace('.', f"_{user_id_err}.")
group.to_csv(error_data_save_path, index=False)
return group
代码说明
-
数据预处理:
- 将宽格式数据转换为长格式数据,便于 Prophet 处理。
- 生成
datetime
列,用于时间序列建模。 - 对
y
列进行对数变换,以稳定数据的方差。
-
模型初始化:
- 使用
Prophet
类初始化模型,并设置参数:growth='logistic'
:设置为逻辑增长模型,确保预测值非负。yearly_seasonality=False
:不考虑年季节性。weekly_seasonality=True
:考虑周季节性。daily_seasonality=True
:考虑日季节性。changepoint_prior_scale
:控制趋势变化点的强度。seasonality_prior_scale
:控制季节性强度。
- 使用
-
模型拟合:
- 使用
fit
方法拟合模型。
- 使用
-
预测缺失值:
- 创建未来数据框
future_df
,包含需要预测的时间段。 - 使用
predict
方法生成预测结果。 - 将预测值转换回原始尺度,并确保预测值非负。
- 创建未来数据框
-
填充缺失值:
- 将预测值填充回原始数据框。
注意事项
-
数据格式:
- Prophet 要求输入数据框包含两列:
ds
(时间戳)和y
(目标值)。
- Prophet 要求输入数据框包含两列:
-
异常值处理:
- Prophet 对异常值具有一定的鲁棒性,但在实际使用中,仍需对异常值进行处理以提高预测精度。
-
模型参数:
changepoint_prior_scale
和seasonality_prior_scale
是重要的超参数,需要根据数据特性进行调整。
-
季节性建模:
- Prophet 会自动检测季节性,但用户也可以手动指定季节性。
总结
Facebook Prophet 是一个强大的时间序列预测工具,适用于快速生成高质量的预测结果。通过对数变换和逻辑增长模型的设置,我们可以根据具体业务需求优化模型,确保预测结果的合理性和准确性。通过本文的介绍和代码示例,我们展示了如何使用 Prophet 填充时间序列中的缺失值。Prophet 的易用性和灵活性使其成为处理时间序列数据的首选工具之一。如果你需要处理复杂的时间序列预测任务,Prophet 绝对值得一试!