Prophet 非日频数据预测实战亚日数据、规律缺失、月度数据与聚合节假日处理【免费下载链接】prophetTool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth.项目地址: https://gitcode.com/gh_mirrors/pro/prophetProphet 官方文档《Non-Daily Data》系统讲解了当时间序列的采样频率不是自然日每天一条时如何正确建模与预测涵盖亚日数据sub-daily、规律缺失数据regular gaps、月度数据monthly以及聚合数据的节假日处理四大场景。本文以该文档为骨架结合仓库中 Python 实现 与 R 实现 的源码细节展开帮助你在实际项目中避免日季节性不可识别月度数据做日频预测出现怪异结果等典型陷阱掌握正确的预测窗口与频率设置方法。核心前提Prophet 的连续时间模型与ds时间戳约定Prophet 的底层模型是连续时间模型它对数据本身的采样频率并不敏感因此理论上可以处理任意粒度的数据。前提是输入的数据框df必须包含ds列且时间戳遵循YYYY-MM-DD HH:MM:SS的格式。仓库内置的示例数据 examples/example_yosemite_temps.csv约塞米蒂国家公园每日温度5 分钟分辨率正是这种格式的典型代表。从源码看fit与predict阶段都会调用setup_dataframe对ds列做规范化处理见 forecaster.py时间戳统一经pd.to_datetime解析排序后重置索引并生成t、y_scaled等内部列。需要注意两个限制ds列不允许带时区Column ds has timezone specified会直接报错R 版本中历史日期会被统一处理为GMT时区ds列不允许出现 NaN否则同样会抛出异常。因此无论数据是分钟级、小时级还是月度级都要确保ds是干净、无时区、可被to_datetime解析的时间戳。亚日数据Sub-daily Data自动启用日季节性当时间序列的观测频率小于一天如 5 分钟、每小时时Prophet 会自动拟合日季节性daily seasonality。这一行为由set_auto_seasonalities中的规则驱动见 forecaster.py当日历史跨度 2 天、且历史数据中相邻时间点的最小间隔 1 天时自动启用日季节性默认使用 4 阶傅里叶项period 1fourier_order 4。同理周季节性3 阶要求历史跨度 2 周且最小间隔 7 天年季节性10 阶要求历史跨度 2 年。这些都是自动判断的除非你显式传入daily_seasonalityTrue/False覆盖。下面用约塞米蒂温度数据5 分钟分辨率完整演示亚日数据的拟合与预测。Python 版本df pd.read_csv(examples/example_yosemite_temps.csv) m Prophet(changepoint_prior_scale0.01).fit(df) future m.make_future_dataframe(periods300, freqH) # 预测未来 300 小时 fcst m.predict(future) fig m.plot(fcst)R 版本df - read.csv(examples/example_yosemite_temps.csv) m - prophet(df, changepoint.prior.scale0.01) future - make_future_dataframe(m, periods 300, freq 60 * 60) fcst - predict(m, future) plot(m, fcst)这里freq的含义是未来每个预测点的间隔。Python 端freqH表示每小时一个点periods300即预测未来 300 小时R 端freq 60 * 60表示以 3600 秒1 小时为步长。changepoint_prior_scale0.01是一个相对较小的趋势变点先验尺度用于抑制温度序列中的过拟合该参数在 forecaster.py 的构造函数文档中解释为调节自动变点选择灵活度。由于数据是亚日频率日季节性会被自动建模预测结果能很好跟随每日的温度波动曲线。日季节性本身可以通过组件图单独查看# Python fig m.plot_components(fcst)# R prophet_plot_components(m, fcst)组件图会分别展示趋势trend与日季节性daily两条曲线方便你确认模型学到的每日温度规律是否符合直觉。规律缺失数据Data with Regular Gaps只在有历史的时段内做预测如果历史数据存在规律性的缺失窗口直接预测会产生很差的结果。文档以每天只有 0 点到 6 点有观测为例先把数据过滤成只保留凌晨时段再照常拟合与预测# Python df2 df.copy() df2[ds] pd.to_datetime(df2[ds]) df2 df2[df2[ds].dt.hour 6] # 只保留 0-5 点的观测 m Prophet().fit(df2) future m.make_future_dataframe(periods300, freqH) fcst m.predict(future) fig m.plot(fcst)# R df2 - df %% mutate(ds as.POSIXct(ds, tzGMT)) %% filter(as.numeric(format(ds, %H)) 6) m - prophet(df2) future - make_future_dataframe(m, periods 300, freq 60 * 60) fcst - predict(m, future) plot(m, fcst)从图中可以看到未来预测区间出现了远超历史幅度的剧烈波动。根因在于我们给一条只覆盖一天中部分时段的序列拟合了完整的日周期日季节性在 6 点到 24 点这段没有数据约束的区间内完全不可识别自然无法被可靠估计。解决方案很直接只对历史数据存在的时段做预测。在本例中即把future数据框同样过滤为 0-5 点# Python future2 future.copy() future2 future2[future2[ds].dt.hour 6] fcst m.predict(future2) fig m.plot(fcst)# R future2 - future %% filter(as.numeric(format(ds, %H)) 6) fcst - predict(m, future2) plot(m, fcst)限制预测窗口后结果明显恢复正常。这一原则可以推广到其他规律缺失场景例如历史数据只包含工作日时周季节性对周末而言同样没有数据约束因此预测应只针对工作日进行否则周末的周季节性分量会被过度外推而产生虚假波动。月度数据Monthly Data连续时间模型下的频率不匹配陷阱Prophet 可以拟合月度数据但底层是连续时间模型——如果用月度数据拟合、再请求日频预测往往得到怪异的结果。文档以美国零售销售额数据examples/example_retail_sales.csv为例先演示了错误做法拟合后直接预测未来 10 年periods 3652即按天生成未来日期# Python df pd.read_csv(examples/example_retail_sales.csv) m Prophet(seasonality_modemultiplicative).fit(df) future m.make_future_dataframe(periods3652) fcst m.predict(future) fig m.plot(fcst)# R df - read.csv(examples/example_retail_sales.csv) m - prophet(df, seasonality.mode multiplicative) future - make_future_dataframe(m, periods 3652) fcst - predict(m, future) plot(m, fcst)注意这里使用了seasonality_modemultiplicativeR 中为seasonality.mode因为零售销售额的季节效应通常随趋势规模放大乘法季节性比加法更贴合——该参数在 forecaster.py 中有明确说明默认是additive。这与上一节规律缺失是同一个问题月度序列每年只有 12 个观测点如每月 1 号拟合年季节性时非月初的日子完全没有数据约束年季节性在这些日期上不可识别、容易过拟合。用 MCMC 抽样可以直观地看到这一点——季节性分量的后验不确定性在每月月初有数据处很小在两次观测之间却急剧膨胀# Python m Prophet(seasonality_modemultiplicative, mcmc_samples300).fit(df, show_progressFalse) fcst m.predict(future) fig m.plot_components(fcst)# R m - prophet(df, seasonality.mode multiplicative, mcmc.samples 300) fcst - predict(m, future) prophet_plot_components(m, fcst)运行时会看到类似WARNING:pystan:481 of 600 iterations saturated the maximum tree depth of 10 (80.2 %)的提示这说明在高度不可识别的参数空间里采样器需要更大的max_treedepth才能正常收敛——这本身就是数据稀疏、分量难估的一个佐证。正确做法只生成月度粒度的预测结论是月度数据只做月度预测。做法是把频率直接传给make_future_dataframe# Python future m.make_future_dataframe(periods120, freqMS) # 未来 120 个月每月 1 号 fcst m.predict(future) fig m.plot(fcst)# R future - make_future_dataframe(m, periods 120, freq month) fcst - predict(m, future) plot(m, fcst)make_future_dataframe的底层实现可以印证频率参数的作用。Python 版见 forecaster.py签名是def make_future_dataframe(self, periods, freqD, include_historyTrue):periods向前预测多少个周期freqpd.date_range支持的任意合法频率字符串默认D按天include_history是否把历史日期一并放进预测数据框默认True若freqNone模型会尝试从历史日期尾部推断频率pd.infer_freq推断失败会直接报错。实现上它会从history_dates的最大值开始用pd.date_range(startlast_date, periodsperiods1, freqfreq)生成日期去掉与末条历史相等的那一个点再按需拼接历史日期。测试用例 test_prophet.py 覆盖了freqD、freqpd.tseries.offsets.MonthEnd(1)、include_historyTrue/False等场景。R 版见 prophet.R签名是make_future_dataframe(m, periods, freq day, include_history TRUE)内部通过seq(max(m$history.dates), length.out periods 1, by freq)生成日期序列并丢弃第一条与最后历史日期重合同样把历史日期拼接到结果中。它还有一个向后兼容的细节freq m会被自动重写为month。在 Python 中freq可以是 pandas 官方频率字符串表中的任意一项。文档特别提示MS表示 month-start即把数据点放在每月 1 号正好与历史数据中每月第一天有观测的约定对齐如果历史观测在月末则应使用MEmonth-end之类的对应别名保证预测点与历史点的相位一致。替代方案用 12 个月份哑变量替代年季节性月度数据还有另一种建模思路不用年季节性改用 12 个二值额外回归器例如is_jan、is_feb……is_dec其中is_jan在日期属于 1 月时取 1、否则取 0。这样做的好处是从根本上规避月内区间不可识别的问题——因为每个回归器只在整月内取值恒定不存在月内自由波动。实现方式如下# Python for month in [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]: df[month] (df[ds].dt.month_name().str.lower() month).astype(int) m.add_regressor(month)# R for (i in 1:12) { df[[paste0(is_, month.abb[i])]] - as.numeric(as.numeric(format(df$ds, %m)) i) m - add_regressor(m, paste0(is_, month.abb[i])) }关键约束使用月份哑变量时必须设置yearly_seasonalityFalse否则年季节性与月份哑变量会同时建模、产生严重的共线性/多重共线性。add_regressor的实现细节见 forecaster.py每个回归器默认以holidays_prior_scale作为系数先验尺度prior_scalemode默认跟随seasonality_modestandardizeauto时二值回归器不会被标准化mu、std保持为 0 与 1因此哑变量的系数直接就是该月的加性/乘性偏移量解释性很强。聚合数据的节假日处理Holidays with Aggregated Data当数据被聚合到周频或月频时节假日处理有一个容易被忽略的细节Prophet 的节假日效应只作用于你指定的那一天即holidays表中该节假日的ds日期。如果数据已经聚合例如周频序列的每个数据点都落在周日那么一个周一的法定节假日就不会对任何观测产生影响——因为没有任何历史数据点正好落在该日期上该效应在模型里等同无效。处理方法把节假日手动移动到历史数据框中你希望产生效应的那个日期。例如某个假期效应希望体现在该周周日的观测上就把这个假期的ds改为对应的周日再传入holidays参数。同时文档也给出一个实用提示对于周频或月频的聚合数据绝大多数节假日效应已经被年季节性很好地吸收了年季节性刻画的是每年同一天的规律而多数假期每年落在相似的日期区间因此通常只需要为那些在整条时间序列中出现在不同周/不同月份的浮动节假日如复活节、感恩节等日期漂移的节日手动添加节假日项。节假日与聚合数据的完整交互机制可以参考 prophet.R 中construct_holiday_dataframe、make_holiday_features等函数以及 Python 侧 make_holidays.py 的实现——节假日最终会被展开成覆盖lower_window/upper_window范围的哑变量特征这一点在聚合数据下同样适用。实践要点总结亚日数据直接可用ds用YYYY-MM-DD HH:MM:SS格式模型会自动启用日季节性历史 2 天且间隔 1 天无需任何额外配置。规律缺失数据必须限制预测窗口日周期只在有数据的时段内可识别预测时要把future过滤到与历史相同的时段如每天 0-5 点、仅工作日。月度数据只做月频预测通过make_future_dataframe的freq参数指定与历史一致的频率Python 用MS/ME等 pandas 频率别名R 用month不要用日频预测月拟合模型。月内不可识别问题可用 12 个月份哑变量规避此时务必关闭yearly_seasonality。聚合数据的节假日要搬到观测日只有落在历史数据点日期上的节假日才会产生效应多数固定假日可交给年季节性浮动假日才需要手动添加。理解连续时间模型Prophet 不关心原始采样频率但这既是灵活性也是风险来源——预测频率必须与历史数据的有效信息粒度匹配。更完整的非日频数据示例与完整 notebook 可参考仓库中的 notebooks/non-daily_data.ipynbPython 版、R 版见 R/vignettes/quick_start.Rmd对应的函数级文档见 man/make_future_dataframe.Rd 与 man/setup_dataframe.Rd。【免费下载链接】prophetTool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth.项目地址: https://gitcode.com/gh_mirrors/pro/prophet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考