Prophet
# ARMA/ARIMA统计模型的不足
- ARMA,要求时序数据是稳定的,现实数据很难符合
- ARIMA,模型为线性模型,无法处理非线性关系,同时要求数据点的间隔等长,比如X1和X2间隔一个小时,那么X2和X3也间隔一个小时
- 如果数据缺失,则需要使用插值等方法来预估缺失值,然后再使用预估值来进行参数拟合,这样会引入噪音
# Facebook prophet工具
- facebook开源的时间序列预测工具https://facebook.github.io/prophet/ (opens new window)
- Prophet是一个基于相加模型(additive model)的时间预测,可以精准的拟合非线性的周期趋势
- 对yearly、weekly和daily的周期性使用非线性拟合,亮点在于Prophet模型还添加了holidays(影响因子),可以很好的对节日(比如十一、春节等)带来的活跃数据的突变进行预测
# prophet的优势:
之前学的时间序列模型,有一个共同的弱点:处理不了突发的影响。
比如:
春节前后,大家都在买年货、转账,金额会突然暴涨
双十一,购物狂欢,申赎数据会出现异常高峰
十一黄金周,又是另一个高峰
这些节假日的影响,用ARIMA、SARIMA很难精确捕捉,因为它们变化不规律。Prophet就是来解决这个问题的。
- 处理数据丢失问题
- 趋势迁移问题(shifts in the trend)
- 异常的数据点(outliers)
prophet模型:y(t)=g(t)+s(t)+h(t)+e
这叫相加模型,意思就是把一个复杂的时间序列,拆成几个部分加在一起:
| 符号 | 含义 | 打个比方 |
|---|---|---|
| g(t) | 趋势项 | 股票的长期走向,是涨是跌的大方向 |
| s(t) | 周期项 | 一周哪天高(周末低)、一年哪月高(年底高) |
| h(t) | 节假日项 | 春节、十一、双十一带来的突然变化 |
| e | 误差项 | 其他乱七八糟的随机波动 |
g(t)代表趋势项,用来表示时间序列中非周期性的变化
"趋势"就是数据长期往上走还是往下走。
Prophet用两种方式来拟合趋势:- 第一种:饱和增长模型(像S形曲线)
比如一个新产品刚上线,用户增长很快,后来增速放缓,慢慢趋于平稳
有点像 logistic growth(逻辑增长) - 第二种:分段线性模型(像折线)
把时间序列分成几段,每段分别用直线拟合
优点是很灵活,能捕捉"增长突然加速"这种转折点
- 第一种:饱和增长模型(像S形曲线)
s(t)代表周期项,用来表示时间序列中的周期变化
运用傅里叶级数作为周期项,使得预测模型具有灵活的周期效应
Prophet默认处理三种周期:- yearly(年度周期):一年内的变化
- weekly(周周期):一周内的变化,你蚂蚁金服数据有明显的周周期,周末金额低
- daily(日周期):一天内的变化
h(t)代表活动效果项,用来表达时间序列中的一些异常活动,例节假日,购物节等
这是Prophet的核心亮点。
传统时间序列模型处理不了"每年日期不固定的节日",比如:- 春节(每年日期不同)
- 母亲节(每年日期不同)
- 感恩节
但Prophet可以!你给它一张"节假日表格",告诉它"春节期间金额会暴增",它就能学习到这个规律。
# 创建一个节假日表格 holidays = pd.DataFrame({ 'holiday': '春节', # 节日名称 'ds': pd.to_datetime(['2014-01-31', '2015-02-19']), # 节日日期 'lower_window': -5, # 春节前5天开始影响 'upper_window': 5, # 春节后5天结束影响 })1
2
3
4
5
6
7这样Prophet就知道:春节前后这10天,数据可能会有异常波动。
e用来表示不能被模型所描述的异常误差,就是模型解释不了的随机波动,这个没什么好说的,任何模型都有。
# prophet工具使用:
# 安装
pip install prophet
# 导入
from prophet import Prophet
# 准备数据(Prophet要求列名叫ds和y)
df = pd.DataFrame({
'ds': daily.index, # 日期列
'y': daily['total_purchase_amt'] # 要预测的值
})
# 创建模型
model = Prophet(
yearly_seasonality=True, # 年度周期
weekly_seasonality=True, # 周周期
daily_seasonality=False, # 日周期(你这个是日数据,不需要)
holidays=holidays # 加上节假日
)
# 训练
model.fit(df)
# 预测未来30天
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)
# forecast里包含预测值,以及各个分解项(趋势、周期、节假日)
print(forecast[['ds', 'yhat', 'trend', 'weekly', 'yearly']])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# prophet模型:
Trend趋势,对时间序列中的趋势部分拟合分段线性函数,线性拟合会将特殊点和缺失数据的影响降到最小
趋势,就是数据长期往哪个方向走。
比如:
某App用户量:1月100人 → 6月500人 → 12月1000人 → 这就是上升趋势
某个衰退中的产品:每月都在下降 → 这就是下降趋势
Prophet用分段线性函数来拟合趋势,意思就是把时间序列分成几段,每段分别用直线拟合。什么是分段线性拟合 普通线性拟合:整段时间用一条直线来描述
分段线性拟合:把时间分成几段,每段用各自的直线为什么分段更好?
假设你用一条直线拟合2019年的口罩销量:
疫情前销量很低
疫情后销量暴涨
如果用一条直线,那"疫情前"的数据会被"疫情后"带偏,反过来也一样,预测就会不准。
分段线性就聪明了,它知道"这里有个转折点",前后分开拟合。饱和增长
通常情况下,增长会有最大容量限制,比如未来12个月某app在某地区的下载量,最大下载量要小于等于该地区手机用户总数
基于这样的领域知识,分析师可以定义模型的容量限制为C(t)在代码里这样设定:
df['cap'] = 100000000 # 设定上限(比如市场容量) df['floor'] = 0 # 设定下限 model = Prophet(growth='logistic') model.fit(df)1
2
3
4
5突变点,随着突变点数量的增多,拟合变得更灵活。在研究趋势成分时,分析师要面临两个基本问题,即过拟合与欠拟合
"突变点"就是趋势发生变化的时间点。
例子:- 某产品1-6月一直上升,7月换了CEO,8月开始下滑
- 7月就是"突变点"
Prophet默认会自动识别突变点,识别逻辑大概是:
- 先用一条整体直线拟合数据
- 找出"偏离这条线最远"的几个点
- 这些点就是潜在的突变点
但它不会一下子变得很灵活,而是有一个默认的灵活性。
changepoint_prior_scale参数,可以调整趋势的灵活性,解决过拟合/欠拟合,参数值越大,拟合的时间序列曲线越灵活
你可以理解为"灵活性的权重"参数值 效果 适合场景 值很小(默认0.05) 趋势变化很小,像一条平缓的曲线 数据很稳定,没有太多波动 值很大(比如1.0) 趋势变化很剧烈,曲线很"抖" 数据经常有大起大落 调参技巧:
如果Prophet预测曲线太平滑,实际数据却有很多波动 → 过拟合 → 调大这个值
如果Prophet预测曲线太"抖",实际数据却很平稳 → 欠拟合 → 调小这个值# 调大,让趋势更灵活 model = Prophet(changepoint_prior_scale=0.1)1
2
| 参数 | 控制什么 | 怎么用 |
|---|---|---|
| growth | 用什么形状的曲线 | 'linear'=直线(最常用)'logistic'=S形曲线(有上限) |
| changepoints | 手动指定突变点在哪几天 | 比如changepoints=['2014-01-31', '2014-02-15'] |
| n_changepoints | 不手动指定时,自动识别几个突变点 | 默认25个,通常不用改 |
| changepoint_prior_scale | 突变点附近的曲线有多灵活 | 越大越"抖",越小越平缓 |
最常用的场景
# 场景1:数据稳定,没有明显突变
model = Prophet(growth='linear')
# 场景2:数据有容量上限(比如用户量不会超过网民总数)
df['cap'] = 1_000_000
model = Prophet(growth='logistic')
# 场景3:知道某个时间点趋势会变(比如政策发布日)
model = Prophet(changepoints=['2014-03-15'])
# 场景4:曲线太平滑,要让它更灵活
model = Prophet(changepoint_prior_scale=0.5)
2
3
4
5
6
7
8
9
10
11
12
# prophet的突变点分析
- 检测突变点
- 先均匀放点:在时间序列的前80%里,均匀放25个"候选突变点"
- 再筛选:用数学方法(叫Laplace分布,你可以理解为一种"惩罚机制")判断哪些点真的需要"突变",哪些只是普通波动
- 留下显著的:最后保留下来的,就是真正需要分段的地方
为什么要"前80%"而不是全部?因为最后20%要留着验证模型,不能让模型偷看答案。
- 分段趋势建模
趋势公式(以线性趋势为例):

δs:突变点s处的斜率变化量。
γ:保证函数连续的偏移量(避免突变点处的跳跃)。
- 预测未来趋势
未来时间段的趋势延续最后一个突变点后的斜率(即假设未来趋势不再突变)。
# 案例
假设某产品的日销量在以下时间点发生突变:
- 2024-01-01:初始增长(斜率=0.5)。
- 2024-04-01:因促销活动,斜率突增至1.2。
- 2024-07-01:市场竞争加剧,斜率降至0.3。
# 导入 pandas 用于处理数据框(DataFrame)和时间序列。
import pandas as pd
# 导入 numpy 用于生成数学序列、随机数和数组拼接。
import numpy as np
# 导入 matplotlib.pyplot 用于绘制数据图表。
import matplotlib.pyplot as plt
from prophet import Prophet
# 生成日期范围
dates = pd.date_range(start='2024-01-01', end='2024-12-31')
n_days = len(dates)
# 模拟趋势突变
np.random.seed(42)
trend = np.concatenate([
0.5 * np.arange(91), # 1月-3月:斜率0.5,共91天
0.5 * 91 + 1.2 * np.arange(91), # 4月-6月:斜率1.2,共91天
0.5 * 91 + 1.2 * 91 + 0.3 * np.arange(n_days - 91 - 91) # 7月-12月:斜率0.3
])
# 添加季节性噪声
# np.linspace(0, 10 * np.pi, n_days) 在 0 到 10π 之间生成 n_days 个等间距的点。因为正弦函数周期是 2π,10π 代表 5 个完整的周期。
# 5 * np.sin(...) 生成一个振幅为 5 的正弦波,模拟数据中的周期性(季节性)波动。
y = trend + 5 * np.sin(np.linspace(0, 10 * np.pi, n_days))
df = pd.DataFrame({'ds': dates, 'y': y})
# 可视化原始数据
plt.figure(figsize=(12, 4))
plt.plot(df['ds'], df['y'], label='Actual')
plt.title("Simulated Data with Changepoints")
plt.legend()
plt.show()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

这段代码的主要目的是生成一段带有“趋势突变点”和“季节性波动”的模拟时间序列数据,并将其可视化。这通常用于测试时间序列预测算法(如 Prophet 模型)对数据趋势变化的捕捉能力。
## 训练 prophet 模型
# 初始化模型
model = Prophet(
yearly_seasonality=True, # 开启年度季节性以捕捉长周期波动
weekly_seasonality=False, # 关闭周季节性,因为数据没有7天周期特征
changepoint_prior_scale=0.05, # 修正:使用默认灵敏度,防止过拟合
changepoint_range=0.9 # 在前90%数据中检测突变点
)
# 拟合数据
# 将模拟数据喂给模型,让模型学习其中的趋势和季节性规律。
model.fit(df)
# 创建未来30天的预测
# 创建一个包含历史数据和未来 30 天的数据框。
future = model.make_future_dataframe(periods=30)
# 模型根据学到的规律,对未来 30 天进行预测。
forecast = model.predict(future)
## 可视化突变点与预测
# 绘制预测结果图(包含历史数据和未来预测)。
fig1 = model.plot(forecast)
plt.title("Forecast with Changepoints")
# 标记突变点,在图表上画出红色的竖线,标记模型自动检测到的趋势突变点。
add_changepoints_to_plot(fig1.gca(), model, forecast)
# 展示图表,观察模型是否准确捕捉到了 4 月和 7 月的趋势变化。
plt.show()
# 分解趋势和季节性
# plot_components 会将预测结果拆解为三个部分分别展示:
# 趋势(Trend):数据整体的增长或下降趋势。
# 年度季节性(Yearly Seasonality):长周期的波动规律。
# 周季节性(Weekly Seasonality):由于我们关闭了它,这里不会显示。
fig2 = model.plot_components(forecast)
plt.show()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35


可以看到,模型捕捉到了4月和7月的突变
Prophet的突变点机制能自动适应趋势变化,非常适合业务场景中突发事件的建模。
传统ARIMA模型,假设趋势是平滑变化的,无法灵活处理突发性趋势转折。
Prophet的解决方案:通过突变点检测,Prophet将时间序列分段建模,每段的趋势斜率可以独立调整。例如:产品销量因营销活动突然增长。或者经济指标因政策调整骤降。
# 用数学方法去拟合和预测数据中的“季节性”波动。
- 季节性,拟合并预测季节的效果,基于傅里叶级数提出了一个灵活的模型:

如何用数学方法去拟合和预测数据中的“季节性”波动。简单来说,就是模型怎么“学会”数据里那些周而复始的规律,比如每天、每周或每年的固定模式。
为什么需要“傅里叶级数”?
现实世界里的季节性,很少是完美的正弦波或余弦波。比如,气温变化、节假日销量波动,它们的形状可能很复杂、很不规则。
傅里叶级数的核心思想是:任何复杂的周期性函数,都可以被拆解成一系列简单的正弦波和余弦波的叠加。
想象一下,一个复杂的波形就像一幅画,而傅里叶级数就是用很多个不同频率、不同振幅的简单正弦/余弦波(就像不同粗细的画笔)来一笔一笔地把它“画”出来。
# 拆解公式
- s(t): 这就是模型要拟合的季节性效果。比如在 t 这一天,季节性因素会给预测值带来多大的影响。
- P: 这是周期。
如果你想拟合年度季节性,P 就是 365.25(考虑闰年)。
如果你想拟合周度季节性,P 就是 7。 - n: 这是阶数,从 1 开始一直到 N。n=1 代表频率最低、最平缓的那个基础波;n 越大,代表波的频率越高,波动越快。
- an 和 bn: 这是模型需要学习的关键参数。
n 控制第 n 个正弦波的“高度”(振幅)。
bn 控制第 n 个余弦波的“高度”(振幅)。
模型通过历史数据,自动算出这一系列 a 和 b 的最佳值,从而组合出最贴合数据的那个季节性曲线。
参数 N 是什么?为什么它很重要?
N 是傅里叶级数的阶数,你可以把它理解为模型的“灵活度”或“复杂度”。
N 值较低:
模型只用少数几个低频的、平缓的波来拟合季节性。
效果:得到的季节性曲线非常平滑,只能捕捉到大的、缓慢的变化趋势。
适用场景:当你认为数据中那些快速的、细小的波动只是随机噪声,不值得去学习时,就把 N 设小一点。这可以防止模型“死记硬背”噪声,提高泛化能力。N 值较高:
模型会使用更多高频的、变化剧烈的波来拟合。
效果:得到的季节性曲线非常灵活,可以捕捉到数据中很多细微的、快速的波动。
适用场景:当你认为这些高频变化是真实存在的、有意义的模式(比如,一周内每天销量的精细变化),而不是噪声时,就把 N 设大一点,以提升预测精度。
# 活动效果项,即节假日和大事件
Prophet 模型如何处理节假日(Holidays)和特殊事件,以及与之相关的核心参数配置。在时间序列预测中,像春节、双十一促销或大型体育赛事等事件,往往会导致数据出现剧烈的突变,普通的趋势和季节性模型很难捕捉到这些规律。
# 节假日与特殊事件的处理机制
Prophet 允许分析师传入一个自定义的“事件列表”(包含过去和未来的日期)。模型会将这些特殊日期及其前后的时间段单独提取出来,通过拟合附加的参数来模拟这些事件对数据的具体影响(例如春节前后销量激增的幅度)。
季节和假日相关的参数:
| 参数 | 描述 |
|---|---|
| yearly_seasonality | 周期为年的季节性 |
| weekly_seasonality | 周期为周的季节性 |
| daily_seasonality | 周期为日的季节性 |
| holidays | 内置的节假日名称和日期 |
| seasonality_priori_scale | 改变季节模型的强度 |
| holiday_prior_scale | 改变假日模型的强度 |
# 核心参数详解
表格中列出的参数是控制模型“趋势”、“季节性”和“节假日”强度的关键开关:
yearly_seasonality / weekly_seasonality / daily_seasonality:
这三个参数分别控制年度、周度和日度的周期性规律。你可以将其设置为 True(开启)、False(关闭)或者一个具体的整数(代表傅里叶阶数,控制拟合的复杂程度)。例如,如果你的数据是月度销量,通常只需要开启年度季节性,而关闭周度和日度季节性。holidays: 这是用来传入自定义节假日或特殊事件的参数。它要求传入一个包含特定列的 DataFrame,通常包括:
- holiday:事件名称(如“春节”、“618大促”)。
- ds:事件发生的具体日期。
- lower_window / upper_window:事件影响的前后窗口期(例如春节前3天到节后7天都会受影响)。
- 此外,Prophet 还支持通过 add_country_holidays(country_name='CN') 直接内置国家法定节假日。
seasonality_prior_scale(季节性先验尺度):
这个参数用来改变季节模型的强度(灵活性)。- 值越大:模型对季节性变化的拟合越灵活,能捕捉到更细微的季节波动,但也更容易过拟合(把噪声当成了季节规律)。
- 值越小:模型对季节性的估计越保守,季节性曲线会更平滑。
holiday_prior_scale(节假日先验尺度): 这个参数用来改变假日模型的强度。其逻辑与季节性参数类似:
- 值较大:表示模型认为节假日对数据的影响非常大,预测曲线在节假日期间会有剧烈的调整。
- 值较小:表示模型对节假日效应持保守态度,节假日对整体预测结果的调整幅度会被限制在一个较窄的范围内。
总结来说,在实际业务中,分析师首先通过 yearly/weekly/daily_seasonality 搭建数据的常规周期骨架,然后利用 holidays 注入春节、促销等业务知识,最后通过调节 prior_scale 参数来平衡模型对“规律”和“突发事件”的敏感度,从而得到最贴合业务实际的预测结果。
# 总结
模型定位与优缺点
定位:Prophet 是专门为商业预测(如销量预测、流量预估)设计的。
优点:自动化程度极高。它不需要像传统机器学习模型那样做复杂的特征工程,就能自动拆解出数据的趋势、季节性和节假日效应。
不足:信息利用单一。它只能处理单变量时间序列(仅靠 ds 和 y 两列数据)。在预测销量时,它无法直接利用“商品价格”、“门店位置”、“促销力度”等外部协变量(尽管可以通过节假日参数勉强模拟部分促销活动)。核心输入与标准工作流
数据格式:Prophet 对输入数据有严格要求,必须是包含两列的 DataFrame:
ds:时间戳(必须是 Pandas 的 datetime 格式)。
y:真实值(需要预测的数值型指标)。
标准四步法:
初始化:m = Prophet(holidays=holidays),可传入自定义节假日。
训练:model.fit(df),让模型学习历史规律。
构建未来时间轴:future = model.make_future_dataframe(periods=365)。注意,这个 future 不仅包含未来 365 天,还包含了历史的所有时间戳。
预测:forecast = model.predict(future),输出包含历史和未来的完整预测结果。
- 预测结果解读(forecast 字段)
预测输出的 forecast 是一个包含丰富信息的 DataFrame,核心字段如下:
预测值:yhat(核心预测结果),yhat_lower 和 yhat_upper(置信区间的上下界)。
趋势分解:trend(基础趋势),weekly(周季节性),yearly(年季节性)。
加法/乘法项:
additive_terms:加法模型下的总季节/节假日效应(等于 weekly + yearly + holidays)。
multiplicative_terms:乘法模型下的总效应。如果使用的是默认的加法模型,该列将为空。
- 趋势突变点(Changepoints)
现实世界的数据趋势往往会发生突然转折(如疫情爆发、政策调整),Prophet 能自动检测这些点:
自动检测机制:默认在前 80% 的历史数据中,均匀分布 25 个潜在的突变点。
参数调优:
n_changepoints:增加或减少潜在突变点的数量(如设为 30)。
changepoint_range:调整寻找突变点的历史数据比例(如设为 0.9,即在前 90% 数据中寻找)。
人工指定:如果你明确知道某天发生了大事件,可以通过 changepoints=['2014-01-01'] 强制模型在该点进行趋势调整。
- 高级参数:增长模式与季节性模式
增长趋势(growth):
'linear'(默认):假设数据呈线性无限增长。
'logistic'(逻辑斯蒂增长):适用于有天花板(饱和值)的业务场景(如市场渗透率)。使用时必须在数据框 df 中增加一列 cap 来指定饱和上限,否则会报错。
季节性模式(seasonality_mode):
'additive'(默认,加法模型):假设季节波动的幅度是固定的,不随整体趋势变化。
'multiplicative'(乘法模型):假设季节波动的幅度会随着整体趋势的增大而按比例放大(例如,销量基数越大,春节带来的绝对增量就越大)。