简介:这份资源面向具备一定机器学习与Python基础的数据分析学习者,围绕旅游与经济指标之间的关联展开完整实战。内容以全球旅游经济数据为对象,覆盖探索性数据分析、特征标准化、时间序列建模与多模型回归预测等环节,帮助读者理解如何从真实数据中挖掘旅游对经济的影响规律。压缩包共9个文件,包含7个py源代码、1个csv数据集与1个txt说明文件,整体约283KB,其中数据集约665.76KB,代码约44.34KB,结构清晰便于按模块学习。代码手工整理、无语法错误且可直接运行,涉及pandas、seaborn、statsmodels、sklearn、xgboost、geopandas等常用库,涵盖ARIMA、指数平滑、随机森林、梯度提升、SVR、KMeans聚类及t检验等方法。已有40人学习下载,适合希望系统掌握旅游经济数据分析与预测流程的读者参考复用。
1. 旅游和经济指标关系数据集分析预测:从一张 Excel 到可复现的预测链路
手里拿到一份「旅游和经济指标关系数据集分析预测」的压缩包,里面躺着 7 个源代码文件和 665.76 KB 的数据集,第一反应往往不是兴奋,而是犯嘀咕:这点数据量能跑出什么名堂?我一开始也这么想,直到把旅游人次、GDP、居民可支配收入、CPI、汇率这几列摊开,才发现它真正值钱的地方不在体量,而在结构——它把宏观经济的几个关键指标和旅游这个具体行业绑在了一起,天然适合做「指标驱动预测」的实战演练。这类数据集分析预测的活儿,核心不是把模型调到多高精度,而是搞清楚经济指标怎么影响旅游需求、哪些指标是领先信号、哪些只是噪声。适合已经会写 Python、跑过 sklearn 或 statsmodels,但没系统做过「经济指标 + 行业预测」这条链路的人。下面我按自己复现的顺序,把选型、代码、参数和踩过的坑讲清楚。
2. 先搞懂数据结构和预测目标:别急着上模型
2.1 665.76 KB 里到底装了什么
拿到数据集先别写模型,用 pandas 把结构摸清楚。旅游和经济指标关系这类数据,常见形态是「时间 + 地区 + 指标列」的宽表,665.76 KB 大概对应几千行、十几列的量级。我一般先跑一段探查代码,把列类型、缺失、时间跨度一次性看清楚。
import pandas as pd # 读取数据集,先不指定 dtype,让 pandas 自己推断 df = pd.read_csv("tourism_economy.csv") # 基础结构:行数、列数、每列类型 print(df.shape) print(df.dtypes) # 时间列单独看,确认粒度和跨度 df["date"] = pd.to_datetime(df["date"]) print(df["date"].min(), df["date"].max()) print(df["date"].dt.to_period("M").nunique()) # 月度粒度有多少期 # 缺失和描述统计 print(df.isnull().sum()) print(df.describe().T[["mean", "std", "min", "max"]])这段代码的逻辑是先确认「有多少行、什么类型、时间跨度多长」,再决定后面能不能做时间序列。参数上,to_datetime一定要显式调用,否则日期列会被当成字符串,后面做滞后特征会直接翻车。describe().T转置是为了让指标名在行上,方便一眼扫完。如果发现时间跨度不到 3 年、月度数据不足 36 期,那季节性分解和 ARIMA 这类方法就要慎用,样本太短,参数估计不稳。
2.2 预测目标怎么定:旅游人次还是旅游收入
这一步决定了后面所有特征工程的方向。旅游和经济指标关系数据集里,通常同时有「旅游人次」和「旅游收入」两列。我的经验是:如果数据里有人均消费或客单价,优先预测人次,因为人次对经济指标的响应更直接、更少受价格波动干扰;如果只有收入,那就预测收入,但要把 CPI 作为解释变量放进去,否则通胀会把趋势带偏。
选目标时还要看它和候选经济指标的相关系数。我一般先算一个相关矩阵,把和目标相关性低于 0.2 的指标先剔掉,避免后面特征太多、模型过拟合。
import seaborn as sns import matplotlib.pyplot as plt # 只保留数值列算相关 num_cols = df.select_dtypes(include="number").columns corr = df[num_cols].corr() # 看和目标列的相关性排序 target = "tourism_arrivals" print(corr[target].sort_values(ascending=False)) # 热力图辅助判断共线性 plt.figure(figsize=(10, 8)) sns.heatmap(corr, annot=False, cmap="coolwarm") plt.show()逻辑说明:先筛数值列,避免把地区名字符串带进相关计算报错。sort_values是为了快速定位哪些经济指标和目标最相关。参数上,热力图annot=False是因为列多时数字会糊成一片,看颜色块就够了。如果发现 GDP 和居民收入相关性超过 0.9,说明存在严重共线性,线性回归里只能留一个,或者改用岭回归。
2.3 为什么先做平稳性检验再谈模型
经济指标和旅游数据大多是时间序列,直接丢进回归会得到「伪回归」——R² 很高但残差有趋势,预测毫无意义。我一般先做 ADF 检验,确认序列平稳性,不平稳就差分。
from statsmodels.tsa.stattools import adfuller def adf_test(series, name): result = adfuller(series.dropna(), autolag="AIC") print(f"{name}: ADF={result[0]:.3f}, p={result[1]:.3f}") return result[1] # 对目标列和每个经济指标做检验 for col in ["tourism_arrivals", "gdp", "income", "cpi"]: p = adf_test(df[col], col) if p > 0.05: print(f" -> {col} 不平稳,需要差分")逻辑上,autolag="AIC"让 statsmodels 自动选滞后阶数,比手动指定稳。p 值大于 0.05 就认为不平稳,需要做一阶差分再检验。这一步不做,后面 ARIMA 的 d 参数就是瞎猜,预测结果会系统性偏移。常见做法是差分后再跑一次 ADF,直到 p 小于 0.05 为止。
3. 特征工程:把经济指标变成模型能吃的信号
3.1 滞后特征:经济指标影响旅游往往有延迟
经济指标对旅游的影响很少是当期的。居民收入涨了,可能过两三个月才体现在旅游人次上;CPI 上升,旅游需求可能滞后一个季度才下滑。所以要把经济指标做滞后处理,构造 lag 特征。
# 对经济指标构造 1-3 期滞后 lag_cols = ["gdp", "income", "cpi"] for col in lag_cols: for lag in [1, 2, 3]: df[f"{col}_lag{lag}"] = df[col].shift(lag) # 目标列也做滞后,用于自回归特征 df["arrivals_lag1"] = df["tourism_arrivals"].shift(1) df["arrivals_lag2"] = df["tourism_arrivals"].shift(2) # 差分后去掉前几行 NaN df_model = df.dropna().reset_index(drop=True) print(df_model.shape)逻辑说明:shift(lag)把序列往下移,lag1 就是上一期的值。参数上,滞后阶数不是越多越好,一般先试 1 到 3 期,超过 3 期在经济意义上解释力就弱了,而且会吃掉样本量。dropna之后要检查还剩多少行,如果原本只有 40 期,去掉 3 期滞后加 2 期自回归,只剩 35 期,样本就偏紧了。
3.2 滚动统计特征:捕捉趋势和波动
除了滞后,滚动均值和滚动标准差能帮模型捕捉趋势方向和波动大小。旅游数据往往有季节性,滚动窗口用 3 或 6 期比较合适。
# 滚动均值和标准差,窗口 3 期 df["arrivals_roll3_mean"] = df["tourism_arrivals"].rolling(3).mean() df["arrivals_roll3_std"] = df["tourism_arrivals"].rolling(3).std() # 经济指标的滚动均值,平滑短期噪声 df["gdp_roll3"] = df["gdp"].rolling(3).mean() df["income_roll3"] = df["income"].rolling(3).mean() df_model = df.dropna().reset_index(drop=True)逻辑上,滚动均值相当于低通滤波,把短期波动压掉,留下趋势。参数上窗口选 3 是因为月度数据里一个季度是 3 期,和很多经济指标的发布节奏对得上。窗口太大(比如 12)会让特征过于平滑,失去对近期变化的敏感度。滚动标准差则反映波动性,旅游旺季和淡季的波动差异会被它捕捉到。
3.3 季节性处理:月度数据绕不开的坑
旅游数据几乎必然有季节性,暑期和节假日是高峰。处理方式有两种:一是把月份做成哑变量,二是做季节性差分。我一般先看季节图,再决定用哪种。
# 月份哑变量 df["month"] = df["date"].dt.month month_dummies = pd.get_dummies(df["month"], prefix="month", drop_first=True) df = pd.concat([df, month_dummies], axis=1) # 季节性差分(12 期) df["arrivals_seasonal_diff"] = df["tourism_arrivals"].diff(12)逻辑说明:get_dummies把月份变成 0/1 列,drop_first=True避免虚拟变量陷阱。季节性差分diff(12)是减去去年同期的值,直接消除年度季节模式。参数上,如果数据不足 24 期,季节性差分会产生大量 NaN,这时候只能用月份哑变量。两种方式不要同时用,否则会过度差分,把真实信号也差掉。
4. 模型选型与训练:从线性基线到树模型
4.1 线性回归基线:先跑通再优化
不管最后用什么模型,我都会先跑一个线性回归当基线。它的好处是可解释性强,能直接看系数判断哪个经济指标影响大。
from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error, r2_score # 特征列:滞后、滚动、月份哑变量 feature_cols = [c for c in df_model.columns if c not in ["date", "tourism_arrivals", "tourism_revenue"]] X = df_model[feature_cols] y = df_model["tourism_arrivals"] # 时间序列不能随机打乱,按时间顺序切 split = int(len(X) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:] lr = LinearRegression() lr.fit(X_train, y_train) pred = lr.predict(X_test) print("MAE:", mean_absolute_error(y_test, pred)) print("R2:", r2_score(y_test, pred)) # 看系数 coef = pd.Series(lr.coef_, index=feature_cols).sort_values() print(coef)逻辑说明:时间序列切分必须按顺序,不能train_test_split随机打乱,否则用未来数据预测过去,指标会虚高。参数上,split取 80% 是常见做法,但如果样本少于 50 期,建议用时间序列交叉验证而不是单次切分。系数排序能直接看出哪个滞后经济指标贡献最大,如果 GDP 滞后 2 期的系数绝对值最大,说明经济对旅游的影响大约滞后两个月。
4.2 树模型:处理非线性和交互效应
经济指标和旅游的关系往往不是线性的,收入到一定水平后边际影响递减。这时候随机森林或梯度提升树更合适。
from sklearn.ensemble import RandomForestRegressor rf = RandomForestRegressor( n_estimators=300, max_depth=6, min_samples_leaf=3, random_state=42 ) rf.fit(X_train, y_train) pred_rf = rf.predict(X_test) print("RF MAE:", mean_absolute_error(y_test, pred_rf)) print("RF R2:", r2_score(y_test, pred_rf)) # 特征重要性 imp = pd.Series(rf.feature_importances_, index=feature_cols).sort_values(ascending=False) print(imp.head(10))逻辑说明:n_estimators=300是精度和速度的平衡点,再多提升有限。max_depth=6控制树深,防止过拟合,样本少的时候尤其重要。min_samples_leaf=3保证叶子节点至少有 3 个样本,避免模型记住噪声。特征重要性排序能验证前面的相关性分析——如果滞后 GDP 和滚动均值排在前列,说明特征工程方向对了。
4.3 时间序列交叉验证:别用单次切分下结论
单次切分的结果波动很大,换个切分点指标就变了。我一般用TimeSeriesSplit做多折验证,看指标的稳定性。
from sklearn.model_selection import TimeSeriesSplit import numpy as np tscv = TimeSeriesSplit(n_splits=5) mae_scores = [] for train_idx, test_idx in tscv.split(X): X_tr, X_te = X.iloc[train_idx], X.iloc[test_idx] y_tr, y_te = y.iloc[train_idx], y.iloc[test_idx] rf.fit(X_tr, y_tr) p = rf.predict(X_te) mae_scores.append(mean_absolute_error(y_te, p)) print("CV MAE:", np.mean(mae_scores), "+/-", np.std(mae_scores))逻辑说明:TimeSeriesSplit保证每折的训练集都在测试集之前,符合时间序列的因果顺序。参数上n_splits=5是常用值,样本少可以降到 3。看均值和标准差,如果标准差很大,说明模型在不同时间段表现不稳定,可能是某个经济指标在某段时期发生了结构变化,需要单独排查。
5. 避坑与排查:7 个源代码文件背后容易翻车的地方
5.1 现象:模型 R² 很高但预测图明显偏移
原因:时间序列做了随机切分,测试集里混入了未来信息,指标虚高。解决:改用按时间顺序切分或TimeSeriesSplit,重新评估。这个坑我踩过不止一次,R² 0.95 看着漂亮,画图一看预测线整体滞后一期,就是数据泄漏。
5.2 现象:ADF 检验 p 值忽高忽低
原因:滞后阶数选择不当,或者序列里有异常值。解决:固定autolag="AIC",先做异常值检测(比如 3 倍标准差),剔除或平滑后再检验。经济指标偶尔有统计口径调整,会造成假的结构突变。
5.3 现象:树模型特征重要性里月份哑变量排第一
原因:旅游数据的季节性太强,月份哑变量把经济指标的信号盖住了。解决:先做季节性差分或去季节化,再跑模型;或者把月份哑变量单独建模,经济指标只解释去季节后的残差。我一般会对比去季节前后的特征重要性,确认经济指标是否还有增量贡献。
5.4 现象:滞后特征一加,样本量骤降
原因:每个滞后吃掉一行,多个滞后叠加后样本不够。解决:优先保留经济意义最强的 1 到 2 个滞后,或者用插值补缺失再构造滞后。样本少于 30 期时,建议放弃复杂模型,改用简单回归加滚动预测。
5.5 现象:预测值出现负的旅游人次
原因:线性模型外推时没有约束,经济指标出现极端值时预测越界。解决:对目标做对数变换再建模,预测后取指数还原;或者用clip(lower=0)做后处理。对数变换还能缓解异方差,一举两得。
6. 进阶技巧:用滚动预测验证真实场景下的表现
前面都是单次预测,真实场景里模型是滚动使用的——用历史数据训练,预测下一期,然后加入新数据再预测。这个验证方式更接近落地,也更容易暴露问题。
# 滚动预测:每次用前 N 期训练,预测下一期 def rolling_forecast(df, feature_cols, target, start_ratio=0.7): n = len(df) start = int(n * start_ratio) preds, actuals = [], [] for i in range(start, n): train = df.iloc[:i] test = df.iloc[i:i+1] model = RandomForestRegressor(n_estimators=200, max_depth=6, random_state=42) model.fit(train[feature_cols], train[target]) preds.append(model.predict(test[feature_cols])[0]) actuals.append(test[target].values[0]) return actuals, preds actuals, preds = rolling_forecast(df_model, feature_cols, "tourism_arrivals") mae = mean_absolute_error(actuals, preds) print("滚动预测 MAE:", mae)逻辑说明:每次只用当前时间点之前的数据训练,预测下一期,模拟真实部署时的信息约束。参数上start_ratio=0.7表示前 70% 数据用于初始训练,后面逐期滚动。这个方式计算量大,但能真实反映模型在「只能看到过去」条件下的表现。如果滚动 MAE 明显高于单次切分的 MAE,说明模型对近期变化不够敏感,需要检查滞后特征是否够新。
| 验证方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 单次时间切分 | 快,易实现 | 结果波动大 | 快速基线 |
| TimeSeriesSplit | 多折,较稳 | 仍有信息约束 | 模型对比 |
| 滚动预测 | 最接近真实 | 计算量大 | 上线前验证 |
最后说个我自己的习惯:每次跑完模型,我都会把预测值和实际值画在同一张图上,不看指标先看图。图对了,指标才有意义;图不对,R² 再高也是玄学。这个数据集不大,但正好够把「经济指标 → 特征工程 → 模型 → 滚动验证」这条链路完整走一遍,走通了再换更大的数据集,心里就有底了。希望帮到你。
本文还有配套的精品资源,点击获取