R语言中能否让auto.arima采用MAPE等样本外指标选择最优模型?
用样本外MAPE选择最优ARIMA模型(替代auto.arima默认信息准则)
当然可以实现!auto.arima()本身确实默认用AICc/AIC/BIC这些样本内信息准则选模型,但如果你想改用样本外MAPE这类更贴近真实预测效果的指标,需要手动实现一套参数搜索+样本外评估的流程。下面是具体的步骤和代码示例:
核心思路
- 拆分数据集:把一部分数据留作测试集(比如最后12个月的月度数据),剩下的作为训练集。
- 遍历ARIMA参数空间:覆盖可能的非季节性(p,d,q)和季节性(P,D,Q,s)参数组合。
- 对每个参数组合:拟合模型→预测测试集→计算MAPE。
- 选择MAPE最小的模型作为最优模型。
具体代码实现
1. 加载依赖包并准备数据
我们用经典的月度航空乘客数据AirPassengers作为示例,你可以替换成自己的月度数据集:
library(forecast) # 加载月度数据 data <- AirPassengers # 拆分训练集和测试集:留最后12个月作为测试集 train_size <- length(data) - 12 train_data <- window(data, end = c(1959, 12)) # 训练集到1959年12月 test_data <- window(data, start = c(1960, 1)) # 测试集为1960年全年
2. 定义MAPE计算函数
MAPE是平均绝对百分比误差,公式为:$\text{MAPE} = \frac{1}{n}\sum_{i=1}^n \left|\frac{\text{actual}_i - \text{predicted}_i}{\text{actual}_i}\right| \times 100$
calculate_mape <- function(actual, predicted) { # 避免实际值为0的情况(如果你的数据有0,可以加判断跳过或替换) if (any(actual == 0)) { warning("实际值包含0,MAPE计算可能失真,建议改用MAE或RMSE") } mean(abs((actual - predicted)/actual)) * 100 }
3. 遍历参数空间并评估
我们定义合理的参数范围(你可以根据自己的数据调整,比如扩大p/q的范围):
# 参数范围定义(月度数据季节性周期s=12) p_values <- 0:3 # 非季节性AR阶数 d_values <- 0:1 # 差分阶数 q_values <- 0:3 # 非季节性MA阶数 P_values <- 0:2 # 季节性AR阶数 D_values <- 0:1 # 季节性差分阶数 Q_values <- 0:2 # 季节性MA阶数 s <- 12 # 月度数据的季节性周期 # 存储每个参数组合的结果 results <- list() # 遍历所有参数组合 for (p in p_values) { for (d in d_values) { for (q in q_values) { for (P in P_values) { for (D in D_values) { for (Q in Q_values) { # 用tryCatch跳过无法拟合的无效参数组合 tryCatch({ # 拟合ARIMA模型 model <- Arima(train_data, order = c(p, d, q), seasonal = c(P, D, Q, s)) # 预测测试集 forecast_result <- forecast(model, h = length(test_data)) # 计算MAPE mape_score <- calculate_mape(test_data, forecast_result$mean) # 保存参数和MAPE param_key <- paste0("ARIMA(",p,",",d,",",q,")(",P,",",D,",",Q,")",s) results[[param_key]] <- list( order = c(p,d,q), seasonal = c(P,D,Q,s), mape = mape_score ) }, error = function(e) { # 跳过报错的参数组合(比如过度差分的情况) NULL }) } } } } } }
4. 选择最优模型
从所有结果中筛选出MAPE最小的模型:
# 提取所有MAPE值 mape_scores <- sapply(results, function(x) x$mape) # 找到MAPE最小的模型索引 best_model_key <- names(which.min(mape_scores)) best_model_info <- results[[best_model_key]] # 输出最优模型信息 cat("最优模型:", best_model_key, "\n") cat("测试集MAPE:", round(best_model_info$mape, 2), "%\n") # 用完整数据集拟合最优模型 final_model <- Arima(data, order = best_model_info$order, seasonal = best_model_info$seasonal) summary(final_model)
进阶优化:滚动窗口验证
如果想让评估更稳健,可以用滚动窗口的方式:每次用前k个月的数据训练,预测第k+1个月,然后滚动窗口(加入新的真实值,去掉最旧的),重复直到遍历完测试集,最后计算平均MAPE。这种方式能避免单次拆分的随机性,结果更可靠。
注意事项
- MAPE对接近0的实际值非常敏感,如果你的数据包含0或极小值,建议改用MAE(平均绝对误差)或RMSE(均方根误差)。
- 参数范围不要设置得过大,否则计算时间会很长(尤其是大数据集)。可以参考
auto.arima()的默认参数范围来调整。 - 样本外评估的测试集大小建议根据数据频率调整:月度数据一般留12-24个月,季度数据留4-8个季度。
内容的提问来源于stack exchange,提问作者Andrei Catana




