去年第四季度,一家中型消费品企业的供应链总监在内部会议上拍着桌子问我:“为什么我们的需求预测总是在节假日前两个月开始跑偏?每年都偏,每年都要手动修正。”我调出他们过去三年的销售数据,把BI平台里的时间序列分解图投在屏幕上。趋势线稳健向上,但季节分量像心电图一样剧烈波动,春节囤货高峰、618大促前置、双十一脉冲,三个周期叠加在一起,让任何不处理季节性的预测模型都成了“算命骰子”。那次分析之后我开始系统地研究一件事:在BI平台的时间序列分析中,剔除季节性因素到底对预测准确率有多大影响?什么场景该剔?什么场景不该剔?剔错了有什么后果?

这篇文章不是教科书式的理论复述,而是基于我在帆软九数云、FineBI等项目交付中遇到的真实案例,结合多个行业的预测复盘数据,试图回答上面的问题。如果你想提升BI看板里预测曲线的可信度,或者想搞清楚什么时候该做季节调整、什么时候该保留原始信号,这篇内容应该能给你一些可落地的判断框架。
先直接给出我在实际项目中的基本判断,方便你快速抓住主线:
所以结论不是一个“要不要剔除”的二选一问题,而是一个“在什么业务目标下、用什么方法、剔到什么程度、如何还原”的多决策链条。下面我们会逐一拆解。

时间序列分析教科书会告诉你:一个时间序列可以被分解为趋势、季节、周期和残差四个成分。预测模型的理想状态是学习到稳定的趋势和周期规律,而季节性是“可预见的重复波动”,它不应该占用模型的参数容量。这个观点在统计学上是成立的,也是ARIMA模型要求序列平稳化的理论基础。我在2019年第一次接触供应链预测时,带我的技术负责人也是这么教的:“先把季节性干掉,让模型专心学趋势,学好了再把季节加回去。”
但真实业务场景比教科书复杂得多。云仓行业的日订单量,既受电商平台大促影响(618、双十一、年货节),又受天气、疫情封控、直播爆单等随机事件冲击。这些事件中,只有平台大促勉强算“季节性”,它固定日期、固定周期、每年重复。而直播爆单可能只出现在某个头部主播的档期里,根本不具备规律性。如果你机械地套用季节差分或STL分解,模型会把这些非规律性事件也当作“季节”来处理,导致剔除了不该剔的信号。
我在服务洁识供应链和先飞数智物流的过程中,观察到云仓行业预测的三个典型困境:
这意味着:云仓行业既需要季节调整来分解误差来源,又不能无脑调整,因为某些SKU的季节性本身就是核心预测信号
不是噪声。

理论归理论,实际在BI平台上做时间序列预测时,操作路径往往是非常受限的。多数BI工具的第一步是让你选择“预测方法”:移动平均、指数平滑、线性回归。稍微进阶一些的会内置ARIMA或Prophet。但这些模型对季节性的处理方式各不相同:
更重要的是,BI平台上的“一键预测”按钮背后,往往隐藏了季节调整的逻辑,用户根本不知道模型到底剔没剔、剔了多少。这种黑箱操作在需要解释预测结果的业务场景里会带来很大的信任问题。
接下来我想具体讲几个在项目中真实遇到的误区。这些误区不是理论推演,而是我或者我的同事在实际交付中踩过的,后来复盘时才发现根因都在季节处理上。
2019年我在一个快消品牌做预测项目时,技术团队坚持用Prophet,理由是“Prophet内置了季节建模,不需要人工干预”。第一批预测结果出来,MAPE大约22%,不符合验收标准。查了两个礼拜才发现:该品牌每年3月和9月有两次大型订货会,这是固定的商业日历事件,但Prophet把它和自然月份的季节性混在一起学习了。3月的预测严重偏高,9月又偏低,因为模型没有区分“订货会带来的脉冲”和“自然季节波动”。
判断逻辑:任何自动季节检测算法,都基于“频率×周期”的假设。如果你的业务季节性是人为定义的(比如订货会、招标周期、预算发放周期),算法很可能会误判。这种情况下反而需要手动剔除这些事件因素,或者把它们作为外部回归变量加入模型。

这是另一个极端,常见于有统计学背景的分析师。我在云港物流项目中见过一个案例:分析师用了极端的季节差分方法(连续两次季节差分),把时序变得非常“干净”,模型训练误差也降到了个位数。但问题来了:当需要把预测结果还原时,叠加上去的季节分量是“算法拟合出来的季节”,而不是“真实的商业季节”。结果是:预测值完美错过了春节前的入仓高峰,因为算法拟合的季节形态已经把2020年疫情冲击下的异常波动当成了“正常季节”的一部分。
判断逻辑:季节分量本身也有信噪比。如果你剔除季节性的方法过度拟合了历史数据中的“异常季节”,那么剔除反而引入了新的偏差。正确的做法是:只在季节模式稳定的情况下做剔除,且保留残差供人工判断。
很多BI用户习惯在公式栏里写“销售额同比增长率”,觉得这就是在做季节调整了。同比确实消除了年周期效应,但它有两个致命问题:一是只能和历史同期比,无法预测未来;二是把趋势变化也一并抹掉了。如果你的业务处于快速成长期,同比会说“增长了30%”,但时间序列分解会告诉你:趋势贡献了20%,季节贡献了10%。这种区分对于预测至关重要。

很多人以为季节调整做一次就行了,建模前处理一遍,然后模型跑下去就行。现实中季节模式是会变的。我在包装行业的精益生产项目中观察到:工厂的OEE(设备综合效率)原本有稳定的夏季低谷(高温导致设备散热问题),但产线做了自动化改造之后,这个季节规律被打破了。如果用改造前的季节参数去做调整,未来的预测会持续高估夏季效率。
判断逻辑:季节调整不是一个离线的预处理步骤,而是需要周期性验证和更新的动态过程。如果业务结构发生了重大变化(产线改造、渠道重组、产品换季),必须重新评估旧的季节参数是否仍然成立。
基于以上误区的复盘,我后来抽象出了一个四维判断框架。每当遇到新的预测项目时,我会依次过这四个维度,决定季节性处理的策略。
预测窗口越短,越需要保留季节信号(甚至故意不剔)。
如果你的预测用于下周的入仓计划、明天的出仓排班,那么季节性(甚至更短周期的星期效应)就是你要预测的核心信号本身。此时你做季节调整等同于把自己的预测目标给删了。
预测窗口越长(季度、年度),越有必要剔除季节,让模型聚焦在趋势上。
因为长周期预测的核心价值是判断“整体方向向上还是向下”,季节波动只是噪声。
| 预测跨度 | 季节性处理建议 | 典型场景 |
|---|---|---|
| 1-7天 | 保留甚至强化季节信号;关注星期效应、节假日效应 | 日度出仓排班、直播补货 |
| 1-4周 | 保留大周期季节,平滑微小波动 | 周度入仓计划、安全库存设定 |
| 1-12个月 | 建议剔除稳定季节分量,保留残差 | 月度销售预测、预算编制 |
| 1年以上 | 必须剔除季节,聚焦趋势和结构变化 | 产能规划、战略投资 |
用历史数据滚动验证:取出前3年的数据,分别拟合季节分量,看3年间季节峰值是否出现在同一时段、幅度是否稳定。如果答案是肯定的,放心剔除。如果季节模式年际变化大,说明你的业务可能正在经历结构性变化,或者存在大量一次性事件冲击,此时机械剔除反而有风险。
我在包装行业设备OEE数据上做过这个验证:改造前3年的夏季低谷非常稳定(6-8月OEE下降7%-9%),改造后这个规律消失。如果在改造后继续按旧参数剔除,预测偏差会逐年累积。

这是经常被技术团队忽略的维度。预测结果的使用者不同,对“季节性是否应该在最终结果里体现”的要求完全不同:
如果数据只有2-3年的历史,且颗粒度是“月”级别,总共也就24-36个数据点。这种体量下,季节分解的可靠性本身就不高(STL需要至少2-3个完整周期才能稳定)。这个时候强行剔除季节性,大概率是在“用噪声拟合噪声”。不如保留原始序列,在解释预测结果时口头说明季节性影响。
如果数据颗粒度是“日”级别、历史覆盖3年以上(1000+数据点),季节分解的效果会稳定很多。
为了不空谈框架,我想完整复盘一个在云港物流做过的预测对照实验。这个实验没有发论文级别的严谨控制,但胜在用的是真实业务数据和真实业务决策环境,能反映BI平台上的实际表现。
数据:某区域云仓2020年1月-2023年6月的日订单量,共1278天。数据包含两个完整的大促周期(2021、2022),以及2020年上半年的疫情冲击异常值。
预测目标:预测2023年7-12月的日订单量。
模型:统一使用ARIMA(2,1,2)。
处理方案:
方案D的设计初衷是:分解出的季节分量可能被2020年异常值污染,改用近两年平均季节分量更稳健。
| 方案 | MAPE(%) | 大促月MAPE(11月)(%) | 非大促月MAPE(%) |
|---|---|---|---|
| A:不处理 | 24.8 | 41.2 | 18.5 |
| B:季节差分 | 19.3 | 22.7 | 16.8 |
| C:STL+原季节还原 | 17.1 | 20.4 | 15.2 |
| D:STL+近两年平均季节还原 | 15.6 | 14.8 | 15.9 |
几个关键发现:

在这个实验中,我们用lag=7的季节差分处理了星期效应。如果不处理,周一和周六的订单量差异能到30%以上(周一少、周六多,因为是直播发货的集中节点)。这种“周内季节”在月粒度分析中会被平均掉,但在日粒度预测中如果不处理,误差会系统性偏高。BI平台上做日预测时必须检查星期效应,这是很多用户忽略的细节。

这一节我想给出可直接落地的操作指南。不同阶段、不同资源的团队可以从中选取适合自己的部分。
能做的事:
不能做的事:
这是更理想的配置。流程建议如下:
# 示例:Python中STL分解的基本调用(示意,非完整生产代码) from statsmodels.tsa.seasonal import STL import pandas as pd df为包含日期和销量的DataFrame,已设置日期为索引 stl = STL(df['sales'], period=365) # 日数据的年周期 result = stl.fit() df['trend'] = result.trend df['seasonal'] = result.seasonal df['resid'] = result.resid df['deseasonalized'] = df['sales'] - df['seasonal'] # 加法模型
建议从三个层面设计:

这篇文章的标题问的是“对预测准确率的影响”,但经过前面六节的拆解,你应该已经意识到:准确率不是唯一的目标函数。这里我想展开讲几个在实战中经常遇到的取舍场景,以及我在类似情况下是怎么做的。
这是最常见的取舍。业务部门希望你在大促前告诉他“峰值会到多少”,他们需要的是点预测的峰值精度。而财务部门需要一条平滑的趋势线来判断全年收入规模,他们需要的是平均预测偏差最小。
我的做法是:不同部门看不同的预测版本,但底层只有一套模型。 模型输入的是剔季节后的数据,模型输出趋势预测,然后叠加两套不同的季节还原:
这不是在操纵数据,而是在用同一个模型服务于不同的决策需求。关键是在看板上标注清楚季节因子的选取口径,让使用者知道自己看的是什么。
算法能处理规律性的季节性,但当业务发生结构性变化时,算法是无感的。2020年疫情初期,所有基于历史季节模式的预测全部失效。当时我们团队的做法是:暂停自动季节调整,改为每两周一次的手工调整,由业务运营根据最新发货数据和封控政策输入一个“季节修正系数”。这个系数本质上是人工经验对算法输出的矫正。
这听起来很“土”,但在剧烈变化期反而是最有效的。关键是要设计好“何时切换回自动模式”的触发条件,我们设定的是“连续4周自动预测MAPE低于20%时恢复自动模式”。

不是所有SKU都有好几年的历史数据。新品、长尾商品可能只有几个月甚至几周的数据。对这类SKU强行做季节分解毫无意义。我的建议是:
这个问题经常被忽略,但它直接影响预测准确率。加法模型假设季节波动幅度不随趋势变化(每年旺季比淡季多卖500件);乘法模型假设季节波动随趋势成比例放大(每年旺季是淡季的1.5倍)。
识别方法很简单:把数据分成前后两半,分别计算季节波动的绝对值。如果前半段和后半段的波动幅度差不多,用加法;如果后半段明显更大,用乘法。
在云仓行业的日订单量数据上,我们验证的是乘法模型更优,因为随着电商体量增长,大促的脉冲绝对值也在增长。但在包装行业的OEE数据上,加法模型更合适,设备的季节性效率降低是绝对值固定的。

因为九数云是我们帆软自己的SAAS BI产品,我对它的能力边界比较清楚。这一节我会具体讲讲在这样一个零代码BI平台上,怎么把前面讲的季节调整逻辑落地。这些思路同样适用于FineBI或简道云的数据分析模块。
九数云的分析空间支持对数据表添加计算字段。你可以创建以下指标作为季节观测的基础:
这些公式在九数云里通过拖拽字段和简单函数就能实现,不需要写SQL。
我建议做一个专门的“季节稳定性监控”仪表板,包含以下组件:
这个看板不需要天天看,但建议每个季度(旺季前一个月)更新一次数据,判断季节参数是否需要调整。
九数云最近上线的AI助手功能(可在仪表板右侧唤起),支持对异常波动做智能总结。当你发现某个月的实际值严重偏离预测时,可以直接让AI帮你分析可能的归因,它会自动关联你数据集中的维度字段,判断是促销活动影响、天气影响还是季节参数出了问题。这个功能目前还在内测,但在我们内部的几个客户项目里已经展现了不错的归因能力。
结合前文说的“手工判断vs算法自动”的取舍,AI归因可以帮你缩短从“发现预测偏差”到“定位原因”的时间,让手工修正更加高效。

回到文章开头那个拍桌子的供应链总监。我后来给他看了调整季节因子后的预测报表,11月的大促预测偏差从41%降到了15%左右。他问了我一句话:“这个准确率还能再提高吗?”我回答他:“能,但性价比不一定高了。剩下的偏差主要来自直播爆单这类随机事件,不是季节调整能解决的。”
这句话也是我整篇文章想传递的最后一条经验:季节性因素剔除是提升预测准确率的重要手段,但它有自己的天花板。识别出这个天花板在哪里,比盲目追求更高的准确率重要得多。
下一步你可以做的:
预测这件事,从来不是算得越复杂越好。理解你数据里的节奏,比任何高级算法都更接近真相。
我在电商公司用Power BI做销量预测,剔除了年度季节性后,预测误差反而从12%飙升到28%。不是说季节性会干扰模型吗?是不是我剔除的方式有问题?
这是典型的「过度剔除」陷阱。你的直觉没错,季节性确实会掩盖长期趋势,但问题在于:业务增长往往与季节性高度耦合。例如,你公司的年增长率是20%,但季节性峰值(比如双11)本身也是增长的一部分。
使用简单的移动平均或差值法去除季节成分时,如果孤立地看待每个周期,可能会把「增长趋势-季节性交互」这一非线性信号也当作季节噪声去掉。我踩过的坑:在Power BI中用RUNNING_SUM做12个月移动平均后再做差分,结果把品牌自然增长的周期性脉冲(比如新品上市带来的每年Q2小高峰)一并抹平了。
后来改用STL(Seasonal-Trend decomposition using LOESS)并手动调整seasonal组件的窗口长度,让算法区分「外部季节性」和「内部季节性」。具体建议:先用傅里叶变换或自相关图观察季节周期是否稳定。
如果旺季峰值逐年递增(如2022年11月销量300万,2023年11月450万),说明季节性与趋势有交互,不要简单用减法,应改用乘法分解(如X-13ARIMA-SEATS的multiplicative模式)。
在BI平台中,Tableau可结合WINDOW_SUM与WINDOW_AVG手动计算季节指数,但更可靠的是预先在Python/R中完成STL分解,再导入BI。
我直接用了Power BI的预测选项,勾选了「季节性检测」,结果预测曲线像一条直线。我感觉内置算法太黑盒了,但自己又不会写复杂代码。我们这种非数据科学家,到底该信任BI自带的功能吗?
不推荐直接依赖BI内置的季节性自动检测,尤其是当你的数据存在多周期(周+年)或趋势突变时。
我在两个项目中做过对比:
| 方法 | 数据集 | MAPE | 训练时间(10万行) |
|---|---|---|---|
| Power BI FORECAST.ETS 自动 | 零售日销额(含周、年双周期) | 19.2% | 3秒 |
| Python STL + Prophet(手动指定双周期) | 相同数据 | 11.7% | 18秒 |
| Tableau 指数平滑(手动调alpha+gamma) | 相同数据 | 14.5% | 2秒(预处理) |
问题出在:Power BI的ETS默认使用单周期检测(通常只识别最强的周期),遇到「周-年双嵌套」时会强制合并,导致季节性剔除后残留子周期噪声。
我的做法是:在ETL阶段用SQL按周和年分组算出季节指数,将原始值除以此指数得到「去季节序列」,再丢给FORECAST.ETS,这样MAPE能降到13.1%。
如果你没有Python环境,Tableau的指数平滑参数手动调整也是可选方案:设置alpha(水平平滑)为0.1-0.3,gamma(季节性平滑)为0.01-0.05,并显式指定周期长度(如365天)。注意:BI平台计算时容易溢出,需要先将数据按天聚合。
我做零售库存预测,双11那天的销量是平时的50倍,这个巨大尖峰让季节性分解直接崩了,剩下的月份全变成锯齿形。我应该先把离群值剔除再分解季节吗?但双11本身就是每年固定的事件,算季节还是离群?
这是实战中最头秃的问题。双11这种「固定且剧烈的峰值」应该被视为「特殊季节成分」,而非离群值。我犯过的错:直接用3倍IQR过滤掉所有异常值,结果双11月份的季节指数被压低,导致下一年预测少备了30%的货。
正确流程(我在QuickSight中实现过): 1. 先对时间序列做初步分解(比如用12个月移动平均),得到趋势-周期分量。2. 计算原始值与趋势分量的比值(或差值),得到残差。3. 对残差做两轮过滤:第一轮用Z-score>3剔除真正的突发离群(如系统故障导致的零值);
第二轮保留每年固定日期附近的尖峰,并计算多年的同日期中位数,作为固定季节因子。4. 将固定季节因子纳入季节性修正,而不是简单剔除。具体数据:某鞋服品牌2020-2023年日销量,双11周销量是平时的8-12倍。用上述方法修正后,预测MAPE从34%降到17%。
而不加区分的剔除离群值后MAPE反而升高到41%。BI平台操作建议:在Power Query中创建「节假日标记」列(如双11、618),然后按节假日分组计算复合季节指数。
Tableau可使用SCRIPT调用Python的fbprophet或statsmodels的STL,并传递节假日参数。如果不用脚本,就在源数据中提前做好标记列,用WINDOW_COUNT和IF ELSE手动加权。
老板让我写报告证明「季节性调整有效」,但光说MAPE从多少降到多少不够,他觉得是偶然。有没有什么统计方法或BI可视化可以直观展示剔除前后的差异?我该用什么指标和图表来让决策者信服?
光看一个整体MAPE确实不够,需要多维度交叉验证。我总结了一套「四步验证法」,每次汇报都用这个框架: 1. 时间切片对比:将预测区间按季节拆分(如按月份)。在Power BI中用FORECAST生成剔除/不剔除两个版本,然后按月份计算平均绝对误差,做成热力图。
例如,12月误差从不剔除的±40%降到剔除后的±8%,而6月几乎不变。这就能证明季节性剔除主要改善了高峰期的预测。2. 统计显著性检验:对两个模型的残差序列做Diebold-Mariano检验(DM test)。虽然BI平台不自带,但可以在Python中计算后导入结果。
我上一次项目中DM统计量为-3.42(p<0.01),显著说明剔除后的模型预测更优。3. 业务指标关联:将预测误差转化为库存成本。假设每多备1单位库存成本$0.5,缺货损失$2。剔除季节后,过度库存天数减少32%,缺货天数减少51%,直接换算成节省$280万。老板最爱看这个。
可视化证明:在Tableau中做两个折线图重叠:真实值、剔除季节的预测、不剔除的预测。关键区域放大显示春节或双11前后。例如,2024年2月实际销量比不剔除的预测高出40%,而剔除后的预测只差5%。这样即使不懂统计的人也能一眼看出差异。
工具提示:Power BI的「预测」功能本身不输出残差,需要先用DAX计算SUMX的逐日误差,再用CALCULATE按月份聚合。Tableau可用ZN(SUM([Sales]) - LOOKUP(SUM([Sales]), -1))配合表计算做残差。
这些细节是我踩坑后总结的,文档里基本没有。


读者评论
写得真实在。我在供应链做预测三年,最头疼的就是双11和618的季节波动。文章里说的‘剔除季节性不是标准动作而是决策选择’一下点醒了我,之前一直机械地做差分,结果旺季预测总偏低。现在我会根据业务目标决定:做备货预测就保留季节信号,做长期趋势才彻底剔除。这种实战框架比教科书有用得多。
作为BI分析师,我深有同感。文章里提到的工具限制很到位:Power BI和Tableau确实只给移动平均和同比,想做STL分解还得靠Python。我在项目里吃过类似亏,用内置ARIMA自动预测,结果模型把春节和突发促销混在一起。现在会先用九数云或Python做预分解,再回传BI做展示。强烈建议BI厂商把季节分解功能做得更透明些。
文章案例详实,但有个点没深入:剔除季节性后预测准确率提升了,可还原时的季节分量如果本身就有‘脏数据’怎么办?比如2020年疫情导致的异常峰值,模型会把它当作正常季节模式,还原后预测反而更偏。文中只提了‘只保留残差供人工判断’,但实战中多数团队没有精力做人工复核。这个风险对业务决策的影响可能被低估了。