
简介这是一份Python课程设计项目面向需要完成机器学习相关课题的学生也适合想通过实战入门数据科学的开发者。项目以天气预测为场景完整演示了从数据获取、缺失值处理、标准化到特征选择的预处理流程并引入线性回归、决策树、随机森林、支持向量机等算法训练模型结合交叉验证评估性能最后通过matplotlib/seaborn可视化手段直观展示温度、湿度、风速等气象要素的变化与预测结果。压缩包内共25个文件以4个py代码文件为核心覆盖数据爬取、处理、建模与主程序搭配4个csv气象数据集、1个训练好的Model.pkl模型、12张结果图表及说明文档整体仅1.42MB轻量且目录结构清晰。目前已有38人学习下载适合作为课程设计参考或机器学习入门实践。读者可基于完整代码与数据直接复现预测流程并在此之上扩展参数调优、模型对比等内容。1. 课程设计里的天气预测为什么值得用 Python 机器学习重做一遍又到了课程设计季十份选题里至少有三份是天气预测。但多数版本还停在“调一个 sklearn 的 DecisionTreeClassifier丢进去年今天的温度跑出一个 70% 的准确率就收工”。这个标题里真正值钱的部分不是分类器本身而是把“天气预测”从一道课后习题抬升为一个完整机器学习闭环历史数据怎么拿、特征怎么构造、模型怎么选、误差怎么评估最后再用可视化把结果讲清楚。这一套流程在工程上和竞赛里都是同一套打法区别只在于数据规模和特征复杂度。对还没毕业的人来说这份代码是答辩时的底气对工作三五年的人而言这个项目正好用来验证自己对特征工程、时间序列切分、模型偏差方差判断这些基本功是否真的过关。下文按数据准备、建模、可视化、工程化四个层级展开命令和代码均可直接复现。2. 天气预测的数据从哪来免费 API、本地 CSV 与特征构造2.1 数据源的三种选择以及它们各自的坑天气预测的第一步是拿到一份带时间戳的历史观测数据。常见做法有三条路按推荐程度排序数据源获取方式优点坑Open-Meteo APIHTTP 请求 JSON免费、无需 key、字段全国内直连偶尔不稳定需重试机制本地 CSV 数据集Kaggle / 政府气象站下载稳定、可离线字段命名不统一时间格式混乱爬虫抓取天气网站requests BeautifulSoup数据新鲜反爬、页面结构变更、法律风险如果你只是交课程设计最稳妥的是 CSV。如果想让答辩有亮点用 API 实时拉数据再落盘保存能顺手展示“数据采集—清洗—入库”的完整链路。Open-Meteo 无需注册请求示例import requests import pandas as pd from datetime import datetime, timedelta # 以北京为例纬度39.9经度116.4过去5年每日最高/最低温 url https://archive-api.open-meteo.com/v1/archive params { latitude: 39.9, longitude: 116.4, start_date: 2019-01-01, end_date: 2024-01-01, daily: temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max, timezone: Asia/Shanghai } resp requests.get(url, paramsparams, timeout30) data resp.json()[daily] df pd.DataFrame(data) df.to_csv(beijing_weather.csv, indexFalse)这段代码的核心在daily参数它决定了你能拿到哪些字段。temperature_2m_max是当日最高气温precipitation_sum是降水总量windspeed_10m_max是最大风速。如果你是预测“明天是否下雨”需要注意这类 archive 接口返回的是历史实况不是预报值——用实况训练模型预测未来本质上是在做“后报”这没问题但答辩时老师问起你要能说清楚。2.2 特征工程把日期变成模型能理解的东西拿到原始数据后直接丢给模型是大忌。2019-06-15这个字符串对机器学习模型来说是无效信息必须拆解成可计算的数值特征。常见做法是构造三组时间循环特征一年有周期用sin/cos编码“一年中的第几天”和“一天中的第几小时”避免 12 月 31 日和 1 月 1 日在数值上被误判为“距离很远”。滞后特征昨天和前天的最低温、最高温这是时间序列预测里最有效的特征。滑动窗口特征过去 7 天的平均温度、最高温极值、降水累计捕捉短期趋势。import numpy as np # 时间列转 datetime df[date] pd.to_datetime(df[date]) # 循环特征一年365天用sin/cos表达周期性 day_of_year df[date].dt.dayofyear df[day_sin] np.sin(2 * np.pi * day_of_year / 365) df[day_cos] np.cos(2 * np.pi * day_of_year / 365) # 滞后特征昨天和前天的最低/最高温 df[temp_min_lag1] df[temperature_2m_min].shift(1) df[temp_min_lag2] df[temperature_2m_min].shift(2) df[temp_max_lag1] df[temperature_2m_max].shift(1) # 滑动窗口过去7天平均最低温 df[temp_min_7d_mean] df[temperature_2m_min].shift(1).rolling(7).mean() # 删除前7行空值 df df.dropna().reset_index(dropTrue)shift(1)是时间序列特征工程里最核心的操作。它把“今天的温度”向前挪一天让模型在第 N 行看到第 N-1 天的信息从而模拟“用昨天预测今天”的真实预测场景。需要注意rolling(7).mean()必须在shift(1)之后再算否则会把当天的真实值泄漏进特征里——这是课程设计里最常见的隐藏扣分点老师们一眼就能看出来。3. 机器学习模型怎么选分类还是回归随机森林还是 XGBoost3.1 先回答“预测什么”再决定模型标题里的“天气预测”含义太宽。做项目前必须把问题定义成两类之一分类问题明天是否下雨、属于哪种天气类型晴/多云/雨/雪。回归问题明天最高温是多少度、最低温是多少度。课程设计里最常见的组合是用分类模型预测“明天是否下雨”用回归模型预测“明天最高气温”。一个项目同时覆盖两类问题展示效果最好。下面分别给出代码。3.2 分类模型随机森林预测是否下雨from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report # 构造二分类标签降水0.1mm视为下雨 df[rain_tomorrow] (df[precipitation_sum].shift(-1) 0.1).astype(int) df df.dropna() features [temp_min_lag1, temp_max_lag1, day_sin, day_cos, temp_min_7d_mean, windspeed_10m_max] X df[features] y df[rain_tomorrow] # 时间序列不能随机打乱按时间顺序切分 split_idx int(len(df) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] clf RandomForestClassifier(n_estimators200, max_depth8, random_state42) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))这里最关键的是train_test_split的替代方案时间序列数据绝对不能随机切分否则模型会偷看到“未来”的数据造成准确率虚高。用split_idx按位置切分会牺牲一点点训练样本量但换来的是真实的泛化能力评估。n_estimators200是随机森林的经验值再大收益有限max_depth8是正则化手段防止树在时间序列上过拟合。3.3 回归模型梯度提升树预测最高温from sklearn.ensemble import GradientBoostingRegressor from sklearn.metrics import mean_absolute_error reg GradientBoostingRegressor( n_estimators300, max_depth5, learning_rate0.05, subsample0.8, random_state42 ) reg.fit(X_train[[temp_min_lag1, temp_max_lag1, day_sin, day_cos]], y_train[[temperature_2m_max]] if temperature_2m_max in y_train else y_train) # 注意回归目标需要单独构造等等上面的写法有问题——回归的标签应该是当天的temperature_2m_max但我们不能直接拿当天的特征去预测当天的值否则特征里包含目标信息。正确做法是y_reg df[temperature_2m_max] X_reg df[[temp_min_lag1, temp_max_lag1, temp_min_lag2, day_sin, day_cos, temp_min_7d_mean]] # 同样按时间切分 Xr_train, Xr_test X_reg.iloc[:split_idx], X_reg.iloc[split_idx:] yr_train, yr_test y_reg.iloc[:split_idx], y_reg.iloc[split_idx:] reg GradientBoostingRegressor(n_estimators300, max_depth5, learning_rate0.05, subsample0.8, random_state42) reg.fit(Xr_train, yr_train) y_reg_pred reg.predict(Xr_test) print(MAE:, mean_absolute_error(yr_test, y_reg_pred))梯度提升树在中小规模表格数据上几乎是无脑首选。learning_rate0.05配合n_estimators300比默认的0.1 100在时间序列上更稳不容易在训练集上冲太高然后验证集掉下来。subsample0.8给每棵树只喂 80% 的样本增加多样性相当于内置了 bagging 的防过拟合机制。3.4 模型对比为什么没提 LSTM模型适用场景项目成本课程设计友好度随机森林分类、表格特征低高梯度提升树GBDT / XGBoost回归、表格特征低高SVR支持向量回归小样本非线性中中LSTM / GRU长序列依赖需大量数据高低LSTM 在天气预测这种场景上被严重高估。天气系统有混沌特性几万个样本的日尺度数据不足以让循环神经网络捕捉到足够的动力学模式。常见做法是先用梯度提升树做基线如果确实想展示深度学习能力再在同样的滞后特征上叠一个 LSTM 对比但别指望它能打赢 GBDT。答辩时如果说“我用了 LSTM”老师大概率追问“为什么不用 Transformer 或 GNN”容易把自己绕进去说“我对比了 LSTM 和 GBDT发现 GBDT 在表格特征上更优”反而显得思路清醒。4. 天气可视化matplotlib 画时间序列、混淆矩阵与误差分布4.1 中文乱码问题一次性解决Python 纯等宽字体族里没有中文字形matplotlib 默认画图输出中文是方块。课程设计报告里满屏口口口观感极差。解决方案要在绘图脚本最前面声明import matplotlib.pyplot as plt import matplotlib # 方式一指定系统已有的中文字体Windows 用 SimHeimacOS 用 Arial Unicode MS plt.rcParams[font.sans-serif] [SimHei, Arial Unicode MS, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题axes.unicode_minus这个参数很容易被漏掉。默认情况下负号用的是 ASCII 连字符转到中文字体后会被显示成乱码方块必须显式设为False。如果你在 Linux 服务器上跑可能没有 SimHei要先用fc-list :langzh查看系统有哪些中文字体或者直接用matplotlib.font_manager的FontProperties指定一个 TTF 文件路径更省事。4.2 三张图搞定一份课程设计报告一张时间序列对比图、一张混淆矩阵热力图、一张预测误差散点图足够撑起整个可视化章节。时间序列图用真实值和预测值双线叠加import matplotlib.pyplot as plt # 取测试集最后90天做展示 days Xr_test.index[:90] # 原始索引 plt.figure(figsize(12, 5)) plt.plot(days, yr_test[:90], label真实最高温, linewidth1.5, color#1f77b4) plt.plot(days, y_reg_pred[:90], label预测最高温, linewidth1.5, color#ff7f0e, linestyle--) plt.xlabel(日期) plt.ylabel(摄氏温度 (°C)) plt.title(气温预测真实值 vs 预测值测试集末90天) plt.legend() plt.grid(alpha0.3) plt.tight_layout() plt.savefig(temperature_forecast.png, dpi150)线段型图能直观反映“哪里预测准、哪里滞后”。一般能看到两个现象一是突变天寒潮、骤热预测值会稍微钝化因为模型依赖的是昨天和前天的值对突变天然滞后二是夏季高温比冬季低温好预测因为夏季天气系统更稳定。这两个现象写进报告是加分项说明你真的观察过结果而不是跑完就完事。混淆矩阵用 seaborn 的热力图最省事import seaborn as sns from sklearn.metrics import confusion_matrix cm confusion_matrix(y_test, y_pred) plt.figure(figsize(5, 4)) sns.heatmap(cm, annotTrue, fmtd, cmapBlues, xticklabels[不下雨, 下雨], yticklabels[不下雨, 下雨]) plt.xlabel(预测标签) plt.ylabel(真实标签) plt.title(降水预测混淆矩阵) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)看混淆矩阵时注意一个细节天气数据里“不下雨”往往占多数类别不平衡。如果矩阵右下角真正例很小但整体准确率却很高说明模型在“猜不下雨”偷懒。这时要报告精确率、召回率和 F1而不是只报 accuracy。分类报告里macro avg比weighted avg更能反映模型在少数类上的表现。4.3 用残差图发现模型系统偏差residuals y_reg_pred - yr_test plt.figure(figsize(8, 5)) plt.scatter(yr_test, residuals, alpha0.4, s10) plt.axhline(y0, colorred, linestyle--, linewidth1) plt.xlabel(真实最高温°C) plt.ylabel(预测误差预测值-真实值) plt.title(残差分布高温低估低温高估) plt.tight_layout() plt.savefig(residual.png, dpi150)如果残差点在 0 线附近随机分布说明模型没有系统性偏差如果高温段残差多为负值预测偏低、低温段多为正值预测偏高说明模型存在“回归到均值”的倾向这在时间序列模型里很常见。5. 让课程设计从“能跑”变成“能答辩”三个进阶技巧5.1 用 SHAP 解释模型回答“为什么预测下雨”答辩老师最爱问的一个问题是“这个模型为什么判定明天会下雨”要回答这个问题需要引入模型可解释性。用 SHAP 库对随机森林做特征贡献分析import shap explainer shap.TreeExplainer(clf) shap_values explainer.shap_values(X_test.iloc[:200]) shap.summary_plot(shap_values[1], X_test.iloc[:200], feature_namesfeatures, showFalse) plt.savefig(shap_summary.png, dpi150, bbox_inchestight)shap_values[1]取的是“下雨”这一类别的 SHAP 值summary_plot会显示每个特征对预测结果的贡献方向和大小。通常能看到temp_min_lag1和temp_min_7d_mean贡献最大——前一天冷、过去一周冷第二天更容易下雨。这个结论符合直觉更重要的是它把“黑盒模型”变成了“可解释的决策依据”答辩时老师无从下口挑刺。5.2 用 streamlit 把项目变成网页 Demo可视化文件只能静态展示做成交互式网页 Demo 的冲击力完全不一样。Streamlit 是最轻量的方案十几行代码就能跑起来import streamlit as st import pandas as pd import matplotlib.pyplot as plt st.title(机器学习天气预测系统) uploaded st.file_uploader(上传历史天气 CSV, typecsv) if uploaded: df pd.read_csv(uploaded) st.line_chart(df[[temperature_2m_max, temperature_2m_min]]) st.dataframe(df.tail(10))用streamlit run app.py启动浏览器打开本地地址就能演示预测结果和图表。如果再花半小时把训练好的模型用joblib.dump(clf, model.pkl)序列化保存streamlit里直接加载模型预测新数据就完成了从“脚本”到“应用”的转变。这个环节对评分的提升比调模型的参数还大因为它的“完成度”肉眼可见。5.3 预测未来一周而不是一天课程设计题目常写“天气预测”但绝大多数人只做了“预测明天”。把任务扩展到“未来 7 天”需要两步先训练一个多步模型或用迭代法复用单步模型再引入按星期编码的日历特征比如is_weekend。迭代预测的误差会累积第一天 MAE 可能是 1.8 度到第七天涨到 3 度以上——这个误差扩散曲线本身就是一篇报告的重要组成部分。不要想着用一次训练直接输出 7 个值机器学习模型对多步输出的直接预测效果很差迭代法是工程上最可靠的做法。最后记得在报告里附上requirements.txt标清scikit-learn、pandas、matplotlib、streamlit的版本号——这决定了你的代码在老师的机器上能不能原样跑起来远比美化图表更有用。本文还有配套的精品资源点击获取