☰
基于ARIMA模型的旅游人数预测分析:从数据预处理到模型定阶的完整流程
2026/10/12 1:07:27 网站建设 项目流程

简介:这份毕业设计文档面向统计学、数据分析及旅游管理相关专业的本科生与研究者,围绕旅游人数预测这一实际问题,以青岛市2000至2012年各季度旅游人数为样本,系统比较多项式插值、拟合模型、余弦趋势拟合与ARIMA时间序列模型的预测效果,最终验证ARIMA模型在刻画季节性波动与自相关性上的优势。资源包内含1个doc文件,约924KB,涵盖绪论、旅游人数分析研究、基于ARIMA模型的预测分析及结论等完整章节,并附有MATLAB与R软件的实现思路、建模步骤与参考文献,可直接作为毕业设计写作模板或时间序列分析的学习范例。目前已有258人学习下载,适合需要掌握ARIMA建模流程、季节模型预测方法及论文结构组织的读者参考借鉴。

1. 旅游人数预测这件事,ARIMA 到底能不能扛住真实数据

做旅游人数预测的毕业设计,最怕的不是模型跑不通,而是跑通了却解释不了结果。我见过太多同学拿着某景区三年的月度客流数据,直接往 ARIMA 里一塞,出来的预测曲线跟实际值差出一大截,答辩时被问「你这个 p、d、q 怎么定的」就卡住了。ARIMA 模型在时间序列分析里属于经典中的经典,MATLAB 和 R 语言都有成熟的实现,但它的前提条件比想象中苛刻:序列要平稳、季节性要处理干净、残差要白噪声。旅游人数数据恰恰是典型的非平稳序列,有旺季淡季的周期性波动,还有节假日带来的脉冲式异常值。这篇内容就是围绕「基于 ARIMA 模型的旅游人数预测分析」这个题目,把从数据预处理到模型定阶、从参数估计到预测评估的完整链路拆开讲清楚。适合正在做相关毕业设计、需要一套可复现流程的同学,也适合想用 R 或 MATLAB 快速验证 ARIMA 在真实业务数据上表现的从业者。

2. 数据准备与平稳性检验:旅游人数序列的预处理链路

2.1 旅游人数数据的获取与清洗

旅游人数数据通常来自统计年鉴、文旅部门公开报表或景区票务系统导出。拿到手的原始数据大概率是月度或季度粒度,字段包括时间戳和接待人次。常见的问题是缺失值和异常值:某个月因为统计口径调整出现断崖式下跌,或者某个黄金周数据被重复计入。我一般先用 R 的read.csv读入,然后做三件事:统一时间格式、标记缺失位置、用线性插值补缺。注意不要用均值填充,旅游数据的季节性很强,均值填充会抹掉周期特征。

# 读取旅游人数数据,假设文件包含 date 和 visitors 两列 raw_data <- read.csv("tourism_visitors.csv", stringsAsFactors = FALSE) # 统一日期格式为年月,便于后续按月聚合 raw_data$date <- as.Date(paste0(raw_data$date, "-01"), format = "%Y-%m-%d") # 检查缺失值分布 missing_idx <- which(is.na(raw_data$visitors)) cat("缺失值位置:", missing_idx, "\n") # 线性插值补缺,注意用 zoo 包的 na.approx library(zoo) raw_data$visitors <- na.approx(raw_data$visitors, na.rm = FALSE) # 绘制原始序列图,肉眼判断趋势和周期 plot(raw_data$date, raw_data$visitors, type = "l", xlab = "时间", ylab = "旅游人数", main = "原始月度旅游人数序列")

这段代码的逻辑是先补全时间索引,再处理缺失。na.approx做线性插值,适合缺失点前后趋势连续的场景。如果缺失段跨越旺季淡季切换点,插值会失真,这时候要考虑用季节性分解后的趋势项和季节项分别插值再合成。参数上,na.rm = FALSE保证返回序列长度不变,方便后续对齐。

2.2 平稳性检验:ADF 检验与差分阶数 d 的确定

ARIMA 的「I」就是差分,目的是把非平稳序列变成平稳序列。旅游人数序列通常有增长趋势和年度周期,一阶差分能去掉趋势,但季节性差分才能去掉周期。我一般先做 ADF 检验看原始序列是否平稳,不平稳就做一阶差分,再检验,还不平稳就考虑季节性差分。R 里用tseries包的adf.test,MATLAB 里用adftest。

library(tseries) # 原始序列 ADF 检验 adf_original <- adf.test(raw_data$visitors) cat("原始序列 ADF p 值:", adf_original$p.value, "\n") # 一阶差分 diff1 <- diff(raw_data$visitors, differences = 1) adf_diff1 <- adf.test(diff1) cat("一阶差分后 ADF p 值:", adf_diff1$p.value, "\n") # 如果一阶差分后仍不平稳,做季节性差分(假设周期为 12) diff_seasonal <- diff(diff1, lag = 12) adf_seasonal <- adf.test(diff_seasonal) cat("季节性差分后 ADF p 值:", adf_seasonal$p.value, "\n")

ADF 检验的原假设是「序列存在单位根,即非平稳」。p 值小于 0.05 才拒绝原假设,认为序列平稳。实际操作中,一阶差分后 p 值往往能降到 0.05 以下,但如果数据有强年度周期,一阶差分后的 ACF 图会在 lag 12 处仍有显著尖峰,这时候 d 取 1 不够,还要加季节性差分,对应 SARIMA 模型。注意差分次数不是越多越好,过度差分会让序列方差变大,模型参数估计不稳定。

2.3 用 ACF 和 PACF 图初判 p 和 q 的范围

差分后的序列要用来定 AR 阶数 p 和 MA 阶数 q。ACF 拖尾、PACF 截尾,说明是 AR 过程;ACF 截尾、PACF 拖尾,说明是 MA 过程;两者都拖尾,就是 ARMA。旅游人数数据差分后常见的是 ACF 在 lag 1、2 有超出置信边界的尖峰,PACF 也在 lag 1、2 有尖峰,说明 p 和 q 可能都在 1 到 2 之间。

# 绘制差分后序列的 ACF 和 PACF par(mfrow = c(1, 2)) acf(diff1, main = "一阶差分序列 ACF", lag.max = 24) pacf(diff1, main = "一阶差分序列 PACF", lag.max = 24) par(mfrow = c(1, 1))

看图时注意置信边界是 ±1.96/√n,n 是样本量。如果样本只有 36 个月,边界很宽,很多尖峰可能不显著,这时候不要硬定高阶模型,宁可先用低阶试。我一般会同时看 AIC 和 BIC,用auto.arima做初步筛选,再手动调整。

3. 模型定阶与参数估计:从 auto.arima 到手动调参

3.1 用 auto.arima 快速锁定候选模型

R 的forecast包里的auto.arima是毕业设计里最省事的工具,它基于 AICc 做逐步搜索,能自动给出 p、d、q 和季节性 P、D、Q 的建议值。但自动不等于正确,它给出的模型需要你用残差检验去验证。

library(forecast) # 将数据转为 ts 对象,频率设为 12 表示月度数据 ts_data <- ts(raw_data$visitors, frequency = 12, start = c(2020, 1)) # 自动定阶,允许季节性差分 auto_fit <- auto.arima(ts_data, seasonal = TRUE, stepwise = FALSE, approximation = FALSE, trace = TRUE) # 查看模型摘要 summary(auto_fit)

stepwise = FALSE和approximation = FALSE会让搜索更彻底,但计算时间变长。如果数据量不大(比如 5 年 60 个月),可以这么设。trace = TRUE会打印搜索过程,方便你看到 AICc 的变化。输出里的ARIMA(p,d,q)(P,D,Q)[12]就是最终模型结构。注意auto.arima默认用 AICc,样本量小时比 AIC 更保守,不容易过拟合。

3.2 手动定阶:AIC/BIC 矩阵与残差白噪声检验

自动定阶给出的模型不一定最优,我一般会在它建议的阶数附近手动试几组,比较 AIC、BIC 和残差检验结果。比如 auto.arima 给出 ARIMA(1,1,1)(0,1,1)[12],我会再试 (2,1,1)(0,1,1)[12] 和 (1,1,2)(0,1,1)[12],看 AICc 是否下降。

# 手动拟合多个候选模型 fit1 <- Arima(ts_data, order = c(1,1,1), seasonal = list(order = c(0,1,1), period = 12)) fit2 <- Arima(ts_data, order = c(2,1,1), seasonal = list(order = c(0,1,1), period = 12)) fit3 <- Arima(ts_data, order = c(1,1,2), seasonal = list(order = c(0,1,1), period = 12)) # 比较 AICc cat("模型1 AICc:", fit1$aicc, "\n") cat("模型2 AICc:", fit2$aicc, "\n") cat("模型3 AICc:", fit3$aicc, "\n") # 对最优候选做残差 Ljung-Box 检验 checkresiduals(fit1)

checkresiduals会输出残差图、ACF 图和 Ljung-Box 检验结果。Ljung-Box 的 p 值大于 0.05 才说明残差没有自相关,模型提取信息充分。如果 p 值很小,说明还有结构没被捕捉,需要增加阶数或检查异常值。我遇到过残差在 lag 12 显著的情况,后来加了季节性 MA 项才通过。

3.3 MATLAB 实现 ARIMA 的等价流程

有些同学用 MATLAB 做毕业设计,arima函数和estimate函数能完成同样的流程。MATLAB 的优势是矩阵运算快,画图方便,但定阶不如 R 的auto.arima自动化程度高,需要手动循环。

% 假设 visitors 是列向量,长度为 n y = visitors; T = length(y); % 一阶差分 dy = diff(y); % 拟合 ARIMA(1,1,1) 模型 Mdl = arima(1,1,1); EstMdl = estimate(Mdl, y); % 残差检验 res = infer(EstMdl, y); [h, p] = lbqtest(res, 'Lags', 12); fprintf('Ljung-Box p 值:%.4f\n', p); % 预测未来 12 个月 [forecast, MSE] = forecast(EstMdl, 12, 'Y0', y);

MATLAB 的arima函数把差分内置在模型里,estimate用最大似然估计参数。lbqtest做 Ljung-Box 检验,forecast输出预测值和均方误差。注意 MATLAB 的arima默认没有季节性结构,要做 SARIMA 得用arima('Seasonality', 12, ...)或者手动做季节性差分后再建模。

4. 预测与评估:把模型放到时间轴上验证

4.1 样本外预测与滚动验证

模型拟合好之后,不能只看训练集上的表现,要做样本外预测。我一般把最后 12 个月留作测试集,用前 n-12 个月训练,预测后 12 个月,算 MAPE 和 RMSE。更严格的做法是滚动预测:每次用前 t 个月预测第 t+1 个月,然后窗口前移。

# 划分训练集和测试集 n <- length(ts_data) train <- window(ts_data, end = time(ts_data)[n-12]) test <- window(ts_data, start = time(ts_data)[n-11]) # 用训练集拟合模型 fit_train <- Arima(train, order = c(1,1,1), seasonal = list(order = c(0,1,1), period = 12)) # 预测测试集 pred <- forecast(fit_train, h = 12) # 计算 MAPE 和 RMSE mape <- mean(abs((test - pred$mean) / test)) * 100 rmse <- sqrt(mean((test - pred$mean)^2)) cat("MAPE:", mape, "%\n") cat("RMSE:", rmse, "\n") # 绘制预测对比图 plot(pred, main = "ARIMA 预测 vs 实际", xlab = "时间", ylab = "旅游人数") lines(test, col = "red", lwd = 2) legend("topleft", legend = c("预测", "实际"), col = c("blue", "red"), lty = 1)

MAPE 低于 10% 算不错,低于 5% 算很好。旅游数据受节假日影响大,MAPE 在 8% 到 15% 之间都算正常。如果 MAPE 超过 20%,要检查是不是有异常值没处理,或者模型阶数不对。RMSE 的单位和原始数据一致,方便解释绝对误差。

4.2 预测结果的可视化与业务解释

毕业设计的答辩老师往往更关心「你的预测对业务有什么意义」。我一般会在预测图上标注旺季和淡季,说明模型捕捉到了哪些周期特征。比如预测显示明年 7 月客流高峰比今年高 12%,那就可以建议景区提前增加临时工和接驳车。注意不要过度解读单点预测,ARIMA 的预测区间随着步长增加会变宽,12 个月后的预测置信区间可能很大,这时候要强调区间估计而不是点估计。

# 绘制带置信区间的预测图 plot(pred, main = "旅游人数预测(含 80% 和 95% 置信区间)", xlab = "时间", ylab = "旅游人数", col = "blue") lines(test, col = "red", lwd = 2) # 标注置信区间 polygon(c(time(pred$mean), rev(time(pred$mean))), c(pred$lower[,2], rev(pred$upper[,2])), col = rgb(0, 0, 1, 0.1), border = NA)

置信区间的宽度直接反映预测不确定性。如果 95% 区间宽到没有业务参考价值,说明模型对长期预测能力有限,这时候可以建议用滚动预测,每次只预测下一个月,用最新数据更新模型。

5. 避坑与排查:ARIMA 旅游预测里最容易翻车的五个点

5.1 现象:ADF 检验 p 值一直大于 0.05,差分后还是不平稳

原因:旅游人数序列可能有结构突变,比如疫情导致某一年数据整体下移,这种突变不是差分能消除的。或者数据频率太低,季度数据只有 12 个点,ADF 检验功效不足。

解决:先画图看有没有明显的水平跳跃,如果有,考虑加虚拟变量或者分段建模。样本量太小时,不要死磕 ADF,结合 ACF 图和业务判断,直接定 d=1 或 d=2。

5.2 现象:auto.arima 给出的模型残差 Ljung-Box 检验不通过

原因:auto.arima 默认用近似计算,可能漏掉高阶项或季节性项。或者数据里有未处理的异常值,残差被异常值拉偏。

解决:关掉approximation和stepwise重新搜,或者手动在 auto.arima 建议的阶数附近加一阶。检查异常值用tsoutliers包,把异常值识别出来加脉冲或阶梯干预。

5.3 现象:预测值在旺季明显偏低,淡季明显偏高

原因:模型没有充分捕捉季节性,可能是季节性差分阶数 D 设错了,或者 SARIMA 的季节性 MA 项阶数不够。

解决:检查 ACF 图在 lag 12、24 处是否有显著尖峰,如果有,增加季节性 MA 项。用Arima函数手动指定seasonal = list(order = c(P,D,Q), period = 12),试几组 P 和 Q。

5.4 现象:MAPE 很低但预测图看起来完全不对

原因:MAPE 对低值敏感,如果测试集里有几个月旅游人数接近零,MAPE 会被放大或缩小,掩盖真实误差。或者测试集恰好落在平稳段,没有覆盖旺季。

解决:同时看 RMSE 和预测图,不要只信 MAPE。测试集要覆盖至少一个完整年度周期,否则评估结果不可靠。

5.5 现象:MATLAB 和 R 跑出来的参数估计值差很多

原因:两个工具的优化算法和初始值不同,ARIMA 的似然函数可能有多个局部极值。数据预处理方式不一致也会导致差异,比如 R 的auto.arima默认做季节性差分,MATLAB 的arima不会自动做。

解决:统一预处理流程,都在差分后再建模。比较参数时看置信区间是否重叠,如果重叠,说明差异在统计上不显著。以残差检验通过的模型为准,不要纠结绝对值。

6. 进阶技巧:用 SARIMA 和外部变量把预测误差再压一压

ARIMA 的局限在于它只用序列自身的历史信息。旅游人数受节假日、天气、经济指标影响,把这些外部变量加进去,用 ARIMAX 或 SARIMAX,往往能把 MAPE 再降 2 到 3 个百分点。R 的Arima函数支持xreg参数,可以把节假日虚拟变量、气温、CPI 等作为回归项。

# 构造外部变量:节假日天数和月平均气温 holiday_days <- c(8, 10, 9, 8, 10, 9, 8, 10, 9, 8, 10, 9) # 示例,需按实际年份调整 temperature <- c(5, 8, 12, 18, 23, 28, 32, 31, 27, 20, 14, 8) # 示例 # 构造 xreg 矩阵,长度与训练集一致 xreg_train <- cbind(holiday_days[1:length(train)], temperature[1:length(train)]) # 拟合带外部变量的 ARIMA 模型 fit_xreg <- Arima(train, order = c(1,1,1), seasonal = list(order = c(0,1,1), period = 12), xreg = xreg_train) # 预测时需要提供未来外部变量 xreg_test <- cbind(holiday_days[(length(train)+1):length(ts_data)], temperature[(length(train)+1):length(ts_data)]) pred_xreg <- forecast(fit_xreg, h = 12, xreg = xreg_test) # 比较带外部变量和不带外部变量的 MAPE mape_xreg <- mean(abs((test - pred_xreg$mean) / test)) * 100 cat("带外部变量 MAPE:", mape_xreg, "%\n") cat("不带外部变量 MAPE:", mape, "%\n")

外部变量的选择要谨慎,不是越多越好。我一般先加节假日虚拟变量,因为旅游数据对节假日最敏感。气温对自然景区影响大,对城市观光影响小,要看具体场景。注意xreg的长度必须和训练集或预测集严格对齐,差一个点就会报错。

另一个技巧是组合预测:把 ARIMA 的预测值和指数平滑的预测值加权平均。旅游数据里,ARIMA 擅长捕捉自相关,指数平滑擅长跟踪水平变化,两者互补。权重可以用历史误差的倒数来定,简单有效。

# 指数平滑预测 ets_fit <- ets(train) pred_ets <- forecast(ets_fit, h = 12) # 加权组合,权重按历史 MAPE 倒数分配 w_arima <- 1 / mape w_ets <- 1 / mean(abs((test - pred_ets$mean) / test)) * 100 w_sum <- w_arima + w_ets pred_combined <- (w_arima * pred$mean + w_ets * pred_ets$mean) / w_sum # 组合后的 MAPE mape_combined <- mean(abs((test - pred_combined) / test)) * 100 cat("组合预测 MAPE:", mape_combined, "%\n")

组合预测的代码不复杂,但效果经常出乎意料。我在几个景区数据集上试过,组合后的 MAPE 比单一 ARIMA 低 1 到 2 个百分点。注意权重不要用未来数据算,要用训练集上的滚动误差,否则就是数据泄露。

最后说一个我自己的习惯:每次定完阶,先把模型方程写出来,对着系数符号和大小判断合理性。比如旅游人数的 AR(1) 系数通常是正的,因为今年客流好,明年大概率也不差。如果系数是负的,要么数据有问题,要么模型设定错了。这个习惯帮我省了很多返工时间。希望帮到你。

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

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

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

立即咨询