Prophet模型将时间序列分成三个成分:趋势项、季节项、节假日项。模型形式如下:
y ( t ) = g ( t ) + s ( t ) + h ( t ) + ε ( t ) y(t)=g(t)+s(t)+h(t)+\varepsilon(t) y(t)=g(t)+s(t)+h(t)+ε(t)
Prophet模型通过加法将趋势项、季节项、节假日项组合在一起,可以很方便地剥离出不同成分对时间序列的影响或效应。
这里考虑两个趋势模型:饱和增长模型(回归趋势模型)和分段线性模型。
在自然经济学中,人口规模不会沿着J型曲线无限增长,最终会由于自然环境的承载力的限制而趋于饱和。在研究互联网用户增长模式时套用此模型,可以假设产品用户量也会由于入网人数(承载力)的影响而达到饱和。原始的饱和增长模型形式如下:
g ( t ) = C 1 + e x p ( − k ( t − m ) ) g(t)= \frac{C}{1+exp(-k(t-m))} g(t)=1+exp(−k(t−m))C
其中,C是承载力,代表业务指标理想中的最大值。k是增长率,m是补偿系数。
这里给出的饱和增长模型形式原始形式有两个问题。
为了解决第一个问题。可以引入外部数据 C ( t ) C(t) C(t),对每一时刻的指标值给定不同的承载力。
为了解决第二个问题。通过定义变点,将导致趋势改变的点放在模型中。设,有 S S S个变点,第 j j j个变点在 s j s_j sj时刻对增长率的影响为 δ j \delta_j δj,于是可以得到增长率 k ′ k^{'} k′:
k ′ = k + ∑ j : t > s j δ j = a j ( t ) T δ k^{'}=k+\sum_{j:t>s_j} \delta_j =a_j(t)^{T}\delta k′=k+j:t>sj∑δj=aj(t)Tδ
其中 a j ( t ) = { 1 t ≥ 1 0 e l s e a_j(t)=\begin{cases} 1 & t \ge1 \\ 0 & else \end{cases} aj(t)={10t≥1else.
于是,修正后的饱和趋势模型的形式如下:
g ( t ) = C ( t ) 1 + e x p ( − k ′ ( t − m ′ ) ) g(t)=\frac{C(t)}{1+exp(-k^{'}(t-m^{'}))} g(t)=1+exp(−k′(t−m′))C(t)
分段线性模型的形式如下:
g ( t ) = k ′ t + ( t − m ′ ) g(t)=k^{'}t+(t-m^{'}) g(t)=k′t+(t−m′)
变点可以是分析师通过经验给出的可能会导致趋势变化的日期,也可以通过给定趋势模型稀疏的先验 δ \delta δ自动识别出来一系列可能的变点。先验的 δ j \delta_j δj~ L a p l a c e ( 0 , τ ) Laplace(0, \tau ) Laplace(0,τ)。
函数的傅里叶展开,通俗理解就是一个周期为T=2L的函数可以表示为无限个简单的周期函数之和。季节模型是通过傅里叶级数去模拟时间序列的季节/周期效应。
S ( t ) = ∑ n = 1 N ( a n cos ( 2 π n t P ) + b n s i n ( 2 p i n t P ) ) S(t)=\sum_{n=1}^{N}(a_n\cos(\frac{2\pi nt}{P})+b_nsin(\frac{2pi nt}{P})) S(t)=n=1∑N(ancos(P2πnt)+bnsin(P2pint))
当考虑年季节性时 P = 365.25 P=365.25 P=365.25;考虑周季节性时 P = 7 P=7 P=7。
设 β = [ a 1 , b 1 , . . . , a N , b N ] β = [a1, b1, . . . , aN , bN ] β=[a1,b1,...,aN,bN],则 S ( t ) = X ( t ) β S(t)=X(t)\beta S(t)=X(t)β
这里 β \beta β的先验分布为 N ( 0 , σ 2 N(0,\sigma^{2} N(0,σ2)。
不同国家有不同的传统节假日,节假日模型可以考虑不同国家的节假日效应。对于每个节假日 i i i, D i D_i Di表示其对应的日期,定义指示函数去表示时间 t t t是否为节假日日期。 κ \kappa κ为节假日的影响效应。
Z ( t ) = [ 1 ( t ∈ D 1 ) , . . . , 1 ( t ∈ D L ) ] Z(t) = [1(t ∈ D1), . . . , 1(t ∈ DL)] Z(t)=[1(t∈D1),...,1(t∈DL)]
于是,节假日模型形式为:
h ( t ) = Z ( t ) κ h(t) = Z(t)\kappa h(t)=Z(t)κ
其中 κ \kappa κ~ N o r m a l ( 0 , ν 2 ) Normal(0,\nu^{2}) Normal(0,ν2)
τ : \tau: τ:调节趋势模型的flexibility
σ : \sigma: σ:调节季节模型的strength
Prophet(self, growth=u'linear', changepoints=None, n_changepoints=25,
yearly_seasonality=u'auto', weekly_seasonality=u'auto',
daily_seasonality=u'auto', holidays=None, seasonality_prior_scale=10.0,
holidays_prior_scale=10.0, changepoint_prior_scale=0.05,
mcmc_samples=0, interval_width=0.8, uncertainty_samples=1000)
# 加载包
import pandas as pd
from fbprophet import Prophet
# DATA为prophet包里面给的示例数据
DATA = pd.read_csv(
'data.csv',
parse_dates=['ds'],
)
# 饱和回归模型demo
m = Prophet(growth='logistic')
N = DATA.shape[0]
DATA_history = DATA.head(N // 2).copy()
DATA_future = DATA.tail(N // 2).copy()
history = DATA_history
future = DATA_future
# 给定指标的floor,capacity值
history['floor'] = history['y'].min()
history['cap'] = history['y'].max()
future['floor'] = history['floor'].mean()*1.05
future['cap'] = history['cap'].mean()*1.05
# 拟合模型
m.fit(history)
fcst1 = m.predict(future)
fcst1.head()
# 节假日设置
holidays = pd.DataFrame({
'ds': pd.to_datetime(['2017-01-02']),
'holiday': ['special_day'],
'prior_scale': [4.],
'lower_window':-2,
'upper_window':2
})
m = Prophet(holidays=holidays, yearly_seasonality=False)
# 添加月季节性
m.add_seasonality(name='monthly', period=30, fourier_order=5,
prior_scale=2.)
# 拟合模型
m.fit(history)
fcst2 = m.predict(future)
fcst2.head()
# 拟合值=趋势效应+节假日效应+季节效应
fcst2[['trend','holidays','seasonalities']].sum(axis=1)==fcst2['yhat']
在实际中,运营指标之间也可能存在一定的相关性。例如:收入可能会受到客单价、客户量等因素的影响。在进行Prophet模型预测时,除了考虑趋势效应、季节效应、节假日效应,也可以同事考虑一些外生变量对运营指标的影响。add_regressor(m, name, prior.scale = NULL, standardize = 'auto' )
在原数据框中增加一列作为解释变量,当参数standardize='auto’时,这个解释变量将会被标准化(除非它是二进制),给定浮动比例参数时回归系数也就确定了,降低浮动比例会产生较大的调整,当不指定浮动比例时,默认为holidays.prior.scale的值。
m = Prophet(holidays=holidays, yearly_seasonality=False)
# 添加外生变量
m.add_regressor('binary_feature', prior_scale=0.2)
m.add_regressor('numeric_feature', prior_scale=0.5)
m.add_regressor('binary_feature2', standardize=True)
df = DATA.copy()
df['binary_feature'] = [0] * 255 + [1] * 255
df['numeric_feature'] = range(510)
df['binary_feature2'] = [1] * 100 + [0] * 410
m.fit(df)
future['binary_feature']=1
future['numeric_feature']=49
future['binary_feature2']=0
fcst3 = m.predict(future)
# 拟合值=趋势效应+节假日效应+季节效应+外生效应
fcst3[['trend','holidays','seasonalities','extra_regressors']].sum(axis=1)==fcst2['yhat']
[1]:https://peerj.com/preprints/3190v2/
[2]: https://facebook.github.io/prophet/