
简介这份毕业设计文档面向统计学、数据分析及旅游管理相关专业的本科生与研究者围绕旅游人数预测这一实际问题以青岛市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.testMATLAB 里用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/√nn 是样本量。如果样本只有 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 个月预测第 t1 个月然后窗口前移。# 划分训练集和测试集 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 图和业务判断直接定 d1 或 d2。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) 系数通常是正的因为今年客流好明年大概率也不差。如果系数是负的要么数据有问题要么模型设定错了。这个习惯帮我省了很多返工时间。希望帮到你。本文还有配套的精品资源点击获取