旅游与经济指标数据集分析:从EDA到建模的完整实战指南

发布时间:2026/9/24 22:14:17
旅游与经济指标数据集分析:从EDA到建模的完整实战指南 简介这份资源面向具备一定机器学习与Python基础的数据分析学习者围绕旅游与经济指标之间的关联展开完整实战。包内共9个文件以7个py源代码为主另含1个csv数据集与1个txt说明文件压缩包约283KB数据文件约665.76KB覆盖从数据读取、清洗到建模预测的全流程。代码手工整理、无语法错误可直接运行涉及pandas、seaborn、matplotlib等可视化与数据处理模块并调用scikit-learn、xgboost、statsmodels、geopandas等库涵盖线性回归、随机森林、梯度提升、SVR、决策树、KMeans聚类、ARIMA与指数平滑等模型还包含t检验等统计方法。读者可借此掌握跨国旅游与经济数据的探索性分析、特征标准化、模型对比与时间序列预测思路理解如何用多种算法评估旅游对经济的影响。目前已有40人学习下载适合作为课程设计、竞赛练习或自学项目的参考案例。1. 旅游和经济指标关系数据集分析一份能直接跑通的实战资源拿到一份写着「AI实战」的压缩包最怕的就是解压之后一堆脚本互相依赖、路径写死、跑起来报错。这次拆的这份旅游与经济指标关系数据集分析预测实例结构上算是比较克制的7 个 Python 脚本加 1 个 CSV 数据文件数据量 665.76 KB代码总量 44.34 KB没有乱七八糟的中间产物。它要解决的问题很具体——把全球旅游指标和宏观经济指标放在一张表里做探索性分析、相关性检验、聚类分群再上回归和时序模型做预测。适合两类人一类是想找一个完整 EDA 到建模闭环练手的机器学习初学者另一类是手里有类似「行业指标 经济指标」宽表、想参考别人怎么组织分析流程的从业者。数据文件world_tourism_economy_data.csv是整份资源的锚点所有脚本都围绕它展开这一点比那些数据散落在多个文件里的包友好得多。2. 数据表结构与七个脚本的分工先看清家底再动手2.1 宽表字段与建模可用性判断world_tourism_economy_data.csv是一张典型的面板宽表粒度是「国家-年份」。常见字段包括国家名称、年份、国际游客到访量、旅游收入、旅游支出、GDP、人均 GDP、通胀率、失业率等。这类表在建模前必须先做三件事确认国家名称的写法是否统一、确认年份范围是否连续、确认缺失值是按国家整段缺还是零星缺。国家名称不统一是这类数据最常见的坑。脚本里引入了pycountry作用就是把各种写法的国家名映射到标准 ISO 编码。如果你跳过这一步直接按国家名分组会出现「United States」和「United States of America」被当成两个国家的情况聚合结果直接翻车。import pandas as pd import pycountry df pd.read_csv(data/world_tourism_economy_data.csv) print(df.shape) print(df.columns.tolist()) print(df.isnull().sum().sort_values(ascendingFalse).head(10)) # 国家名标准化把常见别名映射到 ISO alpha_3 def normalize_country(name): try: return pycountry.countries.lookup(name).alpha_3 except LookupError: return None df[country_code] df[country].apply(normalize_country) print(df[country_code].isnull().sum()) # 看有多少没匹配上这段代码的逻辑是先摸清形状和缺失分布再做国家名标准化。lookup匹配不上会抛LookupError所以用 try 兜住返回 None最后统计 None 的数量。如果没匹配上的行数占比超过 5%就要手动补一张别名映射表而不是直接丢掉。2.2 七个脚本各自负责什么从文件名能大致还原出分析链路。1-Tourism and Economic Impact Analysis.py是总入口式的分析做基础统计和可视化3-TOURISM.py偏数据清洗和指标构造4-Tourism and Economic Impact EDA eg china.py是以中国为例的单国 EDA5-Analyzing Global Tourisms Economic Effects.py做全球层面的经济效应分析6-EDA Tourism.py是更完整的探索性分析7-Global Tourism Indicators and Economic Dynamics.py和8-Global Tourism Economic Analysis.py偏向建模和动态关系。这种编号方式说明作者是按分析深度递进的不是七个互不相干的 demo。实际使用时建议按编号顺序读但不必按顺序跑——因为每个脚本大概率都能独立加载 CSV 并输出自己的图表。先跑1和6建立整体印象再挑4看单国案例最后跑7、8看建模部分。2.3 环境依赖与最小可跑配置模块清单里既有sklearn、xgboost、statsmodels这类建模库也有geopandas这种地理可视化库。geopandas在 Windows 上安装经常出问题如果只是跑分析不想折腾地图可以先把涉及地理绘图的代码注释掉不影响核心建模流程。pip install pandas numpy matplotlib seaborn scikit-learn statsmodels xgboost pycountry scipy # geopandas 单独处理conda 环境下更稳 conda install -c conda-forge geopandaskagglehub出现在依赖里说明原始数据可能来自 Kaggle脚本里或许有在线拉取数据的逻辑。但压缩包已经带了完整 CSV建议把kagglehub相关调用改成直接读本地文件避免网络问题导致跑不通。这是离线复现这类资源时最省事的一步。3. 从 EDA 到建模相关性检验、聚类与回归怎么落地3.1 相关性分析与 t 检验的正确打开方式旅游指标和经济指标之间的关系最容易犯的错误是拿所有国家所有年份混在一起算一个皮尔逊相关系数。面板数据里国家之间的体量差异极大卢森堡的旅游收入和美国的旅游收入不在一个量级混算出来的相关性会被大国主导。更稳妥的做法是先按国家分组算相关系数再看分布或者对指标做标准化后再算。脚本里用到了scipy.stats.ttest_ind这是做两组均值差异检验的。常见用法是把国家按收入水平分成两组检验两组的旅游收入均值是否有显著差异。from scipy.stats import ttest_ind from sklearn.preprocessing import StandardScaler # 按人均GDP中位数把国家分成高低两组 median_gdp df[gdp_per_capita].median() high df[df[gdp_per_capita] median_gdp][tourism_receipts].dropna() low df[df[gdp_per_capita] median_gdp][tourism_receipts].dropna() t_stat, p_val ttest_ind(high, low, equal_varFalse) print(ft{t_stat:.3f}, p{p_val:.4f}) # 标准化后再看相关性避免量纲干扰 scaler StandardScaler() cols [tourism_receipts, gdp_per_capita, inflation, unemployment] df_scaled pd.DataFrame(scaler.fit_transform(df[cols].dropna()), columnscols) print(df_scaled.corr())ttest_ind里equal_varFalse是 Welch t 检验不假设两组方差相等在样本量不等时比默认的 Student t 检验更稳。标准化用StandardScaler做 z-score之后corr()出来的系数才不会被量纲带偏。这一步很多新手会跳过导致相关性矩阵看起来全是 0.9 以上其实是量纲造成的假象。3.2 KMeans 聚类做国家分群sklearn.cluster.KMeans在这份资源里的作用是把国家按旅游和经济特征分成几档。聚类前必须标准化否则 GDP 这种大数值字段会完全主导距离计算。聚类数 K 的选择脚本里可能写死了但实际用的时候建议跑一下肘部法。from sklearn.cluster import KMeans import matplotlib.pyplot as plt features [tourism_receipts, gdp_per_capita, inflation, unemployment] X StandardScaler().fit_transform(df[features].dropna()) inertia [] for k in range(2, 9): km KMeans(n_clustersk, random_state42, n_init10) km.fit(X) inertia.append(km.inertia_) plt.plot(range(2, 9), inertia, markero) plt.xlabel(K) plt.ylabel(Inertia) plt.show()n_init10是显式指定多次初始化取最优新版 sklearn 默认值变过写死更保险。random_state42保证每次跑结果一致做分析记录时这点很重要。肘部法看的是 inertia 下降速度明显变缓的那个点通常落在 3 到 5 之间。聚类结果本身没有对错关键是分出来的组能不能用业务语言解释——比如「高收入低旅游依赖」「中等收入高旅游增长」这样的标签才有意义。3.3 回归模型对比与误差指标资源里出现了LinearRegression、RandomForestRegressor、GradientBoostingRegressor、DecisionTreeRegressor、SVR和xgboost基本覆盖了表格数据回归的常用模型。用train_test_split切分后用mean_squared_error和mean_absolute_error评估。from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, mean_absolute_error X df[[gdp_per_capita, inflation, unemployment]].dropna() y df.loc[X.index, tourism_receipts] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) for name, model in [ (Linear, LinearRegression()), (RF, RandomForestRegressor(n_estimators200, random_state42)), ]: model.fit(X_train, y_train) pred model.predict(X_test) print(name, MSE:, mean_squared_error(y_test, pred), MAE:, mean_absolute_error(y_test, pred))n_estimators200是随机森林的树数量太少会欠拟合太多训练慢且收益递减200 是个常用起点。MSE 对大误差敏感MAE 更直观两个一起看能判断模型是被少数极端值带偏还是整体偏差大。线性模型在这里的作用是当基线如果随机森林比线性好不了多少说明特征和目标的非线性关系不强没必要上复杂模型。4. 时序预测与地理可视化ARIMA、指数平滑和 geopandas4.1 ARIMA 与 Holt-Winters 的适用边界statsmodels里的ARIMA和SimpleExpSmoothing是用来做时间序列预测的。面板数据做时序预测有个前提得先筛出单个国家的时间序列不能拿全球汇总数据直接套。ARIMA 的order(p,d,q)三个参数分别对应自回归阶数、差分次数、移动平均阶数d通常通过 ADF 检验确定。from statsmodels.tsa.arima.model import ARIMA from statsmodels.tsa.holtwinters import SimpleExpSmoothing # 取单个国家的年度旅游收入序列 ts df[df[country] China].sort_values(year).set_index(year)[tourism_receipts].dropna() # ARIMA(1,1,1) 作为起点 model ARIMA(ts, order(1, 1, 1)) fit model.fit() print(fit.summary()) forecast fit.forecast(steps3) print(forecast) # 指数平滑作为对照 es SimpleExpSmoothing(ts).fit() print(es.forecast(3))order(1,1,1)不是万能起点但作为第一次跑通是合理的。fit.summary()里的 AIC 可以用来比较不同 order 的优劣AIC 越小越好。SimpleExpSmoothing适合没有明显趋势和季节性的序列如果序列有趋势应该用Holt而不是SimpleExpSmoothing。这份资源里用的是 Simple 版本说明作者可能只做了短期平滑预测没处理趋势项这是使用时需要自己补的地方。4.2 geopandas 地理可视化的替代方案geopandas用来画国家层面的指标分布图效果直观但安装门槛高。如果装不上可以用plotly的 choropleth 或者直接用matplotlib画条形图替代分析结论不受影响。import geopandas as gpd import matplotlib.pyplot as plt world gpd.read_file(gpd.datasets.get_path(naturalearth_lowres)) merged world.merge(df.groupby(country_code)[tourism_receipts].mean().reset_index(), left_oniso_a3, right_oncountry_code, howleft) merged.plot(columntourism_receipts, legendTrue, cmapOrRd) plt.title(Average Tourism Receipts by Country) plt.show()naturalearth_lowres是 geopandas 自带的数据集不需要额外下载。merge时用iso_a3和前面标准化出来的country_code对齐这一步如果对不上地图上会有大片灰色。cmapOrRd是橙色渐变适合表示数值高低。如果gpd.datasets在新版本里被移除了改用geodatasets包获取底图。4.3 模型结果怎么验证不是自嗨跑完回归和时序最容易自我感觉良好但模型可能只是记住了训练集。验证要做两件事一是看残差是否随机分布二是做时间上的外推测试。回归模型可以用交叉验证时序模型必须按时间切分不能用随机切分。from sklearn.model_selection import TimeSeriesSplit from sklearn.ensemble import GradientBoostingRegressor tscv TimeSeriesSplit(n_splits5) model GradientBoostingRegressor(random_state42) scores [] for train_idx, test_idx in tscv.split(X): model.fit(X.iloc[train_idx], y.iloc[train_idx]) scores.append(model.score(X.iloc[test_idx], y.iloc[test_idx])) print(TimeSeriesSplit R2:, scores)TimeSeriesSplit保证训练集永远在测试集之前符合时间序列的因果性。n_splits5表示切 5 折。如果各折的 R2 波动很大说明模型对时间段敏感泛化能力存疑。这一步是区分「跑通了」和「可信」的关键很多资源包不会写但实际用的时候必须补。5. 避坑与排查这份资源跑不起来时先看这几条5.1 现象FileNotFoundError找不到 CSV原因脚本里的路径大概率写的是相对路径data/world_tourism_economy_data.csv但你的工作目录不是压缩包解压后的根目录。解决在脚本开头统一加os.chdir(os.path.dirname(os.path.abspath(__file__)))或者把 CSV 路径改成绝对路径。os模块已经在依赖里直接用。5.2 现象pycountry匹配大量返回 None原因数据里的国家名可能是「Korea, Rep.」这种世界银行风格写法pycountry的lookup认不出来。解决先打印出所有没匹配上的国家名手动建一个字典映射比如{Korea, Rep.: KOR, Russian Federation: RUS}在normalize_country里先查字典再走lookup。5.3 现象geopandas导入报 DLL 错误原因Windows 上geopandas依赖的fiona和gdal版本不匹配。解决用 conda 装而不是 pipconda install -c conda-forge geopandas会自动解决依赖。如果还是不行直接跳过地理可视化部分用matplotlib画国家排序条形图分析结论一样能出来。5.4 现象ARIMA 报「矩阵非正定」或收敛警告原因序列太短或者差分后接近常数导致参数估计不稳定。解决先检查序列长度少于 20 个点的年度序列不建议上 ARIMA。可以改用SimpleExpSmoothing或者把order降到(0,1,0)即简单差分。另外确认序列里没有全 0 或全相同值的段。5.5 现象聚类结果每次跑都不一样原因KMeans没设random_state初始化中心随机。解决加上random_state42和n_init10。如果加了还是不稳定说明数据本身簇边界模糊这时候应该回到特征选择而不是继续调聚类参数。6. 进阶用法把单国分析扩展成面板回归与预测对比单国 EDA 和全球汇总分析之间缺一层面板回归。面板数据可以用固定效应模型控制国家间的不可观测差异比混合回归更干净。statsmodels里没有直接的面板回归但可以用虚拟变量近似。import statsmodels.api as sm # 构造国家虚拟变量近似固定效应 dummies pd.get_dummies(df[country], prefixc, drop_firstTrue) X_panel pd.concat([df[[gdp_per_capita, inflation, unemployment]], dummies], axis1) X_panel sm.add_constant(X_panel) y_panel df[tourism_receipts] model sm.OLS(y_panel, X_panel, missingdrop).fit() print(model.summary())drop_firstTrue避免虚拟变量陷阱add_constant加截距项。missingdrop自动处理缺失值。看summary()里各经济指标的系数和 p 值比混合回归的系数更可信因为国家固定效应被吸收了。另一个进阶方向是把多个模型的预测结果做对比表用同一测试集评估。我一般会固定random_state把线性、随机森林、XGBoost、SVR 跑一遍记录 MSE 和 MAE然后看哪个模型在误差和可解释性之间平衡最好。XGBoost 通常误差最低但线性模型的系数能直接解释弹性做汇报时反而更有用。from xgboost import XGBRegressor from sklearn.svm import SVR models { XGB: XGBRegressor(n_estimators300, learning_rate0.05, random_state42), SVR: SVR(kernelrbf), } for name, m in models.items(): m.fit(X_train, y_train) pred m.predict(X_test) print(name, MSE:, mean_squared_error(y_test, pred))learning_rate0.05配合n_estimators300是 XGBoost 常用的慢学习率多树组合比默认的 0.3 更不容易过拟合。SVR 的kernelrbf适合非线性关系但对特征缩放敏感前面必须标准化。从那以后我每次拿到这类「指标 经济」宽表都强制先跑一遍国家名标准化和缺失分布再决定用哪些字段建模。这份资源的七个脚本给了一条现成的分析链路但真正让结果可信的是补上它没写的验证步骤。希望帮到你。本文还有配套的精品资源点击获取