ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python天气数据预测系统实战:从采集清洗到建模预测全流程

Python天气数据预测系统实战:从采集清洗到建模预测全流程 前一段时间有个做户外活动的朋友跟我吐槽说活动日期老是撞上天气突变平台上的天气预报又不准一场活动说取消就取消损失不小。我当时就在想与其眼巴巴等着别人给的预报不如自己抓数据、自己分析、自己做个小范围的气温预测模型。于是就有了这个“基于Python的天气数据分析预测系统”。说白了就是自己动手写一套从数据采集、清洗、分析到建模预测的完整流程最后用图表把未来几天的趋势画出来。这套东西特别适合刚学完Python基础、想找实战项目的同学也适合工作中需要做数据分析和简单预测的人比如活动策划、物流调度、农业种植、电力负荷估算这些场景。我先把整套项目的设计和核心代码逻辑拆开讲一遍再把我实际踩过的坑和排查思路整理出来希望能帮你少绕弯路。1. 项目整体设计与思路拆解1.1 先搞清楚预测系统到底要做什么很多人一上来就想搞深度学习、神经网络其实没必要。天气预测这事如果只做短期气温趋势传统机器学习模型完全够用而且训练快、可解释性强出了问题也容易定位。我这个系统核心就干四件事抓取历史天气数据、清洗数据、构造特征、训练模型并预测未来气温走势。目标很明确不要贪多先做一个能用的最小闭环。确定了目标之后就要考虑数据从哪来。常见的路子有三种爬第三方天气网站、调用免费天气API、找现成的历史数据集。我当时为了练手先用了requests库直接爬公开天气页面后来又换了免费的Open-Meteo API做数据源。爬虫的好处是灵活什么字段都能抓坏处是容易被反爬限制而且要考虑对方网站的结构变化。API的好处是数据格式规整、稳定通常有历史数据接口也有未来预报接口省掉不少解析功夫。如果你只是想做分析建模我建议直接用API别跟反爬较劲。1.2 方案选型为什么用Python这一套组合这个项目的技术栈不算复杂Python做主语言requests或者urllib做数据采集pandas做数据处理scikit-learn做模型训练matplotlib做可视化。有人会问为什么要用pandas而不是直接操作列表因为天气数据是典型的结构化表格数据有日期、最高温度、最低温度、天气现象、湿度、风速等字段pandas的DataFrame天生就是干这个的。筛选、分组、滞后字段生成、缺失值填充十行以内就能搞定如果用纯Python列表这些操作会让你写到怀疑人生。至于为什么选scikit-learn而不是自己手写模型或者上深度学习框架原因也很实际数据量就几百条到几千条用线性回归、随机森林这类经典模型已经能取得不错的效果。深度学习需要的数据量和调参成本太高模型解释性是负资产对一个小系统来说纯属杀鸡用牛刀。我后续会专门说模型的选择这里先不展开。1.3 系统架构和模块划分整个系统我拆成了四个模块采集模块负责把历史数据和未来预报数据拉下来清洗模块做格式规范、缺失值处理、异常值剔除特征工程模块负责把日期转换成星期、月份、滞后温度差等特征模型与输出模块负责训练、评估和画图。这样做的好处是每一块都可以独立调试爬虫挂了不影响建模清洗逻辑改了模型也能继续用。我见过不少新手把一个系统所有代码堆在一个脚本里一旦出了问题改一处崩一片排查起来特别痛苦。所以从项目一开始就要养成模块化的习惯。2. 环境准备与数据获取2.1 Python环境搭建和依赖安装这一步看似基础但很多人卡得莫名其妙。如果你电脑上装的是Python 3.8以上版本其实大部分库都能直接支持。我建议你用虚拟环境别把包一股脑装到全局环境里不然不同项目依赖冲突起来会让人崩溃。用venv或者conda都行我个人习惯用conda因为它在Windows下处理某些库的底层依赖更方便。装包的时候国内用户直接用pip install容易超时或者下载慢我建议配置一下国内镜像源。比如在命令行里执行pip install pandas scikit-learn matplotlib requests -i https://pypi.tuna.tsinghua.edu.cn/simple这里我踩过一个坑新电脑上直接pip install scikit-learn结果因为Python版本是3.12某几个旧版本的依赖编译不兼容报了一堆红字。后来换成了指定版本号比如scikit-learn1.3.2问题就解决了。所以装包的时候如果遇到编译错误优先检查Python版本和库版本是否匹配不要盲目升级到最新版。2.2 数据源选择API和爬虫怎么权衡我最初是用爬虫去抓天气网站的抓到的是HTML页面需要解析字段代码里充满了各种find和正则维护成本很高。后来换成Open-Meteo这个API无需密钥支持历史数据和未来预报返回JSON格式还允许指定经纬度、地区、时间范围和需要的气象变量。调用方式非常简单就是一个返回JSON的URLhttps://archive-api.open-meteo.com/v1/archive?latitude39.90longitude116.40start_date2023-01-01end_date2023-12-31dailytemperature_2m_max,temperature_2m_mintimezoneAsia%2FShanghai返回内容用requests.get()拿下来再调json()方法解析直接就能转成DataFrame。这样数据获取的时间从一个多小时压缩到几十秒稳定性也大大提升。如果你非要用爬虫我提醒一句一定要设置合理的请求头和访问频率不然很容易被对方拒绝访问严重的还可能被拉黑IP。2.3 采集模块的具体实现我用requests写了一个简单的封装函数只干一件事给定开始日期、结束日期、经纬度返回一个清洗好的DataFrame。核心代码大概是这样的import requests import pandas as pd def fetch_weather_history(latitude, longitude, start_date, end_date): url https://archive-api.open-meteo.com/v1/archive params { latitude: latitude, longitude: longitude, start_date: start_date, end_date: end_date, daily: temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max, timezone: Asia/Shanghai } resp requests.get(url, paramsparams, timeout30) resp.raise_for_status() data resp.json()[daily] df pd.DataFrame(data) df[date] pd.to_datetime(df[time]) df df.drop(columns[time]) return df这一步有个小细节容易被忽略不管API返回的日期字段是字符串还是时间戳都要显式转换成pandas的Datetime类型不然后面按日期分组、画图时横轴排序都是乱的。我自己刚开始就是没转换结果画出来的趋势线跟波浪线一样上下乱跳找了好久才定位到是日期类型的问题。3. 数据清洗与特征处理3.1 用DataFrame摸清数据家底数据拿到手之后第一步不是急着建模而是先做全面检查。我习惯用df.head()、df.info()、df.describe()三件套看数据长什么样。df.info()能看出每列有没有缺失值、数据类型对不对df.describe()能看到温度、降水、风速这些数值列的分布比如最大值是不是明显离谱。这一步能帮你提前发现数据问题省得建模出来效果莫名其妙。比如有一次我拉到的数据里出现了-999这样的填充值明显是API缺失值的占位符。如果不清理模型就会把-999当作真实温度样本预测结果直接跑偏。这种情况就需要用np.where把-999替换成NaN然后再统一做缺失值处理。3.2 缺失值和异常值处理缺失值处理的原则是不能不做但也不能乱做。对于温度这种连续变量我一般用前后几天的均值填充或者直接用ffill方法向上填充因为温度在短期内变化是连续的前一天和后一天的温度往往比较接近。如果你用整个列的平均值填充遇到季节变化大的情况反而会把数据搞失真。代码很简单df[temperature_2m_max] df[temperature_2m_max].replace(-999, pd.NA) df[temperature_2m_max] df[temperature_2m_max].ffill().bfill()异常值的判断我倾向于先用可视化的方式看分布比如画个箱线图如果有温度在夏天出现-30度这种明显不合理的点就直接剔除。判断标准可以结合实际地区的气候范围不用搞太复杂的算法毕竟天气数据质量整体还是比较靠谱的。3.3 构造特征让模型有更多线索可挖很多新手忽略特征工程直接把原始日期和温度丢给模型效果自然很一般。我做的第一版模型也是这么干的预测出来的结果基本是前一天数值的平移完全体现不出变化趋势。后来我在特征里加入了星期、月份、季节、前一天的温差甚至几日滑动平均效果立刻不一样了。构造特征的核心思路是让模型能识别出周期性规律。比如月份这个特征对模型来说是一月份还是六月份温差差异非常明显。星期特征则能捕捉周中和周末的细微差异。代码上就是几行pandas的操作df[month] df[date].dt.month df[day_of_week] df[date].dt.dayofweek df[temp_diff] df[temperature_2m_max] - df[temperature_2m_min] df[prev_max] df[temperature_2m_max].shift(1) df[rolling_avg_3] df[temperature_2m_max].rolling(3).mean()这里注意shift(1)生成的prev_max在数据第一行会变成NaN所以用完滞后特征之后需要再清洗一次缺失值。滚动均值是很好的平滑特征能把噪声去掉让模型学到的趋势更稳健。4. 模型构建与预测评估4.1 划分数据集别让未来数据偷看答案建模前最重要的一件事就是划分训练集和测试集但天气数据跟普通表格数据不一样它是有时间顺序的不能随便随机打乱。如果随机切分模型会看到未来的数据去预测过去的数据准确率虚高一到真正预测未来时就露馅。这个叫数据泄漏是时序预测最常见的坑。正确做法是按照时间顺序划分比如前80%的时间段做训练后20%做验证。我常这么写train_size int(len(df) * 0.8) train_df df.iloc[:train_size] test_df df.iloc[train_size:]还有一种更稳的方案是时序交叉验证比如TimeSeriesSplit不过对这个小项目来说简单的时间切分已经够用了。特征列和标签列要分开定义标签是你要预测的气温值特征是你构造的那些维度。注意把date列从特征里去掉模型不需要直接用日期字符串日期只是用来对齐预测结果的。4.2 多模型对比不要只认准一个模型我一开始只用线性回归结果发现效果一般毕竟温度和时间、其他特征之间的关系不完全是线性的。后来我同时跑了决策树、随机森林和支持向量回归用同样的训练集和测试集对比发现随机森林在这份数据上表现最好线性回归次之决策树单树容易过拟合。具体实现用scikit-learn非常方便from sklearn.ensemble import RandomForestRegressor from sklearn.linear_model import LinearRegression from sklearn.svm import SVR models { linear: LinearRegression(), rf: RandomForestRegressor(n_estimators100, random_state42), svr: SVR(kernelrbf) } for name, model in models.items(): model.fit(X_train, y_train) score model.score(X_test, y_test) print(f{name}: {score:.3f})scikit-learn的score()函数返回的是R平方分数越接近1越好。不要只看这一个指标我一般还看均方误差和平均绝对误差因为R方在样本量小的时候容易欺骗人。平均绝对误差更直观比如误差两度就是两度。4.3 评估指标与简单调参思路评价模型好坏不要只盯着测试集还得看训练集和测试集差距。如果训练集上表现极好测试集上却一塌糊涂那就是过拟合。我调参的第一步不是网格搜索而是先看模型对数据的拟合状态。比如决策树的高度限制一下随机森林的树数量调整一下往往就能改善。调参可以用GridSearchCV但数据量小的时候网格搜索很容易变成抖机灵调出来的参数换了数据就不行。我更建议先做特征筛选去掉无关特征再去网格搜索范围设置小一点比如随机森林只看50、100、150三档。简单来说模型复杂度要跟数据量匹配别拿几百条数据硬套一个高复杂度模型。5. 可视化与结果呈现5.1 matplotlib画预测对比图模型训练完最终要能直观看出预测和实际的对比。我用matplotlib画了两条折线一条是真实温度一条是预测温度。最基础画法其实很简单import matplotlib.pyplot as plt plt.figure(figsize(12, 5)) plt.plot(test_df[date], y_test, labelactual, linewidth2) plt.plot(test_df[date], y_pred, labelpredicted, linestyle--) plt.xlabel(date) plt.ylabel(temperature) plt.legend() plt.title(Temperature Forecast vs Actual) plt.tight_layout() plt.show()这里有个非常常见的问题就是横坐标日期太多、太密集标签叠在一起变成一片黑根本看不清。解决办法有两个一是设置刻度间隔只显示一部分日期二是旋转刻度标签。我通常两个一起用效果最好import matplotlib.dates as mdates plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval7)) plt.xticks(rotation45)图不能光给自己看如果给不懂技术的朋友看最好把中文字体处理好不然默认字体显示不了中文全是方块。Windows下可以用simheiLinux下可能要装中文字体这一步记得检查。5.2 导出结果到Excel或CSV预测结果不能只停留在内存里我用to_csv()把预测值和真实值存到文件里方便后续查看或直接发给相关同事。代码非常简单result pd.DataFrame({ date: test_df[date], actual_max_temp: y_test, predicted_max_temp: y_pred }) result.to_csv(weather_forecast_result.csv, indexFalse, encodingutf-8-sig)这里有个小坑Windows下的Excel打开CSV文件如果不加utf-8-sig参数中文文件名和内容会乱码。这个编码参数如果你不知道十有八九会踩到。5.3 用图表发现模型问题画图还有一个重要用途是帮助调参。我跑完模型后如果发现预测曲线整体比实际偏低往往是因为模型学到的平均温度偏低这时候考虑添加截距项或者检查是否有特征把平均值带偏了。如果预测曲线的波动比实际小很多则说明模型平滑过头了可以考虑增加滞后特征或者改用对突变更敏感的模型。多画几次图你就能慢慢培养出看线识病的感觉。6. 常见问题与排查技巧实录6.1 环境配置阶段的高频坑这个项目最常见的安装问题集中在numpy和scikit-learn的版本冲突上尤其在Python 3.12以上版本某些库还没完全适配。遇到这类问题我给你的建议是第一看Python版本第二看库的官方文档支持的版本范围。装库时不要一股脑装最新版优先装已经验证过兼容的版本组合。还有一个常见问题就是pip下载超时。除了换镜像源还可以给pip加超时时间参数比如--timeout60。跑代码之前在命令行先执行一下import pandas和import sklearn能提前发现环境问题别到时候把报错跟业务逻辑混在一起排查起来头大。6.2 数据与模型层面的经典错误我在开发过程中整理了一个问题速查表每次项目卡住时都会对照一下效率特别高现象可能原因处理办法预测曲线是一条水平线特征与标签关系没学出来或特征全被丢弃检查特征列是否包含日期等无效字段重新做特征工程训练集评分很高测试集评分很低过拟合模型太复杂或数据量太少减少特征维度限制模型深度改用随机森林并调n_estimators数据导入后全是NaNAPI返回字段名变了或者JSON解析出问题打印原始JSON确认字段名用pd.json_normalize转成DataFrame模型预测值整体偏高或偏低数据存在季节性偏差或训练集和测试集分布差异大检查时间段是否跨季节尽量让训练集包含完整周期matplotlib图表中文乱码默认字体不支持中文设置中文字体或确认系统安装过中文字体文件日期横坐标重叠看不清数据量大、刻度过密用DayLocator设置间隔再用rotation调整角度6.3 排查流程与实操心得当你拿到一个报错不要急着搜解决方案先做三件事读最后一行报错信息看是哪个模块抛出的看报错有没有提示具体的文件名和行号直接跳过去打印出错位置附近的关键变量的shape和值。很多所谓的疑难杂症其实都是DataFrame列名拼错了或者多了一个空格导致列不存在。我个人比较推荐在代码里多写assert语句比如确认df没有空值、确认训练集和测试集长度符合预期。这些断言能在早期发现问题而不是等到模型跑完才看到结果异常。项目到了后期数据清洗的函数可能比自己预想的复杂这时候写单元测试能帮你省下大把时间。别把单元测试当成负担对一个小项目来说几个简单的assert就够用了。从实际效果来看这套系统的预测准确度在短期气温趋势上能控制在误差两度左右应对日常活动安排、出行参考已经足够。如果你后续想继续扩展建议往这几个方向走一是接入实时预报接口做滚动预测二是用Prophet或LightGBM这类更强的时间序列工具替换现在的模型三是把预测结果做进一个简单的Flask网页服务让朋友输入城市名就能看到预测曲线。我这个项目的代码整体没有特别华丽的技巧但每一步都是踏踏实实踩出来的希望这份复盘能帮你节省一些自己摸索的时间。
返回列表