Python天气预测实战:从数据清洗到随机森林建模与可视化
简介基于Python机器学习实现天气预测与可视化的课程设计项目适合高校学生作为数据科学、机器学习课程实战练手。压缩包共25个文件包含4个Python源码、4个CSV数据集、训练好的模型文件及说明文档等全部内容仅1.42MB轻量易部署。项目从天气数据获取与清洗、特征工程到线性回归、决策树、随机森林等算法建模再通过Matplotlib与Seaborn完成温度、湿度、风速等多维可视化覆盖数据分析与模型部署完整链路帮助初学者快速跑通全流程。目前已有38人学习资料提供完整可运行代码与配套数据集便于参考实现与二次开发。1. 一个 Zip 包里的完整天气预测闭环拿到这个项目压缩包时我第一反应是“又一个课设缝合怪”但解开之后发现它把数据获取、清洗、建模、预测、可视化到模型持久化全串起来了——main.py、GetData.py、ProcessData.py、GetModel.py、Model.pkl 一个不少连天气网.html 都打包了。这意味着你不需要去 Kaggle 找现成数据集也不需要调第三方天气 API靠包里给的数据和脚本就能复现一条可运行的机器学习流水线。对于正在做 Python 课程设计、或者想快速上手 sklearn 完整流程的人来说这个项目比一堆零散的 notebook 有价值得多。它解决的问题很实在如何用历史天气数据训练一个分类或回归模型预测未来的天气状况并把预测结果和真实分布用图表展示出来。适合三类人——刚学完 pandas 和 sklearn 基础、需要完整项目练手的学生想抄一套“数据清洗模型训练可视化”代码骨架的开发者以及想了解天气数据特征工程该怎么做的人。当然项目里有些代码不一定写得最优但这恰恰是拆解和改写的价值所在。2. ProcessData.py 里的数据清洗链路从缺失值到特征工程2.1 原始 CSV 的结构与字段猜测压缩包里的 date_train.csv、date_test.csv、china_today.csv 是三个核心数据文件。从命名推断前两个是训练集和测试集china_today.csv 可能是当天全国天气实况或预测数据。常见的天气数据集字段包括日期、最高温、最低温、天气现象、风向、风力、湿度、气压等而 date 开头的文件名暗示时间序列特征。在动手之前我会先用 pandas 快速探查数据结构import pandas as pd train pd.read_csv(date_train.csv, encodingutf-8-sig) print(train.shape) print(train.columns.tolist()) print(train.head(10)) print(train.dtypes) print(train.isnull().sum())这段代码做了四件事读取文件并自动处理可能的 BOM 头输出数据集维度和小样本列出所有列名及数据类型统计每列缺失值数量。encoding 参数用 utf-8-sig 是兼容 Windows 下 Excel 导出的 CSV 文件如果乱码就换成 gb2312 或 gbk 再试。参数说明encodingutf-8-sig会在读取时去掉文件开头的 BOM 字符train.isnull().sum()返回一个 Series索引是列名值是该列的缺失数量这是判断数据质量的第一张体检报告。从项目截图里的 wps 文件来看数据可能包含降水、湿度等多列且存在离散的天气现象文本需要转换成数值型特征后才能喂给模型。2.2 缺失值处理的三种策略天气数据最常见的缺失场景是某几天湿度传感器故障、或者夜间没有日照导致紫外线列全空。针对不同情况我一般会做层次化处理列类型缺失比例处理方式理由数值型温度、湿度、气压5%用前向填充法ffill或中位数填充天气数据连续性强前后天数值接近数值型风速、降水5%~20%用线性插值法interpolate插值能保留趋势变化比固定值更平滑类别型天气现象、风向少量用众数填充或单独生成“未知”类别类别不平衡时众数最安全时序关键列日期任何比例直接删除该行日期缺失会破坏时间序列对齐ProcessData.py 里最可能用到的是前向填充和 dropna 组合因为课程设计数据一般不会挖太深。但如果你要让模型效果更好线性插值值得换上去试试。对于温度这种昼夜周期性明显的特征前向填充会导致预测值滞后一天这是常见坑。2.3 类别特征编码把“多云转晴”变成数字天气现象是中文文本sklearn 的模型只能吃数值。常见做法是先用 pandas 的 factorize 或 sklearn 的 LabelEncoder 做标签编码但要注意如果是无序分类比如阴、晴、雨直接用 0、1、2 会让树模型认为类别之间有大小关系。我的处理方式分两种如果天气现象作为预测目标用 LabelEncoder如果作为输入特征用 One-Hot 编码或者直接把几个关键现象拆成布尔列是否下雨、是否下雪、是否多云。树模型用 LabelEncoder 问题不大但线性模型必须 One-Hot。ProcessData.py 里如果直接用 factorize我建议改成 pd.get_dummies 看看效果差异。from sklearn.preprocessing import LabelEncoder le LabelEncoder() train[weather_label] le.fit_transform(train[weather_type]) test[weather_label] le.transform(test[weather_type]) # 保存映射关系预测结束后反解 label_mapping dict(zip(le.classes_, le.transform(le.classes_)))这段代码的关键点是 fit_transform 只能用在训练集测试集必须用 transform 复用同一个映射否则标签编码对不上。le.classes_保存了原始类别列表预测完用le.inverse_transform()就能把数字还原成天气文本。这是课程设计答辩时老师最常问的点值得在代码注释里写清楚。2.4 特征归一化和时间特征提取温度和湿度数值范围差异大如果用 SVM 或 KNN不归一化会导致距离计算被数值较大的特征主导。随机森林和 XGBoost 不受影响但作为课程设计最好还是展示标准流程。我一般在 ProcessData.py 里加两个步骤一是用 StandardScaler 对连续特征做 Z-score 标准化二是从 date 列拆出年、月、日、星期几、是否是周末等时间特征。天气有明显季节周期和星期效应这些在原始的日期字符串里是看不出来的。from sklearn.preprocessing import StandardScaler feature_cols [temp_max, temp_min, humidity, pressure] scaler StandardScaler() train[feature_cols] scaler.fit_transform(train[feature_cols]) test[feature_cols] scaler.transform(test[feature_cols]) # 时间特征提取 train[date] pd.to_datetime(train[date]) train[month] train[date].dt.month train[weekday] train[date].dt.weekday train[is_weekend] train[weekday].isin([5, 6]).astype(int)标准化方向先在训练集上 fit得到均值和标准差再用同一套参数去 transform 测试集。如果在测试集上重新 fit会导致数据泄漏——测试集的信息混进了标准化参数里模型评估结果虚高。时间特征提取中dt.month和dt.weekday是 pandas 内置的 datetime 访问器比字符串切片更可靠。is_weekend 这列对预测降雨、人流量相关的天气场景有帮助因为在树模型里它可以作为强分裂点。3. GetModel.py 的模型选择从线性回归到随机森林的取舍3.1 预测目标决定模型家族这个项目标题是“天气预测”但预测目标不同模型选型完全不同。如果目标是预测明天的具体温度数值属于回归问题如果预测“晴/雨/阴”这种类别属于分类问题。从 date_train.csv 这种命名习惯看训练集有标签列很可能同时包含温度回归和天气现象分类两个任务main.py 里可能是分开建模的。我通常会先跑一个基线模型确定下限比如线性回归或逻辑回归。如果分数过低再上随机森林因为它在课程设计里有三个不可替代的优势不需要复杂调参、能输出特征重要性、对非线性关系捕获能力强。支持向量机在这类任务是备选但天气数据噪声大RBF 核调参成本高不推荐作为第一选择。3.2 用交叉验证替代单次切分课程设计里最常见的问题是 train_test_split 只切一次就出最终结果这样模型评估的方差很大。数据顺序如果是按时间排列的随机切分还会造成未来数据泄漏到训练集。正确的做法是用时间序列切分或者 K 折交叉验证。from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import cross_val_score, KFold from sklearn.metrics import accuracy_score model RandomForestClassifier( n_estimators200, max_depth12, min_samples_split5, random_state42 ) kf KFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(model, train_features, train_labels, cvkf, scoringaccuracy) print(fCV Accuracy: {scores.mean():.4f} (/- {scores.std():.4f}))这里用 KFold 替换默认的 train_test_split每一折都用 80% 数据训练、20% 验证最终取 5 次结果均值能更稳定地反映模型真实水平。random_state 固定后结果可复现这是老师检查代码规范性时一定会看的细节。参数解释n_estimators 是随机森林中决策树的数量200 在天气数据规模下是性价比最高的区间max_depth12 限制单棵树深度防止过拟合min_samples_split5 表示节点分裂至少需要 5 个样本进一步正则化。如果训练集有 8000 条以上可以试试RandomForestRegressor预测最高温和最低温两个独立模型然后用classification_report输出精确率、召回率、F1 值这比只看准确率有说服力得多。3.3 特征重要性分析作为模型诊断随机森林在训练完成后可以通过 feature_importances_ 直接查看每个特征对预测的贡献度。这一步对天气预测特别有价值能验证数据直觉比如“湿度对降雨预测最重要”“月份对温度预测最重要”。import numpy as np model.fit(train_features, train_labels) importance pd.DataFrame({ feature: train_features.columns, importance: model.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance) importance.head(8).plot.bar(xfeature, yimportance, figsize(10, 5))输出结果后重点看是不是有特征贡献度几乎为零。如果有比如风向编码列的 importance 低于 0.01可以考虑删掉再训练模型效果通常不会下降反而更快。这也是对 ProcessData.py 特征选择结果的闭环验证——特征工程做得好不好看这里就明白了。3.4 模型持久化的实现方式项目里自带 Model.pkl说明 GetModel.py 最后一定用了序列化保存模型。我一般用 joblib 而不是 pickle因为 joblib 对大 numpy 数组更高效而且压缩后体积更小。import joblib joblib.dump(model, Model.pkl) # 预测阶段加载 loaded_model joblib.load(Model.pkl) predictions loaded_model.predict(test_features)joblib.dump 的输出文件就是压缩包里的 Model.pkl。加载后无需重新训练直接 predict 即可。这里要注意如果保存模型时用了 LabelEncoder 和 StandardScaler加载模型时也必须把这两个转换器同时保存否则新数据无法正确处理。所以规范的做法是把它打包成一个 pipeline后面第 5 章会展开讲。4. main.py 的预测流程与天气可视化输出4.1 主程序的运行逻辑拆解main.py 是整个项目的入口承担三件事加载已训练好的模型、读取测试数据做预测、把预测结果和真实数据画成图。课程设计答辩时这段代码是必看的因为它决定了项目“能不能跑起来”。一种常见的 main.py 结构是先从 date_test.csv 读入测试集做和训练集完全相同的预处理然后调用 Model.pkl 做预测最后合并结果绘制可视化图表。如果测试集没有标签列只展示预测结果如果有可以把真实值和预测值放在同一张图里对比。import pandas as pd import joblib import matplotlib.pyplot as plt import matplotlib.dates as mdates # 加载模型和标签映射假设已经存在 model joblib.load(Model.pkl) test_data pd.read_csv(date_test.csv, encodingutf-8-sig) # 特征工程与 ProcessData.py 保持一致 test_features process_test_data(test_data) # 预测 test_data[predict] model.predict(test_features) test_data[date] pd.to_datetime(test_data[date])process_test_data函数在 main.py 里的职责就是复现 ProcessData.py 的清洗逻辑包括缺失值填充、标准化、编码映射。这里最大的坑是测试集和训练集的编码映射不一致比如训练集里天气现象有 5 类测试集里出现第 6 类transform 时会直接报错。处理方式是在 ProcessData.py 里把类别全集定义好或者在 main.py 里用 try-except 捕获并归为未知类。4.2 用 matplotlib 绘制双轴图展示温湿度预测天气可视化不能只画一条线的折线图那样信息量太低。我建议用双 Y 轴柱线混合图柱状图展示日降水或天气类别折线图展示最高温和最低温预测值。这种图在课程设计报告里截图效果很好也直观体现预测能力。fig, ax1 plt.subplots(figsize(14, 6)) ax1.plot(test_data[date], test_data[pred_max_temp], colorred, linewidth2, labelPred Max Temp) ax1.plot(test_data[date], test_data[pred_min_temp], colorblue, linewidth2, labelPred Min Temp) ax1.set_xlabel(Date, fontsize12) ax1.set_ylabel(Temperature (°C), fontsize12) ax1.tick_params(axisx, rotation45) ax2 ax1.twinx() ax2.bar(test_data[date], test_data[predict], alpha0.3, colorgray, labelWeather Category) ax2.set_ylabel(Category Code, fontsize12) lines1, labels1 ax1.get_legend_handles_labels() lines2, labels2 ax2.get_legend_handles_labels() ax1.legend(lines1 lines2, labels1 labels2, locupper left) ax1.xaxis.set_major_formatter(mdates.DateFormatter(%m-%d)) ax1.grid(True, linestyle--, alpha0.4) plt.tight_layout() plt.savefig(prediction_result.png, dpi150)twinx() 创建共享 X 轴的第二个坐标系解决了温度和类别数值范围量级不同的图例覆盖问题。alpha0.3 控制柱状图透明度避免遮住折线。mdates.DateFormatter(%m-%d)把 X 轴刻度格式化为“月-日”防止显示时间戳时分秒带来的拥挤。保存图片时 dpi150 是课程设计打印入报告的最低清晰度要求。4.3 混淆矩阵热力图评估分类效果如果测试集带了天气现象的真实标签光画预测曲线没有说服力还应该可视化混淆矩阵。用 sklearn.metrics 计算混淆矩阵并用 seaborn 热力图展示一眼就能看出哪两类容易被混淆比如“小雨”和“阵雨”最容易分错。from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay cm confusion_matrix(test_data[actual], test_data[predict]) disp ConfusionMatrixDisplay(confusion_matrixcm, display_labelslabel_mapping.keys()) disp.plot(cmapBlues, values_formatd) plt.xticks(rotation45) plt.tight_layout() plt.savefig(confusion_matrix.png, dpi150)values_formatd强制显示整数避免科学计数法影响阅读。display_labels 传原始天气名称而不是编码数字这样老师不用查表就能看懂。ConfusionMatrixDisplay 是 matplotlib 之后的新 API比直接 plt.imshow 更简洁但需要 import 这个专属函数老版本 sklearn 可能没有。5. 从课程设计到工程化Pipeline 封装与天气数据爬取改写5.1 用 Pipeline 统一预处理和模型杜绝测试集数据泄漏课程设计里最容易暴露“代码是抄的”的细节就是测试集处理时重新 fit 了 StandardScaler 或 LabelEncoder。用 sklearn 的 Pipeline 能从根本上解决这个问题——它会把标准化、编码、模型训练封装成一个整体fit 时在训练集上学习参数predict 时自动复用这些参数。from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.compose import ColumnTransformer from sklearn.ensemble import RandomForestClassifier numeric_features [temp_max, temp_min, humidity] categorical_features [wind_direction] preprocessor ColumnTransformer([ (num, StandardScaler(), numeric_features), (cat, OneHotEncoder(handle_unknownignore), categorical_features) ]) pipe Pipeline([ (preprocess, preprocessor), (model, RandomForestClassifier(n_estimators200, random_state42)) ]) pipe.fit(X_train, y_train) y_pred pipe.predict(X_test) joblib.dump(pipe, Model_with_pipeline.pkl)handle_unknownignore处理测试集出现新类别的情况One-Hot 编码器不会报错而是把新类别全部编码为 0。这一行代码非常实用因为天气现象这种人工填写的字段在训练集中很可能不完整。Pipeline 保存后加载时不需要再单独保存 scaler 和 encoder因为已经打包在 pipeline 里了。我之前在实际项目里遇到过学生把三个模型分开保存结果把 LabelEncoder 的 classes_ 顺序弄丢预测结果全是乱码用 Pipeline 就完全规避了这个问题。5.2 GetData.py 的天气数据爬取思路拆解压缩包里的天气网.html 说明 GetData.py 很可能是从某个中国天气网站抓数据。我没有项目里给定的具体爬虫代码不方便反推作者封装的地址和解析方式但根据项目场景最可能的实现是用 Requests 获取页面、用 BeautifulSoup 或正则解析表格。到了这一步项目里常见的爬虫写法是下面这样的骨架import requests from bs4 import BeautifulSoup import pandas as pd url https://some_weather_history_page # 实际地址以项目说明.txt 为准 headers { user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) # 找到历史天气表格 table soup.find(table, class_history-table) rows table.find_all(tr) data [] for row in rows[1:]: cells row.find_all(td) if len(cells) 5: data.append({ date: cells[0].text.strip(), weather_type: cells[1].text.strip(), temp_max: int(cells[2].text.replace(℃, ).strip()), wind_direction: cells[3].text.strip(), }) df pd.DataFrame(data) df.to_csv(date_train.csv, indexFalse, encodingutf-8-sig)这段爬虫的核心是三件事伪造 User-Agent 绕过基础反爬、用 BeautifulSoup 的 find 精准定位表格、清洗单元格文本中的单位字符。很多课程设计只给一个写死的 csv而这个项目允许你自己扩展数据源这在答辩时是很好的加分点。需要注意大多数天气网站有访问频率限制爬取时最好加上 time.sleep(2) 控制请求间隔。5.3 数据验证的最后一个技巧用回归残差图发现季节偏差预测做完后不要只看准确率或 R² 就完事。把残差真实值 - 预测值按时间顺序画出来如果有明显的周期性波动说明模型没有抓到季节规律。比如连续 30 个点的残差为正说明模型系统性低估了某段时间的温度这时候把月份特征放进模型通常能解决问题。判定方法很简单residual test_data[actual_temp] - test_data[pred_max_temp] residual.plot(figsize(14, 3), titleResidual Over Time) plt.axhline(0, colorblack, linestyle--, linewidth1) plt.xlabel(Sample Index) plt.ylabel(Prediction Error (℃))随机残差会在 0 轴附近均匀上下波动如果出现明显的“波浪”或“漂移”就得回 ProcessData.py 检查特征工程。这种验证方式比单看均方误差更细腻也是课程设计报告里能突出“思考深度”的一页。把这张图放到报告末尾配合“模型在换季时偏差增大”的小结项目的技术含量会明显上一个档次。本文还有配套的精品资源点击获取