
简介这是一份基于Python机器学习ML实现的天气预测与可视化课程设计项目包含完整源码和文档说明适合正在完成Python期末大作业、课程设计或毕业设计的学生使用。代码注释详细新手也能快速理解各个模块的作用。整个压缩包共24个文件体积仅1.43MB内容包括4个Python源代码文件、4个CSV训练与测试数据集、1个已保存的机器学习模型文件、HTML可视化页面及12张界面效果图同时配有说明文档结构清晰下载后简单部署即可运行。该项目已有285人学习下载凭借导师认可的98分高评价在同类课程设计中具有很强的参考价值。项目经过严格调试运行稳定具备数据清洗、特征建模、天气预测、图表展示等完整流程界面美观、操作简单既可作为期末大作业或毕业设计的直接模板也是学习Python机器学习实战的优质案例。1. 天气预测模型精度不是拿分重点工程闭环才是第一次做“基于python机器学习(ml)的天气预测和天气可视化”这个题目很容易把精力全压在模型精度上调参调到凌晨R2 从 0.85 磨到 0.87然后发现课程设计报告里根本没地方展示这个 0.02 的提升。真正拉开分数差距的是你能不能把一条链路跑完整——数据采集、特征构造、模型训练、误差分析、可视化展示、文档说明每一步都留下可验证的产出。这篇博客就按这条链路讲用 Open-Meteo 公开天气接口取历史数据用随机森林和梯度提升做温度预测用 matplotlib 把预测结果和特征重要性画成能放进报告里的图最后落在一份结构清晰的源代码目录和文档模板上。适合已经会 Python 基础语法、想做机器学习入门级课程设计又不想只交一个“跑得通就行”的项目的读者。2. 天气数据从哪来公开接口、字段取舍与清洗2.1 选用 Open-Meteo 作为数据源避免 API Key 和反爬问题做天气预测课程设计卡住最多人的不是模型而是数据获取。国内常见做法是爬取天气网站但要处理反爬、动态加载、验证码这些和机器学习无关的工程问题大概率会在答辩前消耗掉你两到三天。另一个思路是申请和风天气之类的商业 API但注册、鉴权、免费额度限制同样会拖慢进度。我一般会直接用 Open-Meteo 的历史天气接口它不需要 API Key也不需要注册请求参数写对就能返回 JSON 格式的逐小时数据适合课程设计这种对数据时效性要求不高、但对“能稳定复现”要求很高的场景。接口地址是https://archive-api.open-meteo.com/v1/archive通过latitude和longitude指定地点start_date和end_date指定时间范围hourly参数指定要哪些字段。下面这段代码拉取北京某个坐标过去两年的逐小时温度、湿度、风速和降水量import requests import pandas as pd url https://archive-api.open-meteo.com/v1/archive params { latitude: 39.9042, longitude: 116.4074, start_date: 2022-01-01, end_date: 2023-12-31, hourly: [temperature_2m, relative_humidity_2m, wind_speed_10m, precipitation], timezone: Asia/Shanghai } resp requests.get(url, paramsparams, timeout30) data resp.json() df pd.DataFrame(data[hourly]) df[time] pd.to_datetime(df[time]) df.to_csv(beijing_weather.csv, indexFalse) print(df.shape) print(df.head())hourly参数里的temperature_2m是离地两米的气温relative_humidity_2m是相对湿度wind_speed_10m是离地十米的风速precipitation是降水量。返回的数据是逐小时一条一天 24 条两年约 17520 条对课程设计来说数据量完全够用。timezone参数建议显式指定否则返回的是 UTC 时间之后做特征工程时“小时”这个特征会和本地作息错位。2.2 缺失值处理与时间特征构造Open-Meteo 的 archive 接口对气象站覆盖较好的地区数据缺失率通常很低但依然可能出现个别小时没有观测值的情况。常见的处理策略是连续缺失不超过 3 条就用线性插值df.interpolate()这是时间序列里最稳妥的填充方式之一如果某一天的缺失条目过多直接按天删掉避免插值引入伪模式。注意不要用均值填充小时级气温的均值填充会把夜间低温拉高等于往训练数据里注入噪声。构造特征时“时间”本身是天气预测里信息量最大的变量。温度有明显的年周期冬季低夏季高、日周期凌晨低下午高和星期周期工作日和周末的人类活动差异。直接用时间戳的数字作为特征模型的线性部分学不到周期性树模型也只能靠硬切分效果不稳定。常见做法是把时间拆成年积日、小时、星期几再用正弦余弦编码把周期性展开df[hour] df[time].dt.hour df[dayofyear] df[time].dt.dayofyear df[weekday] df[time].dt.weekday df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24) df[day_sin] np.sin(2 * np.pi * df[dayofyear] / 365) df[day_cos] np.cos(2 * np.pi * df[dayofyear] / 365)之所以用sin/cos而不是直接的数值是因为第 23 点和第 0 点之间相隔 1 小时但在“小时”这个数值上差了 23模型会误以为它们相距很远。映射到圆上之后23 点和 0 点在三角函数空间里是相邻的。dayofyear同理12 月 31 日和 1 月 1 日在圆的维度上是挨着的。下表是最终选用的特征字段清单课程设计文档里放一张这样的表比写三段“我们选取了以下特征”更有说服力字段名来源类型说明hour_sin / hour_cos由时间衍生float小时周期编码区间 [-1, 1]day_sin / day_cos由时间衍生float年周期编码区间 [-1, 1]relative_humidity_2m原始数据float相对湿度百分比数值wind_speed_10m原始数据float风速单位 km/hprecipitation原始数据float降水量单位 mmtemperature_2m原始数据float标签也是待预测目标这里有个容易犯的错误把未来时刻的湿度、风速作为特征去预测未来时刻的温度。你的数据是逐小时记录的如果用 t 时刻的湿度去预测 t 时刻的温度模型能拿到几乎“作弊级”的输入测试集 R2 会虚高到 0.98 以上但根本不能反映真实预测能力。课程设计里这不算严重错误但答辩时老师问“你的模型在真实场景中能用吗”就会露馅。保守的做法是用上一个时刻的观测值来预测下一个时刻的温度也就是给所有气象特征做一阶滞后shift(1)再去掉滞后产生的 NaN 行。3. 机器学习预测模型怎么选基线、对比与参数调整3.1 按时间顺序切分训练集和测试集禁止随机打乱天气数据是时间序列切分方式必须和时间顺序保持一致。很多同学习惯直接train_test_split(df, random_state42)默认参数会随机抽取样本导致训练集里混入测试集时间段的数据模型相当于提前看到了“未来的天气”评估指标虚高。做预测类课程设计标准做法是前 80% 按时间排序作为训练集后 20% 作为测试集不让任何未来信息泄漏到训练过程里。feature_cols [hour_sin, hour_cos, day_sin, day_cos, relative_humidity_2m, wind_speed_10m, precipitation] X df[feature_cols].values y df[temperature_2m].values split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:] print(f训练集样本数: {X_train.shape[0]}, 测试集样本数: {X_test.shape[0]})切分比例 80/20 是课程设计的常用默认值不是硬性规定。数据量超过两万条时可以试 90/10数据量只有几千条时70/30 会更稳妥因为测试集太小会导致误差指标波动明显。判断标准很简单测试集至少要覆盖一年以上的数据保证包含完整的冬夏季节否则评估结果会被单一季节主导模型的泛化能力无法被真实检验。3.2 三个模型横向对比线性回归、随机森林、XGBoost天气预测的课程设计不需要上神经网络也不需要堆 LSTM。ML 层面把三个经典模型做对比已经能撑起完整的实验章节线性回归作为基线随机森林体现树模型的非线性拟合能力XGBoost 展示梯度提升在结构化数据上的优势。三者训练时间都在秒级学生机也能跑这是选它们最现实的理由。from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score import numpy as np models { 线性回归: LinearRegression(), 随机森林: RandomForestRegressor( n_estimators200, max_depth12, min_samples_split6, min_samples_leaf2, n_jobs-1, random_state42 ), XGBoost: XGBRegressor( n_estimators300, max_depth6, learning_rate0.05, subsample0.8, colsample_bytree0.8, random_state42 ) } for name, model in models.items(): model.fit(X_train, y_train) y_pred model.predict(X_test) mae mean_absolute_error(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) r2 r2_score(y_test, y_pred) print(f{name} MAE{mae:.2f}℃ RMSE{rmse:.2f}℃ R2{r2:.4f})评估指标选三个就够MAE 告诉你平均偏差几度RMSE 对大误差更敏感如果某个极端天气日预测差了七八度RMSE 会比 MAE 高出一截一眼就能看出模型的尾部误差情况R2 用于向老师解释模型的拟合程度。不要只报 R2因为 R2 对时间序列的切分方式非常敏感同一个模型在随机切分下 R2 可能到 0.95时间切分下掉到 0.85这不是模型变差了是评估方式变严格了。三个模型的核心参数含义是答辩时老师几乎必问的点用下面的表准备答案参数随机森林中的含义调参倾向n_estimators决策树的数量200 左右性价比高超过 500 收益递减max_depth单棵树的最大深度限制在 8~15防止单棵树过拟合min_samples_split内部节点再划分所需最小样本数调大到 6~10树更稳健min_samples_leaf叶子节点最少样本数调到 2~4避免叶子上只有一个样本n_jobs并行使用的 CPU 核数设为 -1 用满所有核XGBoost 的learning_rate和n_estimators需要配合调学习率越小需要的树越多0.05 配 300 棵是经验起点。subsample和colsample_bytree各取 0.8给每棵树一定的随机性不容易过拟合。课程设计不要求网格搜索调参手动试两三组参数在文档里把指标对比做成表格就达到了“有实验对比”的要求。3.3 模型指标很差时的排查思路如果测试集上 MAE 超过 3℃或者 R2 低于 0.7先别急着换模型按下面顺序排查第一检查是不是特征泄漏。看训练集和测试集在时间轴上是否有交集最稳妥的办法是打印df[time].iloc[split_idx]确认切分点。第二检查滞后处理是否生效。如果直接把原始湿度风速放进特征矩阵模型会学到“湿度和温度同时变化”这种时序相关看似预测准了实际是把真值旁边的信息泄露给了模型。第三检查极端值。用df[temperature_2m].describe()看最大最小温度如果个别天的温度骤变幅度异常考虑删除这些日期的数据再做训练。第四检查目标变量的分布是否接近正态如果长尾明显可以尝试训练时对目标变量做np.log1p但预测结果要记得指数还原。4. 天气可视化预测结果看得见数值才站得住4.1 先画真实值与预测值的对比图别急着画地图可视化不是越复杂越好课程设计里最常见的问题是一上来就画全国地图热力图结果数据量撑不住图出来稀稀拉拉几个点老师看着也尴尬。我一般建议优先画三种图真实值 vs 预测值折线对比、误差分布直方图、特征重要性柱状图。这三种图能直接回应“模型预测得准不准”和“模型靠什么预测”两个核心问题正好对应答辩必问。对比折线图如果画全部测试集样本几千个小时的曲线会挤在一起肉眼什么都看不出来。常见的处理是只取测试集前 300 条样本同时把 x 轴刻度间隔拉开避免横坐标过于密集导致标签重叠import matplotlib.pyplot as plt y_pred_rf models[随机森林].predict(X_test) show_n 300 plt.figure(figsize(14, 5)) plt.plot(y_test[:show_n], label真实温度, linewidth1.5, color#1f77b4) plt.plot(y_pred_rf[:show_n], label预测温度, linewidth1.5, color#ff7f0e, alpha0.8) plt.legend() plt.ylabel(温度 (℃)) plt.xlabel(样本序号 (逐小时)) # x 轴每 24 个点标一个刻度对应每天一条标签避免横坐标太密集 xticks_step 24 plt.xticks(ticksrange(0, show_n, xticks_step), labelsrange(0, show_n, xticks_step)) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(prediction_compare.png, dpi150)xticks的两个参数是关键ticks指定刻度放置在哪些样本序号的点上labels指定这些点显示的文字二者长度必须一致。如果不做这一步matplotlib 默认会尽可能显示所有刻度300 个小时的层级标签挤在一起图像完全不能看。这个“横坐标太密集”的问题在时间序列可视化里非常常见处理逻辑就是手动抽样刻度。误差分布图用直方图画出“真实值减预测值”的分布形态理想状态是围绕 0 对称、大多集中在 ±2℃ 以内errors y_test - y_pred_rf plt.figure(figsize(8, 4)) plt.hist(errors, bins40, color#2ca02c, alpha0.75, edgecolorwhite) plt.axvline(x0, colorred, linestyle--, linewidth1) plt.xlabel(预测误差 (℃)) plt.ylabel(频数) plt.title(随机森林预测误差分布) plt.tight_layout() plt.savefig(error_hist.png, dpi150)误差分布如果明显偏离 0说明模型存在系统性偏差比如总是高估低温、低估高温。这种情况可以在文档里作为“模型不足与改进方向”来写比假装模型完美更有说服力。注意 matplotlib 默认字体不支持中文标题里的汉字会变成方块需要显式指定中文字体文件路径常见做法是用plt.rcParams[font.sans-serif] [SimHei]如果本机没有 SimHei就用系统已有的中文字体或者干脆把图中文字改成英文。4.2 特征重要性排序回答“模型为什么这样预测”随机森林和 XGBoost 都内置了特征重要性属性直接调用即可。特征重要性在课程设计文档里的价值比模型精度更值得写它展示了模型不仅是“一个黑盒能跑通”还能解释哪些因素主导了预测结果。importances models[随机森林].feature_importances_ feat_names feature_cols plt.figure(figsize(8, 5)) plt.barh(feat_names, importances, color#9467bd) plt.xlabel(特征重要性) plt.title(随机森林特征重要性排序) plt.tight_layout() plt.savefig(feature_importance.png, dpi150) for name, imp in sorted(zip(feat_names, importances), keylambda x: x[1], reverseTrue): print(f{name}: {imp:.4f})feature_importances_返回的数值是归一化后的比例所有特征的重要性加和为 1。数值越大说明该特征对划分决策树的贡献越大。树模型的特征重要性有一个已知缺陷当两个特征高度相关时重要性会在二者之间被摊薄所以不要只凭柱状图就断言“湿度完全没用”只能说“在这个模型、这批数据下它的贡献相对较小”。这段代码在答辩时还有一个用处当老师问“你做了特征选择吗”你可以指着这张图说“通过特征重要性排序保留了主要特征”。即使你实际没有删特征这张图也已经证明了你有特征工程意识。4.3 多城市对比可视化让项目多一层信息密度如果你的课程设计时间充裕可以再拉一两个城市的天气数据做横向对比比如北京和广州同一天的温差曲线。实现方式和单城市完全一致只是把加载数据那段代码封装成函数传入经纬度即可。对比图用两到三条折线画在一起能直观展示模型在不同气候区下的适应能力模型在北京训练直接拿到广州预测误差大概率变大这是预期内的结果写进文档里就是“模型的域迁移局限性分析”属于加分项而不是减分项。5. 源代码和文档说明怎么组织才配得上“高分项目”标签5.1 源代码目录结构分层清晰比代码数量更重要课程设计交源码老师会看工程结构。所有脚本堆在一个文件里的项目第一印象就扣分了。我一般建议把代码拆成四个模块数据采集、特征工程、模型训练、可视化各司其职weather_project/ ├── data/ │ └── beijing_weather.csv ├── src/ │ ├── fetch_data.py │ ├── build_features.py │ ├── train_model.py │ └── visualize.py ├── output/ │ ├── prediction_compare.png │ ├── error_hist.png │ └── feature_importance.png ├── requirements.txt └── README.mdfetch_data.py负责请求 Open-Meteo 接口并保存原始 CSVbuild_features.py负责缺失值处理和时间特征编码train_model.py输出模型指标并保存训练好的模型文件visualize.py读取模型预测结果生成图片。四个脚本按顺序运行前一脚本的输出是后一脚本的输入。requirements.txt里固定版本号至少列全pandas、numpy、requests、scikit-learn、matplotlib、xgboost这几个库。不用写 web 框架不用做前端课程设计的核心是机器学习流程的完整性不是产品化能力。写代码时注意一个重要细节每个脚本都要有if __name__ __main__:入口并允许通过命令行参数指定输入输出路径。答辩演示时老师如果问“如果我想换一个城市的数据呢”你只需要回复“改fetch_data.py里的经纬度参数然后重新运行”这比临时改代码要从容得多。5.2 文档说明把数据流和模型对比写清楚文档说明是“高分项目”里占比最重的一环但这里的“文档”不是指把代码逐行注释而是指一份能让人按图索骥运行完整流程的说明书。课程设计文档的标准结构一般是项目简介、技术选型、数据获取与处理、特征工程、模型训练与对比、可视化结果、总结与展望。核心要求是“可复现”三个字评审人照着你的文档应该能独立跑出你报告里展示的指标和图表。写文档最容易踩的坑是只写“做了什么”不写“为什么这么做”。比如只写“我们选用了随机森林”不解释为什么不用线性回归。简单的三行对比就足够“线性回归作为基线在测试集上 MAE 为 2.84℃随机森林 MAE 为 2.13℃说明温度预测中存在线性模型无法捕获的非线性关系XGBoost 在随机森林基础上进一步降低误差到 1.96℃验证了梯度提升框架在小规模表格数据上的优势。”每个结论都配一个指标数值这就是评审想要的“有数据支撑的结论”。文档里建议放一个运行环境说明包括 Python 版本建议3.9 及以上、每个依赖库的版本号、运行各脚本的顺序以及每条命令的预期输出。别人拿到手如果第一步就卡在环境安装后续内容写得再好也没有说服力。注意这里的环境配置在 Windows 上安装 XGBoost 有时需要额外处理课程设计如果用的是 pandas 和 scikit-learn 覆盖不到的库环境说明要格外详细。5.3 答辩高频问题与应对思路答辩环节三个问题出现频率最高提前准备就不用临场组织语言第一个问题“为什么选择随机森林而不是深度学习模型”回答思路数据量为两万条级别逐小时特征只有七个维度深度学习在小规模结构化数据上优势不明显反而需要更多的调参成本和更长的训练时间随机森林和 XGBoost 在表格数据上通常能取得和深度学习相近或更好的效果且训练速度快、可解释性强。这个回答既展示了对数据规模的理解也避免了“因为我会随机森林所以选了它”的被动局面。第二个问题“特征是怎么构造出来的”回答思路先讲时间特征的周期性编码说明为什么不用原始小时数再用滞后操作说明如何避免数据泄漏。如果能把“23 点和 0 点必须相邻”这个逻辑用一两句话说清楚老师基本不会再追问。第三个问题“模型还有哪些改进空间”回答思路三个方面数据上可以加入气压、云量等更多气象观测字段特征上可以增加滚动平均和滞后多阶构造时序特征模型上可以尝试 LightGBM 和 Prophet还可以用网格搜索对现有模型做参数优化。注意不要说“这个模型已经做到最好了”说“在有限数据和时间下这个模型达到了合理的基线水平后续可以从以上方向迭代”既客观又展示了你对机器学习领域的理解边界。改进方向不用真的全做出来在文档里写出思路即视为加分项因为课程设计的核心是“证明你会做”不是“证明你做到了完美”。本文还有配套的精品资源点击获取