多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

机器学习天气预测源码解析:数据清洗、特征工程到可视化

机器学习天气预测源码解析:数据清洗、特征工程到可视化 简介基于Python的机器学习天气预测与数据可视化完整源码属于Python期末大作业与课程设计类资源适合计算机相关专业正在完成大作业、毕业设计或需要实战练习的学习者。项目经导师指导并认可评审得分98分源码均经本地编译调试、确认可正常运行。资源共24个文件压缩包仅1.43MB包含4个Python脚本数据采集、数据处理、模型训练与主程序模块、4个CSV数据集训练集、验证集、测试集及当日气象数据、1个pkl模型文件、可视化HTML页面、12张项目效果截图及readme说明文档完整覆盖数据采集、处理、建模、预测到可视化的全流程。目前已有124人学习下载。下载后可直接运行体验完整流程也可参照截图与说明文档理清各模块设计思路适合作为课程设计报告撰写、答辩展示的参考。1. Python机器学习天气预测源码期末大作业从跑通到讲明白如果你正为Python期末大作业发愁这份基于机器学习的天气预测与数据可视化完整源码值得拆开看一遍。它不是空壳demo数据清洗、特征工程、模型训练到可视化出图一条链路全部跑通源码里函数命名和注释风格也接近答辩要求作为课程资源可以直接拿来改。我拿到这个项目源码时先做了两件事打开目录看文件组织再跑一遍训练入口确认能否直接出结果。确认之后才去读特征构造逻辑。天气预测这类任务九成分数差不在模型多新而在数据切分和特征设计这套资源恰好把这两块做厚了后面章节我会把每一步怎么落地、坑在哪一次性讲清楚。2. 数据与特征工程清洗、滞后特征与时间序列切分的完整流水线源码包的data目录下有一份天气观测历史数据CSV格式包含日期、最高气温、最低气温、湿度、气压、风速、天气状况等字段。项目要预测的核心目标是temp_max也就是当天最高气温。这是一个典型回归任务难点不在模型结构而在怎么把日期、天气类型这些非数值信息变成温度预测可用的输入。下面按清洗、特征构造、切分的顺序拆解源码里的处理逻辑。2.1 数据清洗类型转换、缺失值填充与异常值处理天气数据最常踩的坑是脏数据直接喂模型。源码里的第一步是先做类型转换和缺失值填充。我一般会先打印数据前后几行和缺失值统计再决定填充策略而不是直接dropna。以下是最核心的处理逻辑import pandas as pd df pd.read_csv(data/weather_data.csv, encodingutf-8) df[date] pd.to_datetime(df[date], format%Y-%m-%d) # 提前拆出月份后面做缺失值填补和特征都会用到 df[month] df[date].dt.month # 按月份分组填充避免直接用全局中位数把冬天填成夏天 for col in [humidity, pressure, wind_speed]: df[col] df[col].fillna(df.groupby(month)[col].transform(median)) # 天气状况是文本列用众数填充 df[weather_desc] df[weather_desc].fillna(df[weather_desc].mode()[0]) print(df.isnull().sum())这里有两个关键点pd.to_datetime里format字段必须和数据实际格式匹配如果日期里有小时分钟格式串要写成%Y-%m-%d %H:%M:%S否则解析报错或者解析出全Null。填充策略用groupby按月分组原因很简单气温、湿度、气压都有强季节性全局中位数会把冬季缺失值填成夏季水平模型学到错误的季节偏移。数值列用中位数而不是均值是因为天气数据里偶发极端值会拉高均值中位数更稳。天气状况这类文本列用众数填充即便有一点偏差也不会把类别体系打乱。源码里还做了异常值截断对temp_max超过某个分位数的样本单独检查我一般会看前后七天的温度再决定是否替换绝不直接删整行否则会把寒潮、极端高温这类有效信息丢掉。2.2 特征工程日期拆解、滞后特征与数据量取舍特征构造的目标是让模型感知两件事周期性和惯性。周期性来自季节、月份、星期惯性来自昨天的温度对今天的影响。天气变化不是独立事件高温天后面大概率还是高温天这就是滞后特征存在的理由。源码里特征构造部分是这样的df[dayofweek] df[date].dt.dayofweek def to_season(m): if m in (12, 1, 2): return 1 # 冬 if m in (3, 4, 5): return 2 # 春 if m in (6, 7, 8): return 3 # 夏 return 4 # 秋 df[season] df[month].apply(to_season) # 滞后特征前1天、前2天的最高气温 df[temp_max_lag1] df[temp_max].shift(1) df[temp_max_lag2] df[temp_max].shift(2) # 删除因滞后产生的空行 df df.dropna().reset_index(dropTrue)month和dayofweek是周期性特征season把12个月压缩成4档避免模型把1月和12月当成完全无关的月份。滞后特征shift(1)、shift(2)的含义是取前一天、前两天的最高气温作为今天预测的输入这两个特征在天气预测里往往比湿度、气压更重要因为天气系统有连续性。lag窗口一般从1、2开始不要一上来就做7天、30天滞后滞后步长越大dropna丢掉的样本越多。这段代码dropna之后会少两行如果原始数据只有800条少2行还好但如果滞后特征做到lag7一次性少7行边界月份的样本会被切掉模型遇到季节切换时容易失真。更稳的做法是对滞后列用bfill填充或者前几行直接沿用首条有效值让样本量损失降到最低。2.3 数据集划分时间顺序切分与特征列选择天气预测项目里这个步骤最容易被低估。数据切分决定了模型验证结果有没有说服力。很多人在这一步直接调用train_test_split默认shuffleTrue数据一打乱模型就把未来信息学进去了验证集分数虚高到新数据上立刻露馅。源码里使用的是严格的时间顺序切分train_size int(len(df) * 0.8) train df.iloc[:train_size].copy() test df.iloc[train_size:].copy() feature_cols [temp_min, humidity, pressure, wind_speed, month, dayofweek, season, temp_max_lag1, temp_max_lag2] X_train train[feature_cols] y_train train[temp_max] X_test test[feature_cols] y_test test[temp_max]时间顺序切分的核心是保证训练集所有日期都在测试集之前相当于拿过去预测未来。这里8:2的切分比例对这个样本量是合适的选择测试集大约覆盖最近20%的时间段足够看出模型在季节变化下的表现。feature_cols里没有放weather_desc因为天气状况直接进回归模型需要做one-hot编码而且它和温度之间是间接关系后面避坑章节会单独说。temp_min也作为特征放进去了这在业务上说得通清晨最低气温对午后最高气温有很强的参考性。但要留意如果预测目标是第二天的temp_max那temp_min必须也是预测目标不能拿当天的值去预测当天否则模型在实际部署时拿不到输入特征这在答辩时是老师最爱问的问题提前想好应对说法。3. 模型训练与评估线性回归和随机森林的实测差距有多大3.1 模型选型为什么先跑线性回归再跑随机森林天气回归任务最稳妥的组合是线性回归加随机森林。线性回归的作用是给出可解释基线湿度上升、气压下降时温度大概怎么变系数直观答辩时容易讲。随机森林则能捕捉非线性交叉影响比如湿度超过80%时体感降温效应会明显增强这种阈值效应线性模型学不到。源码里两个模型都训练最后用MAE、RMSE、R²对比谁的误差小谁做主模型。如果你只交一个模型我会建议把对比过程也保留在代码里因为课程设计的评分点往往包括“是否比较了不同算法”。3.2 训练与评估MAE、RMSE、R²怎么读from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score lr LinearRegression() lr.fit(X_train, y_train) pred_lr lr.predict(X_test) rf RandomForestRegressor(n_estimators200, max_depth8, random_state42) rf.fit(X_train, y_train) pred_rf rf.predict(X_test) for name, pred in [(linear, pred_lr), (random_forest, pred_rf)]: mae mean_absolute_error(y_test, pred) rmse mean_squared_error(y_test, pred) ** 0.5 r2 r2_score(y_test, pred) print(f{name}: MAE{mae:.2f}, RMSE{rmse:.2f}, R2{r2:.3f})这两个模型的差别通常会体现在极端温度上。RMSE对误差做了平方惩罚如果差两天预测误差都在5°C以上RMSE会比MAE大不少这是正常现象不是代码写错了。random_state42固定随机种子保证每次运行结果一致答辩现场重新跑一遍也不会出现分数跳变。n_estimators设为200对几千条样本已经足够继续加大到500只会增加训练时间精度提升有限。max_depth8是为了限制单棵树过深天气数据的有效信号不足以支撑深度很大的树。读指标的时候重点看MAE温度预测平均偏差在2°C以内算合格超过3°C就要回头检查特征或数据划分。3.3 参数调优TimeSeriesSplit与GridSearchCV组合如果只是手动改参数说服力不够。源码里用时间序列交叉验证做网格搜索这是比随机切分更严谨的做法from sklearn.model_selection import TimeSeriesSplit, GridSearchCV tscv TimeSeriesSplit(n_splits5) param_grid { n_estimators: [100, 200], max_depth: [6, 8, 10], min_samples_leaf: [2, 4] } gs GridSearchCV( RandomForestRegressor(random_state42), param_grid, cvtscv, scoringneg_mean_absolute_error, n_jobs-1 ) gs.fit(X_train, y_train) print(gs.best_params_)TimeSeriesSplit和普通KFold的区别在于每一折的训练集永远在当前验证集之前模拟的是滚动预测场景。n_splits5意味着验证集依次取最后20%、40%、60%、80%的数据。scoring选neg_mean_absolute_error因为天气预测更关心平均偏多少度而不是被个别极端天拉爆的MSE。整个网格只有12组参数组合乘上5折也就60次训练跑起来很快。如果换成十几层网格再加几百棵树时间成本会翻几十倍课设完全不值得。调完参后记得用gs.best_estimator_重新在全部训练数据上拟合一次再预测测试集最后输出的指标才是你写进报告的数。4. 常见问题排查五个把项目卡住的坑与具体解决方案4.1 数据阶段的坑随机切分与日期字符串坑一时间序列被随机切分R²虚高到不真实。现象用train_test_split后验证集R²到了0.96换成时间顺序切分R²掉到0.85以下答辩时被问一句“未来数据是不是进了训练集”就解释不清。原因shuffleTrue把相邻日期的样本打散气温序列自相关性强测试集里的每一天都能在训练集里找到几乎一样的“邻居”模型等于开卷考试。解决一律按时间顺序切分并加一句断言自检。assert test[date].min() train[date].max(), 测试集日期必须晚于训练集坑二日期字符串被当成特征特征列爆炸式增长。现象特征列从9列变成30多列训练时间翻倍。原因把date字符串原样交给pd.get_dummies每一天变成一列哑变量测试集一旦出现训练集没见过的日期还会直接报特征数量不一致的错误。解决原始date列只用来画坐标轴和切分数据不进模型进入模型的只有month、dayofweek、season这些派生数值特征。4.2 模型阶段的坑标签编码与滞后特征坑三天气状况文本做主标签编码模型学出伪规律。现象把weather_desc用LabelEncoder编成0、1、2、3后模型出现“天气编号越大、温度越高”的规律预测多云天气时系统性偏高。原因LabelEncoder给无顺序类别强加了数字顺序晴4、多云2这种排序纯属巧合线性回归会把数字当成连续大小来用。解决天气状况不参与回归主模型或者用one-hot编码后再进特征集。weather_encoded pd.get_dummies(df[weather_desc], prefixweather) df pd.concat([df, weather_encoded], axis1)坑四滞后特征dropna之后样本量骤减季节边界失真。现象原始数据800行两个滞后特征dropna后少了行冬季初期样本被切掉模型遇到寒潮就翻车。原因shift产生的NaN被直接丢弃。解决前两行沿用首条有效值或者用bfill填充如果决定保留dropna也要先确认样本量损失不超过整体5%。4.3 可视化阶段的坑中文乱码坑五图标题、坐标轴全是方块。现象plt.title写中文出图后显示成□□□□。原因Matplotlib默认字体没有中文字形。解决在绘图前强制指定中文字体和负数符号显示。import matplotlib matplotlib.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] matplotlib.rcParams[axes.unicode_minus] FalseMac环境可以换成PingFang SCWindows用SimHei基本都能覆盖。这行配置要放在所有plt绘图代码之前放在import matplotlib之后。如果用了seaborn也需要在设置rcParams之后再导入seaborn否则某些样式会覆盖字体设置乱码依然存在。5. 数据可视化折线图、热力图与交互面板怎么选5.1 Matplotlib静态图真实值与预测值的双线对比答辩展示里最核心的一张图是真实最高气温与预测值的对比折线。源码里用Matplotlib直接出图简洁且便于保存PNG插入报告import matplotlib.pyplot as plt plt.figure(figsize(14, 6)) plt.plot(test[date], y_test, label真实值, lw1.5, color#1f77b4) plt.plot(test[date], pred_rf, label随机森林预测值, lw1.5, alpha0.8, color#ff7f0e) plt.legend() plt.title(测试集最高气温预测对比) plt.ylabel(温度(°C)) plt.xticks(rotation45) plt.tight_layout() plt.savefig(forecast_plot.png, dpi300) plt.show()lw是线宽alpha是透明度pred_rf来自前面随机森林的训练结果。保存图的时候dpi300插入论文后放大也不会糊。x轴日期如果太密集可以用plt.xticks(rotation45)旋转不然刻度标签叠成一团。tight_layout会自动调整边距避免坐标轴标签被裁掉。出图后先看残差分布如果预测线整体比真实线低说明模型有系统性偏差如果只在极端温度处偏说明滞后特征对突变天气反应慢这是时间序列模型的正常现象不是bug。5.2 Seaborn热力图判断特征复共线性另一个值得放进报告的是相关性热力图。它能直观说明为什么某些特征可以留着、某些需要谨慎import seaborn as sns corr_cols [temp_max, temp_min, humidity, pressure, wind_speed, temp_max_lag1, temp_max_lag2, month] corr df[corr_cols].corr() plt.figure(figsize(10, 8)) sns.heatmap(corr, annotTrue, fmt.2f, cmapRdBu_r, squareTrue) plt.show()annotTrue在格子里显示相关系数数值fmt.2f保留两位小数cmapRdBu_r红蓝渐变squareTrue让格子呈正方形、视觉更整齐。热力图里temp_max和temp_min相关系数通常会在0.9以上说明两个特征高度共线如果模型用线性回归这种共线性会让系数不稳定解释起来很麻烦。滞后特征和当前温度的相关性则能验证“今天气温受昨天影响”的直觉。答辩时被问“为什么选这几个特征”拿这张图就能顶回去比空口解释有说服力。注意df必须是做完dropna之后的数据否则corr里会出现NaN导致热力图格子空白。5.3 Plotly交互式图表答辩演示阶段的加分项Matplotlib是静态图适合放进报告。如果现场演示Plotly的交互缩放体验会直观得多import plotly.graph_objects as go fig go.Figure() fig.add_trace(go.Scatter(xtest[date], yy_test, name真实值)) fig.add_trace(go.Scatter(xtest[date], ypred_rf, name预测值)) fig.update_layout( title最高气温预测对比交互版, hovermodex unified, templateplotly_white ) fig.show()hovermodex unified让鼠标悬停时同时显示当前日期的真实值和预测值视觉上更直观。templateplotly_white切换白底样式印到PPT里不突兀。页面缩放时拖拽局部波峰区域比静态图更容易解释“模型在冷空气来临时反应滞后”这类现象。交互图适合演示但不建议写进打印版报告评委没法在纸上缩放。源码包里如果只有Matplotlib版本自己补一个Plotly页面写进“改进与扩展”部分很容易成为答辩加分项。6. 进阶技巧把单次预测改成每日自动更新的小脚本前面所有流程跑通之后项目还能往前走一步。天气预测的价值在于持续更新而不是跑一次就结束。把数据读取、特征构造、模型训练、预测打包成一个函数再用系统计划任务每天自动执行这是这门课设计里性价比最高的进阶方向。def train_predict_latest(csv_pathdata/weather_data.csv): df pd.read_csv(csv_path) df[date] pd.to_datetime(df[date]) df[month] df[date].dt.month df[dayofweek] df[date].dt.dayofweek df[temp_max_lag1] df[temp_max].shift(1) df[temp_max_lag2] df[temp_max].shift(2) df df.dropna() feature_cols [temp_min, humidity, pressure, wind_speed, month, dayofweek, season, temp_max_lag1, temp_max_lag2] X df[feature_cols] y df[temp_max] model RandomForestRegressor(n_estimators200, max_depth8, random_state42) model.fit(X, y) last_row df.iloc[-1][feature_cols].values.reshape(1, -1) return model.predict(last_row)[0]这个函数每次读取最新CSV重建特征重新训练然后输出明天最高气温。Windows上用任务计划程序或者Linux服务器上写下cron表达式每天早晨8点执行一次把预测结果追加到一个新的CSV里。新增数据写入原文件前记得先用和历史数据相同的格式做日期解析否则第二天脚本直接卡在pd.to_datetime上。还有一个容易被忽略的问题如果新增记录的日期和预测目标之间缺了几天的数据滞后特征会取到错误的行需要在追加数据时先检查日期连续性。我从这套项目里学到的最大习惯是改完特征之后强制用时间顺序切分重新跑一遍记录MAE再用最近一周数据和预测结果做对比。偏差超过3°C就要回去查数据而不是继续调参因为特征选错的模型调多少轮也救不回来。有一次答辩前夜图省事直接跑了默认shuffle切分结果报告里的R²和现场新数据完全对不上当场翻车。从那以后我每次跑天气预测项目都会把时间顺序切分和残差目检强制走一遍希望帮到你。本文还有配套的精品资源点击获取
返回列表