简介围绕新能源汽车销量预测这一现实需求这份PDF文档提供了一套基于ARIMA模型的时间序列分析实例适合汽车行业研究人员、数据分析学习者及企业规划人员参考。文中以2014年1月至2019年5月国内新能源汽车月度销量为样本完整展示了序列平稳化处理、差分运算、ACF/PACF识别以及ARIMA(1,0,0)(1,1,0)模型构建与验证过程并给出2019年3月至5月的预测对照结果便于读者理解模型参数含义并复现预测流程。资源为单文件PDF压缩包仅1.11MB包含数据来源说明、SPSS操作输出、拟合效果图和预测误差表可直接阅读或打印。当前已有479人学习下载可作为课题研究、课程作业或企业销量预测初期的快捷方法参考。1. ARIMA凭什么做新能源汽车销量预测小样本时序的“老实人”做新能源销量预测的人第一反应多半是LSTM或者Transformer。但真拿到手只有三年左右的月度数据、中间还隔着补贴退坡和价格战你会发现这些深度学习模型根本吃不饱跑出来的曲线比抛硬币还不靠谱。反而是ARIMA这种上世纪七十年代的“老模型”在样本量小、规律偏线性、需要解释性强的场景里特别能打。它不追求学习“所有复杂因素”只抓住一条主线当前值等于过去值的加权记忆加上随机扰动。这份PDF标题把它用在新能源汽车销量上核心要解决的问题很具体——把月度零售或上险量中隐含的趋势、季节性和随机波动拆开然后给出未来3到6个月的带区间的预测数字。适合的人群也很明确车企市场部做目标拆解的人、做供应链备产计划的人以及想给投资判断加一个量化锚点的分析岗。它不是万能药但它是你被复杂模型反复打脸之后最值得回头捡起来的那个基线模型。2. 前置功课把销量序列洗干净再谈建模2.1 数据口径零售、上险、批发的差别直接决定预测结果新能源汽车销量预测最大的坑不是模型而是口径。零售数据来自乘联会上险数据来自保险机构批发数据来自厂家自己。三者差多少季度末冲量的时候批发可以比上险高出两倍。你拿批发数据建的ARIMA模型预测出来的是“厂家压库节奏”不是“消费者真实买单量”。我一般会建议先锚定两个口径之一做市场研判用上险量做生产计划用批发量。选定后就不要换。如果你的历史数据里混了口径比如前年是批发、去年是零售ARIMA的差分和季节项会被这种“伪突变”骗得一塌糊涂。PDF任何一个建模任务第一步永远是画全序列图肉眼看有没有断崖或台阶。这一步不能省后面所有检验统计量都是在给视觉判断打下手。2.2 缺失值和异常值宁插值不删点但政策断裂带要单独处理处理缺失值常见做法是线性插值还是用前值填充对销量序列我倾向用前向填充加一个“该月是否为补录”的哑变量而不是纯线性插值。因为销量有假期效应和季度末效应线性插值会把2月的低值拖向3月的高值人为抹平波动。异常值呢两个月销量从20万跌到8万如果发生在补贴政策切换的节点那不是异常值是真实的政策冲击。你要做的是打一个“断点虚拟变量”让模型知道这个低点不是随机扰动。如果用3σ原则直接把它掐平模型会以为销量天生就该在20万附近震荡后面每一步预测都会偏高。所以预处理这张工作台永远分两堆技术异常数据录入错、漏报走插值业务异常政策、价格战留待建模时加外生变量处理。2.3 时间索引的坑月度数据必须对齐自然月ARIMA对时间索引极其敏感。你的数据如果是“2023年1月、2月……”对应的时间索引应该是月末日期比如2023-01-31而不是字符串“202301”或数字1、2、3。索引错了最直接的问题是季节性分解时周期数识别错、滞后阶数对不上、甚至statsmodels直接报错让你改频率。对于月度销量数据周期设为12是标准做法。代码里这一步要提前做import pandas as pd df pd.read_csv(sales_monthly.csv, parse_dates[month]) df[month] pd.to_datetime(df[month]) df df.set_index(month).asfreq(MS) # 对齐到每月1号 df[sales] df[sales].ffill() # 替换缺失值 # 丢到季节性分解里先看体检报告 from statsmodels.tsa.seasonal import seasonal_decompose result seasonal_decompose(df[sales], modeladditive, period12) result.plot()这里逻辑分三层第一层asfreq(MS)把不规则的日期索引强制变成以月为单位的、连续的时间轴缺失的月份会被插入NaN这一步是把“脏日历”变成“干净日历”的关键第二层ffill()是处理缺失值的保守方案适合小幅空缺如果缺失超过3个月建议改用插值第三层seasonal_decompose是建模前的体检——如果分解出来的残差项带周期性说明这个序列的季节性不稳定后面定阶时季节参数的p/q要放大。2.4 样本量底线数据不满24个月就别硬上ARIMAARIMA对样本量的要求被人为低估了。很多论文拿12个点就建ARIMA那是训练集和测试集混在一起玩过拟合。我踩过的底线是非季节ARIMA至少24个月、带季节性成分至少36个月低于这个数预测区间会宽到没有业务意义。如果你的数据只有两年优先拆成两步走先用简单指数平滑看一眼趋势方向再用ARIMA(1,1,1)或ARIMA(2,1,2)这种低阶配置做试探性预测不要一上来就搜出个(5,1,7)。记住一个血泪经验采样点越少模型阶数越高翻车越快。3. 定阶不是玄学ADF检验、ACF/PACF与信息准则的配合3.1 平稳性检验ADF的p值让你决定差分几轮ARIMA的I就是差分次数d。绝大多数销量序列是非平稳的——有上升趋势、有季节性波动。做差分前先跑ADF检验这是最标准的开工动作。from statsmodels.tsa.stattools import adfuller # 原始序列 adf_orig adfuller(df[sales].dropna()) print(f原始序列 p 值: {adf_orig[1]:.4f}) # 一阶差分 diff1 df[sales].diff().dropna() adf_diff1 adfuller(diff1) print(f一阶差分 p 值: {adf_diff1[1]:.4f})用的时候记住判断逻辑p值小于0.05就认为拒绝“存在单位根”的原假设也就是序列平稳了大于0.05就继续差分。新能源汽车月度销量通常在一阶差分后就平稳因为本身有明确的增长斜率差分一次把斜率去掉就变成围绕一个均值波动的随机序列。不要盲目二阶差分那个会把序列差成纯噪声回头再看ACF图你会怀疑自己拿的是白噪声数据。3.2 ACF和PACF怎么看拖尾、截尾和决策表定阶是ARIMA最劝退新人的地方。其实就一张决策表自相关函数ACF拖尾、偏自相关函数PACF截尾用ARACF截尾、PACF拖尾用MA两个都拖尾用ARMA就是p和q都不为0。from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 一阶差分后的数据 plot_acf(diff1, lags24) plot_pacf(diff1, lags24)具体读图口诀PACF在滞后几阶后突然掉到蓝色置信区间以内这个阶数就是p的候选ACF那边同理掉进区间的阶数就是q的候选。实际看的时候别太较真因为销量数据通常没有教科书上那种完美的“截尾”更多是“快速衰减”的形态。这时候p和q从1开始往上试探配合AIC筛选这是最务实的路径。ACF和PACF在这里是给候选范围划线不是直接定音锤。3.3 信息准则选型AIC与BIC谁更该信AIC和BIC都是“越小越好”但它们的惩罚力度不同。AIC惩罚项是2kk是参数个数BIC惩罚项是k·ln(n)n是样本量样本量一大BIC的惩罚明显更重。换句话说AIC倾向于选更复杂的模型BIC更保守。对销量预测这种场景我更站BIC。原因很简单样本量通常不够大复杂的模型在训练集上拟合得很美在测试集上就会表演跳水。跑自动定阶的时候我固定先用BIC给出的结果再人工看一遍AIC给出的候选如果两者阶数差距很大说明数据本身信息量不够保守的BIC结果优先。import itertools from statsmodels.tsa.arima.model import ARIMA best_aic, best_bic float(inf), float(inf) best_order_aic, best_order_bic None, None for p in range(0, 4): for q in range(0, 4): try: model ARIMA(df[sales].dropna(), order(p, 1, q)) result model.fit() if result.aic best_aic: best_aic result.aic best_order_aic (p, 1, q) if result.bic best_bic: best_bic result.bic best_order_bic (p, 1, q) except Exception: continue print(fAIC 最优: {best_order_aic}, BIC 最优: {best_order_bic})这段代码的逻辑就是暴力枚举限制p和q在0到3之间属于最安全的搜索空间。超过这个范围的ARIMA在销量预测上基本就是在拟合噪声了。如果你把范围扩到(0,7)大概率会得到一个在训练集上R²高得吓人、测试集上一塌糊涂的模型。这时候你会真正理解什么叫过拟合。3.4 季节项怎么选先看分解图再看数据量新能源车销量有明显的季节性但季节性不稳定——年初是低点、年末冲刺、中间还有价格战干扰。对这种季节性直接上SARIMA是个选项但SARIMA需要的样本量更大而且参数搜索空间从(p,d,q)变成(p,d,q)(P,D,Q,s)翻车概率翻倍。我的做法是先把季节性强弱量化一次。对月度数据做12阶差分后再跑一次平稳性检验如果12阶差分后序列的方差下降明显说明季节性稳定存在可以上SARIMA如果差分后反而更乱说明季节性只是每年不同走势的巧合不是稳定的周期波动。简单说就是季节性分解图里那个seasonal子图线条如果每年形状一致上SARIMA如果线条每年高低错落继续用普通ARIMA加外生变量。4. 用 statsmodels 跑通最小模型训练、预测与区间解读4.1 数据切分别拿最后一段当测试集拿最后一年时间序列的交叉验证和时间切片是有讲究的。随机打乱数据是分类任务的玩法时序预测必须按时间前后切。常见做法是拿最后12个月做测试集前面的做训练集。但这里有个细节预测的位置离训练集越远误差越大。如果你只预测下一个月测试集就用最后1个月预测半年那就用最后6个月做测试。留出12个月当测试当然更有说服力但代价是你训练集会少一年数据对ARIMA这种本来就吃不饱的模型太奢侈。行业里还有一个折中方案滚动预测后验。每次都只预测下一个月然后把真实值并入下一个训练窗口。这个验证方式对“下个月备多少货”的业务场景最贴合。后面第6章我会展开写具体的滚动代码。4.2 最小可跑模型ARIMA(1,1,1)起步不管定阶搜索给什么结果我个人的习惯是先用ARIMA(1,1,1)建立一个基线再看误差。这个模型是Box-Jenkins方法论里的“万金油”——一个自回归项、一阶差分、一个移动平均项对很多经济金融序列都有七成以上的解释力。如果它的误差就已经接近业务能接受的范围就别折腾高阶模型了。from statsmodels.tsa.arima.model import ARIMA train df[sales][:-6] # 测试集留最后6个月 test df[sales][-6:] model ARIMA(train, order(1, 1, 1)) result model.fit() pred result.forecast(steps6)order(1, 1, 1)三个数字分别对应p、d、q。p1表示用过去1个月的销量来回归当前值d1表示做了一阶差分消除趋势q1表示当前值是过去一步预测误差的加权修正。这个模型的哲学是“昨天的误差里藏着今天的方向”预测逻辑比较容易被业务方理解。这句话在向管理层解释模型时很管用你不需要给他们讲卡尔曼滤波只需要让他们知道模型会记住上次猜错了多少并据此修正下一次。4.3 预测区间为什么ARIMA的置信区间越往后越宽ARIMA给出的是分布预测不是点预测。点值只是期望置信区间才是业务上下限。你会看到区间宽度随时间步数增加越来越大——这不是bug是模型在诚实地告诉你走得越远不确定性越大。pred_result result.get_forecast(steps6) pred_mean pred_result.predicted_mean pred_conf pred_result.conf_int(alpha0.2) # 80%置信区间alpha0.2alpha取0.2意味着置信区间是80%对应业务里说的“八成的把握”。给管理层汇报的时候建议输出三个数字下限做保守计划、均值做基准计划、上限做激进目标。如果你把alpha改成0.05区间会宽到好像什么也没说反而不利于决策alpha取0.2到0.3之间的区间在业务上最有指导意义。做产能规划的管理层要的不是精确值而是一个“最坏情况也扛得住”的下限。4.4 模型诊断残差必须是白噪声拟合完模型不是验收残差诊断过了关才算。检查三件事残差序列有没有明显的自相关性用Ljung-Box检验残差的分布是不是近似正态看直方图残差和原始序列之间有没有没被提取的模式看图。from statsmodels.stats.diagnostic import acorr_ljungbox residuals result.resid lb_test acorr_ljungbox(residuals, lags[12], return_dfTrue) print(lb_test)Ljung-Box检验的p值如果大于0.05说明残差里没有明显的自相关所有可提取的信息已经被模型吃干净了。如果p值小于0.05说明还有信息没榨干最常见的三个原因定阶偏低、季节项没纳入、断点事件没处理。这时候回头看数据预处理而不是急着换高阶模型。5. 避坑排查过差分、伪季节性、负销量与短序列四大翻车现场5.1 过差分销量跌穿零点的“幻觉”现象预测结果里出现负数销量或者后几个月预测曲线直线向下跌破历史最低点。原因二阶差分把序列中的有效信息当噪声差掉了模型被迫用纯随机波动去外推。我见过有人对销量序列做二阶差分后ADF的p值变成0.000高兴地以为“越差分越平稳”结果预测到第三个月销量变成负三万台。解决差分次数d的最高上限设为1。如果你做一阶差分后ADF的p值还是大于0.05不要加二阶差分换个思路序列可能是趋势突变而非单位根试试分段处理或加一个表示政策断点的外生变量。一句话销量数据几乎从来不需要二阶差分如果你觉得“需要二阶差分”才能稳那大概率是断点没处理干净。5.2 伪季节性把“去年降价潮”当成了周期规律现象季节性分解看到明显的季节因子但用SARIMA建模后测试集上误差比ARIMA还大。原因历史某一年因为补贴退坡或某明星车型上市当月销量暴增这个“一次性事件”在分解图里被识别成了“周期性规律”模型每年那个月都会自动预测一个不存在的暴涨。解决对季节性分解观察期拉长至少看36个月再下结论。如果只有24个月建议手工对比两年同期的差值若差值不稳定超过30%则用普通ARIMA加外生变量处理而不是上SARIMA的固定季节项。另外季节性参数s是固定的12不要因为你有一年特殊就改成其他模数值。5.3 预测区间越过0轴负数销量怎么跟管理层交代现象80%置信区间的下界变成负数。这个是ARIMA在数学上没有任何限制导致的销量是严格非负的但模型不知道这件事。原因模型在数学上允许负值它没有“销量最低为0”的先验常识。解决两招并用的常见做法。第一招在进入模型前对原始序列做对数变换预测完再取指数回算。对数变换能压缩高波动、保证预测值在回算时为正——注意这里要用正态分布的回算修正项但实际业务上可以忽略。第二招更直接如果置信区间下界只是偶尔轻微的负值且在预测精度范围内内直接裁断为0即可并在报告里标注“下界被截断”。裁断比对数变换更少扭曲原序列。5.4 短序列的“饿死”问题样本不够也硬跑高阶的代价现象用30个历史点跑出ARIMA(6,1,5)AIC低得漂亮测试集上预测值一路狂奔完全抓不住真实销量的波动范围。原因样本量太少高阶参数的自由度太大模型把训练集上的每个尖峰都记住了本质上是在背题库。解决强制限制p和q搜索不超过2或者用AIC但把惩罚调成更严格的BIC。更稳妥的做法是缩小测试集——从月度粒度换到季度粒度或者改用简单的回归模型加上时间趋势和季节哑变量。ARIMA吃的是“样本量×序列质量”的综合成本你给它喂多少它就还你多少。5.5 一个兜底排查流程预测离谱时该先查哪里预测结果离谱时按以下顺序排查每步五秒钟就能出结论第一步看训练集最后一年的形状和测试集是否像“同一个世界”如果不像断点没处理第二步看残差Ljung-Box p值如果小于0.05信息没吃干净第三步看预测区间的宽度如果第三个月的区间跨度已经超过历史均值样本量不够或模型阶数过大第四步回到原始数据找有没有口径切换的台阶。这四个步骤解决我遇到的八成翻车场景。另外那个经常被忽视的细节是forecast(steps6)得到的是“从训练集最后一个点开始往后推6个月”不是“从未来第一个真实点开始”。如果训练测试切分没做好你可能一直在用包含测试集信息的模型在“预测测试集”结果自然好得离谱。一旦发现预测结果好到你都不敢信那一定是切分出了bug。6. 让预测更抗造滚动预测与政策断点适配滚动预测是让ARIMA这类短记忆模型在业务上站住脚的最佳实践。步骤很简单预测下一个月→拿到真实值→把这个值拼进历史窗口→重新拟合→再预测下一个月。它对模型的调用次数变多了但每次只做一步预测误差被控制在一个月内不会滚雪球。在汽车行业做月度库存准备时这种“一步一更新”的方式比一次性预测六个月更实用——你每个月都会拿到新的上险数据没必要为六个月的预测一次性承担所有不确定性。代码里滚动预测的骨架是这样一个循环history list(train) predictions [] for t in range(len(test)): model ARIMA(history, order(1, 1, 1)) model_fit model.fit() yhat model_fit.forecast(steps1)[0] predictions.append(yhat) history.append(test.iloc[t])这个循环内的history.append是关键动作——预测完后马上把真实值加进历史窗口模型每步都在用“最新鲜”的数据重估参数。滚动窗口的代价是训练时间但对月度数据来说可以忽略不计。与滚动预测配套的另一个进阶是引入外生变量。新能源汽车销量不是封闭系统补贴政策、购置税减免直接改变购买成本把这些事件做成0/1哑变量传给ARIMA模型模型就能在参数估计时把政策月的基数压低或抬高。在statsmodels里用SARIMAX类实现并加exog参数设置外生变量。from statsmodels.tsa.statespace.sarimax import SARIMAX exog_train df[policy_break][:-6] # 政策断点哑变量 exog_test df[policy_break][-6:] model SARIMAX(train, order(1, 1, 1), exogexog_train) result model.fit() pred result.forecast(steps6, exogexog_test)做这个操作时脑子里时刻要有一个数你的预测周期内有没有已知的政策变动如果有这个哑变量必须在测试集上也给出值你是提前知道政策时间表的。如果未来半年没有政策变化哑变量全为0。这也意味着ARIMA的预测能力依赖于“未来状况已知但这个影响可以用简单开关表达”的场景。政策如果中途突然加码谁都救不了模型那时只能靠业务主观修正。这套方案能不能长期用取决于一个基本认知ARIMA的预测质量会随时间推移衰减所以必须把“每月重拟合”固化到工作流里把它当成一个持续运转的仪表盘而不是写一次就一劳永逸的黑匣子。我个人的习惯是把预测脚本挂在月底数据发布的那天定时跑一次然后拿预测区间跟业务方的实际预期做交叉验证。ARIMA不会给你惊喜但它会诚实地告诉你上限和下限这在充满惊喜惊吓的新能源行业里反而是最稀缺的品质。希望这套做法帮你在下一轮需求预测里少走点弯路。本文还有配套的精品资源点击获取