☰
基于ARIMA的新能源汽车销量预测全流程解析
2026/10/13 15:52:23 网站建设 项目流程

简介:这份PDF是一篇基于ARIMA模型开展新能源汽车销量预测研究的完整论文,适合汽车行业研究人员、市场分析人员以及学习时间序列预测的统计与数据科学学习者阅读。全文从全球环保与能源背景切入,说明了新能源汽车销量预测的必要性,并详细介绍了ARIMA模型的原理、差分平稳化处理、模型识别定阶、参数估计及拟合验证流程。作者选取2014年1月至2019年5月的月度销量数据,建立ARIMA(1,0,0)(1,1,0)模型进行短期预测,通过与实际销量对比验证了模型在趋势预测中的有效性,也指出了极端值下误差较大的局限。资源为1个PDF文件,压缩包大小约1.11MB,内容包含摘要、关键词、模型概述、实例分析、表格与图表结果等,结构完整、可读性强。该资料目前已有480人浏览学习,对于希望了解ARIMA模型在新能源汽车销量场景中实际应用方法的读者具有直接参考价值,也可为后续开展相关预测研究或企业销量规划提供方法借鉴。

1. 销量预测先别上深度学习:ARIMA在新能源序列上的适用边界

当一家车企只有三四十个月的新能源销量历史数据,却要预估未来半年的市场走势时,最先被想到的方案往往不是ARIMA,而是LSTM或者各种树模型集成。实际落地时你会很快发现,二三十个样本根本喂不饱神经网络,树模型也学不出时间依赖。这时候回头用ARIMA,反而能给出一个稳定、可解释、带置信区间的预测结果。本文围绕“基于ARIMA模型的新能源汽车销量预测”这条主线,讲清楚从数据清洗、平稳化、建模拟合、滚动回测到业务落地的完整流程,重点写那些真正让预测翻车的常见问题。适合正在做销量预测、需求计划或市场分析的从业者,也适合第一次接手时间序列预测、想少走弯路的新手。

2. 建模前先过数据关:新能源销量序列的清洗、平稳化与季节性拆分

2.1 销量数据里藏着三类“假异常”:季度末冲量、节假日与政策节点

新能源月度销量数据拿到手,第一件事不是画趋势图,而是先搞清楚口径。批发量是厂家出货口径,上险量是终端零售口径,交付量是运营口径,三者存在时间差。预测时口径必须统一,否则模型会把口径切换误判成市场起伏。我在实际项目里会先确认历史数据用的哪个口径,未来采集是否一致,这个环节出错,后面所有工作都白做。

口径确认之后,再看数据里最常见的三类“假异常”。第一类是季度末冲量。汽车行业的交付节奏里,季度末集中交付是常态,用批发量或交付量口径时,3月、6月、9月、12月往往出现明显尖峰。很多入门者拿到数据第一件事就是把这几个点标成异常,用3σ或者IQR抹掉。这是错的——它不是测量错误,是真实的业务节奏,ARIMA的低阶滞后项本来就能捕捉一部分规律。抹掉之后,模型反而会把冲量“还回去”,预测结果在季度末系统性偏低。

第二类是节假日效应。春节落在1月还是2月,直接决定这两个月销量对比的形态。月度数据上这不是随机噪声,是可解释的偏移。第三类是政策节点。补贴退坡前后的抢购、促销政策到期后的回落,销量会在几个月内出现断崖式变化,这种变化是结构性的,均值、趋势在断点前后完全不同。这类点应该在数据里显式标记,而不是交给模型自己猜。

所以数据预处理的判断准则是:能保留业务含义的波动,就不要为了“数据好看”而抹平。真正需要处理的只有三件事——缺失值、录入错误、口径不一致。缺失值优先按业务规则补:上险数据缺某个月,用相近月份的均值或上月值补,不要一上来就线性插值。录入错误靠来源校验,比如批发量出现负数、单月环比翻三倍这种,回原始台账核对。口径不一致就得彻底统一,宁可少用一部分历史,也别混合建模。

2.2 ADF检验、KPSS检验与差分阶数:p值到底怎么读

ARIMA建模前先解决平稳性。这里最容易读反的是ADF检验的原假设:它假设序列存在单位根,也就是非平稳。p值小于0.05时拒绝原假设,才认为序列平稳。很多人刚开始接触时把p值大于0.05当作“平稳”,方向整个反了。

实操时我一般会先读入数据,把索引转成月度频率,这样缺失月份会显式暴露出来,而不是被pandas静默跳过:

import pandas as pd import numpy as np from statsmodels.tsa.stattools import adfuller, kpss df = pd.read_csv("nev_sales_monthly.csv", parse_dates=["month"]) series = df.set_index("month")["volume"].asfreq("MS") # 缺失月份会变成NaN,先看缺失位置 missing_idx = series[series.isna()].index # 业务兜底:缺失月份用相邻均值补,不超过2个月 series = series.fillna(series.rolling(3, center=True, min_periods=1).mean()) # ADF检验原序列 adf_stat, adf_p, _, _, _, _ = adfuller(series, autolag="AIC") # 一阶差分后再检验 diff1 = series.diff(1).dropna() adf_stat_d1, adf_p_d1, _, _, _, _ = adfuller(diff1, autolag="AIC") print("原序列 ADF p值:", round(adf_p, 4)) print("一阶差分后 ADF p值:", round(adf_p_d1, 4)) # KPSS作为对照 kpss_stat, kpss_p, _, _ = kpss(diff1, regression="ct") print("一阶差分后 KPSS p值:", round(kpss_p, 4))

这段代码里有几个参数值得说明。asfreq("MS")是把时间索引强制对齐到每月月初,原始数据如果漏了某个月,这里会变成NaN,比pandas自动推断可靠得多。adfuller的autolag="AIC"让函数自动选择回归滞后阶数,不要手动指定maxlag,除非你有明确的业务理由。差分后检测序列用diff1 = series.diff(1).dropna(),注意差分会产生第一个NaN,必须dropna再检验。

判断逻辑:如果原序列p大于0.05,一阶差分后p小于0.05,d=1就是合理选择。KPSS在这里是辅助验证,它的原假设是序列趋势平稳,和ADF正好互补。实操中一个反直觉的提醒:不要迷恋“完美平稳”。有些序列一阶差分后ADF的p值已经到0.001,但看差分序列的方差反而比原序列更剧烈——说明差分只是把趋势强行转成了噪声,这种情况要停下来,回看数据里是不是有断点,而不是继续差分。

2.3 季节性能不能交给ARIMA:分解建模与业务叠加

新能源月度销量存在明显的年度季节性:年末冲量、年初回落、春节月前后错位。这里的矛盾在于,季节性是看出来了,但历史周期数不够。SARIMA的季节项要求至少三到四个完整周期才能稳定估计,大多数车型上市才两三年,月度数据二三十个点,做12阶季节差分又会丢掉12个样本,剩余样本量根本不够支撑季节项的估计。

我一般先做一次STL分解,量化季节强度再决定路线:

from statsmodels.tsa.seasonal import STL # 月度数据固定period=12;robust=True抵抗季度末冲量对趋势的污染 stl = STL(series, period=12, robust=True).fit() # 季节强度:接近1说明季节性主导 seas_strength = 1 - np.var(stl.resid) / np.var(stl.seasonal + stl.resid) print("季节强度:", round(seas_strength, 3))

period=12对月度数据是固定的,robust=True让分解过程对极端值不敏感,季度末冲量不会污染趋势分量。季节强度超过0.6时,我会考虑SARIMA或者加月份虚拟变量;低于0.3时直接用ARIMA,不用硬处理。无论走哪条路,预测结果出来之后,业务上年末冲量、年初回落的节奏仍然建议按已知规则叠加校准——这部分业务经验比模型参数更可靠。

3. 基于ARIMA的新能源汽车销量预测全流程:定阶、拟合与多步预测

3.1 看ACF/PACF定阶:截尾拖尾怎么读,为什么经常读不出来

ARIMA定阶的理论套路是看ACF和PACF图:PACF在k阶后突然截尾,p取k;ACF在k阶后截尾,q取k;拖尾就是指数衰减。这个读法在教科书序列上很干净,但放在新能源销量序列上,我几乎没有见过能直接读出来的时候。原因也简单:序列里趋势、季节、断点混在一起,ACF/PACF图基本都是缓慢衰减加周期性尖峰,截尾特征被淹没。

所以我的定位是:ACF/PACF只用来确定搜索范围,以及判断残差里有没有残留信息,最终参数交给信息准则扫描。定阶这一步不要花太多时间纠结“p到底取2还是3”,把p、q各放到0到4的范围去做网格搜索,结果会自己说话。

3.2 网格搜索选最优参数:完整可跑的Python流程

网格搜索的核心思路是穷举(p, d, q)组合,按信息准则排序。d在上一章已经确定取1,这里只需要扫p和q:

import itertools import warnings import numpy as np from statsmodels.tsa.arima.model import ARIMA from statsmodels.stats.diagnostic import acorr_ljungbox warnings.filterwarnings("ignore") y = series # 原始序列,ARIMA内部会自动做d阶差分 best_bic = np.inf best_order = None best_model = None # p和q各取0到4:月度销量的业务记忆一般不超过半年 for p in range(0, 5): for q in range(0, 5): order = (p, 1, q) try: model = ARIMA(y, order=order) result = model.fit() if result.bic < best_bic: best_bic = result.bic best_order = order best_model = result except Exception: # 部分参数组合可能无法收敛,跳过比强行处理合理 continue print("最优参数:", best_order, "BIC:", round(best_bic, 2)) # 残差白噪声检验:p值大于0.05说明残差中无信息残留 resid = best_model.resid lb_pvalue = acorr_ljungbox(resid, lags=12, return_df=True)["lb_pvalue"].iloc[-1] print("Ljung-Box p值:", round(lb_pvalue, 4))

这里有几个参数选择的逻辑需要说明。p和q都从0扫到4,是因为新能源汽车月度销量数据的自相关记忆通常不会超过半年,滞后5个月以上既没有业务含义,也很难有统计可靠性,扫进去只会增加过拟合风险。result.bic作为选择标准,是因为样本量不大时AIC容易选中复杂模型,BIC的惩罚更重,选出来的模型更稳健。warnings.filterwarnings("ignore")是收敛警告的常规处理方式——警告不代表失败,模型收敛了就能用,但如果有报错,会被try except捕获并跳过。

Ljung-Box检验是选完参数后必须做的一步:p值大于0.05,说明残差是白噪声,AR部分已经把序列里的信息提取干净了;如果p值小于0.05,说明残差里还有相关性没被利用,要么调大p或q,要么回去检查是不是有断点没处理。这个检验比AIC本身更能说明模型是否合格。

3.3 用get_forecast做多步预测:预测区间怎么取

选好参数后做未来预测,我统一用get_forecast而不是predict。predict是样本内拟合值,回看历史趋势用;get_forecast才是真正对未来做多步外推:

# 未来6个月的预测 forecast_result = best_model.get_forecast(steps=6) pred_mean = forecast_result.predicted_mean pred_ci = forecast_result.conf_int(alpha=0.05) # 输出成表格方便和业务对齐 out = pd.DataFrame({ "forecast": pred_mean, "lower_95": pred_ci.iloc[:, 0], "upper_95": pred_ci.iloc[:, 1] }) print(out.round(0))

steps=6对应预测未来6个月,这是多步递归预测:第6个月的误差会继承前5个月的误差,置信区间会逐月放大,这是正常现象。conf_int(alpha=0.05)输出95%置信区间,但实际业务汇报里我更喜欢用80%区间,也就是alpha=0.2——月度销量的95%区间宽到没法做决策,80%区间反而更有参考意义。

还有一个容易被忽略的习惯:预测不是一次性工作。每个月实际销量出来后,要把新数据追加进序列,重新跑一次网格搜索和拟合,而不是“拟合好一个模型用一年”。月度重拟合的成本只有几秒钟,换来的是参数始终跟随市场状态更新,这一点在新能源这种快速变化的市场里尤其重要。

4. 别拿一次性划分当验证:滚动回测、误差口径与对比基准

4.1 滚动回测替代固定切分:实现与参数说明

时间序列模型最常见的验证翻车点,是把数据当成横截面数据一次性切分成训练集和测试集。比如前80%训练、后20%测试——如果测试集恰好落在市场结构变化后的阶段,误差会大得离谱;反过来如果在训练集里混入了未来的月份,误差又好看得骗人。正确做法是滚动回测:每次只用截止到某个时间点的数据训练,预测未来h个月,然后向前推进,反复验证。

def rolling_forecast(series, order, horizon=6, initial_window=24): rows = [] total = len(series) start = initial_window while start + horizon <= total: train = series.iloc[:start] test = series.iloc[start:start + horizon] model = ARIMA(train, order=order).fit() pred = model.forecast(horizon) rows.append({ "refit_point": train.index[-1], "actual": test.values, "pred": pred.values }) start += 1 return rows rows = rolling_forecast(series, best_order, horizon=6, initial_window=24) print("回测窗口数:", len(rows))

initial_window=24表示至少用24个月做首次训练,这是月度销量预测的下限,数据再少模型参数估计会非常不稳定。horizon=6是业务上常见的预测跨度,和前面的预测步数保持一致。每轮循环start += 1,相当于训练集终点逐月前移,每次重新拟合模型,代价是计算时间增加,但这是最接近业务节奏的验证方式。

回测结果不要只看总误差,要按时间画出实际值和预测值的对比,观察系统性偏差出现在哪个时间段。如果断点之后连续高估,那是结构变化问题;如果误差在年末冲量月份集中放大,那是季节性没处理干净。这两种情况的处理路径完全不同。

4.2 三个误差口径的使用场景:MAE、MAPE、RMSE

误差统计口径的选择直接影响模型评估结论,这里我一般会同时算三个:

def evaluate_forecast(actual, pred): mae = np.mean(np.abs(actual - pred)) rmse = np.sqrt(np.mean((actual - pred) ** 2)) mape = np.mean(np.abs((actual - pred) / actual)) * 100 return {"MAE": round(mae, 1), "RMSE": round(rmse, 1), "MAPE": round(mape, 2)} # 以第1步预测误差为例 step1_actual = np.array([r["actual"][0] for r in rows]) step1_pred = np.array([r["pred"][0] for r in rows]) print(evaluate_forecast(step1_actual, step1_pred))

MAE是绝对误差的平均值,单位是台,最直观,内部技术评估我用它。RMSE对大误差更敏感,一次巨大的预测偏差会被放大,适合库存场景——因为一次大幅偏差带来的仓储成本远大于多次小幅偏差。MAPE是百分比,管理层爱看,但低销量月份会失真:春节月销量可能只有正常月份的一半,同样的绝对误差在MAPE上会被放大成很大的百分比。我的习惯是:内部用MAE和RMSE做技术评估,对外汇报用MAPE但要注明低月份失真的问题。

4.3 先跑三个简单基准:ARIMA的增量价值要用对比证明

任何预测模型都必须回答一个问题:比naive好在哪。我接手预测项目的第一件事,永远是先把三个简单基准跑出来,而不是直接上ARIMA。

基准方法逻辑适用性
Naive下个月等于上个月平稳期表现尚可,趋势段系统性滞后
Seasonal Naive下个月等于去年同月需要至少12个月历史,样本少时不可用
MA-3最近3个月移动平均波动小、趋势平缓时性价比极高

对比必须在同一个滚动回测框架下进行,不能让ARIMA用滚动回测、基准用全样本拟合,那是作弊。经验阈值方面,我一般看滚动回测的MAPE:如果ARIMA比MA-3低3到5个百分点以上,说明模型确实学到了额外结构,值得上生产;如果差距在2个百分点以内,继续优化数据质量,而不是继续调参——这时候问题的瓶颈已经不在模型上了。

5. 新能源销量预测的常见问题排查:五个翻车点与处理方法

5.1 差分过头:数据被磨平,预测跟着躺平

现象:一阶差分后ADF的p值仍大于0.05,于是补做二阶差分,结果模型预测出来一条近似水平线,对后续几个月的趋势完全无响应。

原因:新能源销量序列里最常见的非平稳来源不是随机游走,而是趋势加上结构性断点。二阶差分把趋势本身也消掉了,序列只剩噪声,ARIMA自然学不出方向。

解决:差分阶数限制在0或1,超过1就回看数据。一阶差分后仍不平稳时,优先检查是否存在断点或季节性残留,而不是继续差分。一个辅助手段是观察一阶差分序列的标准差,如果比原序列波动还剧烈,说明差分已经把信号转成了噪声,这条路走不通。

5.2 断裂点被当噪声平均掉:预测持续高估或低估

现象:历史中存在某个月销量从高位直接腰斩,模型在随后几个月持续高估,误差系统性偏大。

原因:AR项会把断点前的均值惯性传导到未来,模型不认识“市场结构变了”这件事。

解决:在ARIMAX里加干预变量,断点之后取1、之前取0,让回归部分单独拟合结构变化。也可以用断点之后的数据重新拟合,和全样本模型的滚动回测误差做对比,数据量允许时后者往往更干净。但要注意不要凭肉眼随便切数据,断点位置必须有业务依据——政策节点、工厂停产、主力车型改款这类事件才行。

5.3 用全样本选参数:模型偷看了未来

现象:在完整序列上用AIC或BIC选出的最优参数,滚动回测里表现反而不如MA-3。

原因:参数是在包含未来月份的序列上选的,相当于模型“知道”了未来的自相关结构,回测自然露馅。

解决:参数选择必须在训练窗口内完成。第一次滚动回测时,只用最开始的24个月做网格搜索定参,后续窗口沿用这个参数。另一个更省事的做法是记录滚动回测中不同窗口选出的参数,如果(p,q)在多数窗口保持稳定,那用全样本选参的问题就不大;如果参数跳来跳去,说明序列结构不稳定,模型本身不适合。

5.4 只看点预测不问区间:预测数对不上就怀疑模型

现象:模型输出下月销量8.2万台,月底实际是9.6万台,业务觉得预测不准,开始质疑模型。

原因:月度销量的点预测本质上是条件期望,单次误差范围很大,把点预测当成唯一答案是预期管理出了问题。

解决:对外永远同时输出80%置信区间。点预测作为考核基线,区间的下限用于库存下限规划,上限用于产能预研。区间覆盖实际值的比例才是模型真实水平的体现——比如80%区间的历史覆盖率接近80%,这个模型就是合格的。

5.5 月度样本不足硬上ARIMA:预测区间宽到没意义

现象:只有18到24个月的数据,模型拟合得“很好看”,但预测区间宽到无法做任何决策。

原因:ARIMA需要估计p+q个参数,每个参数都有方差,样本太少时参数估计不可靠,区间自然爆炸。

解决:少于30个月的数据,我不建议单独用ARIMA出预测结论。替代方案有三个:用参数更少的模型,比如指数平滑或MA(1);改用周度数据,需要回数据源确认是否存在周度明细;或者直接接受现实,用基准模型加业务判断。预测的意义在决策,不在模型复杂度。

6. 让ARIMA真正落进业务:区间输出、外生变量与弃用边界

6.1 预测区间如何变成库存决策

业务落地时,80%预测区间的下限对应保守估计,采购和排产按下限走,能避免库存积压;区间上限对应乐观情形,用于产能预案,但不直接投入真金白银。点预测只是运营考核的基线,不是决策的唯一输入。我一般会在每月预测报告中把区间和历史覆盖率一起列出来,让业务对这个模型的波动有一个稳定的预期,而不是每个月都被实际数吓一跳。

6.2 给ARIMA加外生变量的加法

政策节点、春节月份这类已知事件,可以用外生变量进入模型,叫ARIMAX:

# 春节月份标记:1月或2月,业务确认具体年份的春节位置 df["spring_festival"] = df["month"].dt.month.isin([1, 2]).astype(int) model = ARIMA(df["volume"], exog=df["spring_festival"], order=(1, 1, 1)) result = model.fit()

外生变量进入的是回归部分,AR部分仍然处理序列自身的依赖。加变量要克制:每加一个变量就少一份统计自由度,节假日、政策节点最多加两三个。另一个关键约束是,预测未来月份时必须提供外生变量的未来值——春节排序是已知的,政策节点需要业务判断,不知道就别加。

6.3 什么时候该弃用ARIMA

ARIMA的边界要心里有数:预测期超过12个月时,趋势外推在新能源这种价格、技术、政策都在快速变化的市场里没有意义;数据粒度细化到周或天时,促销活动、竞品上市带来的高频波动超过了ARIMA低阶形式的表达能力;当销量进入平台期且外部因素主导时,应该先做供需拆分,再做预测,模型只是其中一环。

我的习惯是,每次拿到新月份的实际数,第一时间和上个月预测的区间对一下。偏出太多,就先查数据有没有断点,再查模型参数有没有漂移,而不是急着改模型。预测翻车不可怕,可怕的是不知道在哪翻的。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询