资讯动态

基于Python的天气数据分析与预测系统设计与实现

发布时间:2026/10/6 3:58:28 来源:尧图企业网站定制
最近折腾了一个有意思的小项目基于Python的天气数据分析预测系统。事情起因其实挺简单我所在的位置通常和城市中心天气预报站点有十几公里的距离手机天气App上写“多云25度”的时候我这边可能是阴天还带阵风。商业预报做不到每个人的具体位置都精准那么能不能自己用Python搭一套基于历史观测数据的小型预测系统针对特定地点做未来24到48小时的温度、降水概率分析顺着这个想法我把数据获取、清洗、特征构造、模型训练到最后可视化验证的完整链路都走了一遍踩了不少坑也沉淀了不少经验。这篇内容就围绕这个系统的设计与实现展开适合刚学完Python基础、想找一个完整数据分析项目练手的人也适合想给自己做一个“私人天气预报”的爱好者参考。1. 项目从哪里开始需求拆解和整体方案动手之前我先把问题拆了一遍。天气预测这件事市面上有两条技术路线一条是大型机构的数值预报靠着超级计算机求解大气运动方程我们个人基本碰不了另一条是纯数据驱动的统计预测历史观测数据喂给模型让模型自己找规律。个人项目要跑得动、要能落地明显应该走第二条路。我的目标定得很具体不追求精确预测每一个时刻的温度而是预测未来24小时内的最高温度、最低温度以及降水概率区间。这三个指标足够日常决策使用。整体架构分四层数据层定时抓取天气观测数据、存储层本地落库成结构化数据、分析层清洗、特征工程与模型训练、应用层预测结果可视化和导出。方案要克制工具链能省则省。我最终选了requests做数据请求、pandas做数据处理、scikit-learn做模型训练、matplotlib做可视化最后用APScheduler加了个定时任务每天自动跑一次。没有上Spark、没有上Hadoop因为数据量根本到不了那个级别单机杀鸡用牛刀也平白增加维护负担。顺着这个思路先把数据源搞定。2. 气象数据从哪来数据源选型和抓取策略2.1 公开API与爬虫的取舍做天气分析的第一道坎就是数据。市面上的天气数据来源无非三类官方气象部门开放的接口、商业天气API平台、自己爬网页数据。直接爬网页维护成本太高网页改版反爬策略一变项目就废了除非只是做一次性爬虫练习否则不推荐作为长期数据来源。我当时把几个主流数据源对比了一遍数据源数据精度获取难度更新频率适用场景国家气象数据中心开放平台小时级/日级需要申请接口不统一延迟较大长期历史数据研究和风天气API分钟级/小时级注册即用免费额度有限实时国内地点预报与实况OpenWeatherMap小时级/日级注册即用免费额度有限实时全球城市英文环境爬虫方式抓取天气网站取决于源站简单但脆弱实时短期实验不推荐长期用我最终用的是开放API为主、手动备份为辅的模式。每天定时抓取两个东西一是当前实况数据温度、湿度、气压、风速、天气现象二是未来三天的预报数据。预报数据拿回来有两个用途短期来看可以作为对照参考长期来看等目标时间过去之后这份“预报值”和“实况值”就能组成一条训练样本让模型去学“当观测条件是这样时实况最终变成了那样”。2.2 数据抓取模块的写法代码结构非常简单核心就是构造请求、解析JSON、附加时间戳落库。需要注意的是绝大多数天气API返回的温度在某些区域体系下可能是华氏度务必先确认单位再入库不然后面特征构建全是错的。import requests import pandas as pd from datetime import datetime # 以OpenWeatherMap为例的请求示例 API_KEY your_api_key LAT, LON 39.9042, 116.4074 # 替换成你的目标经纬度 url fhttps://api.openweathermap.org/data/2.5/onecall params { lat: LAT, lon: LON, exclude: minutely,hourly,alerts, units: metric, appid: API_KEY } resp requests.get(url, paramsparams) data resp.json() current data[current] daily_forecast data[daily][0] record { record_time: datetime.now(), temp_current: current[temp], humidity: current[humidity], pressure: current[pressure], wind_speed: current[wind_speed], weather_desc: current[weather][0][description], forecast_max_temp: daily_forecast[temp][max], forecast_min_temp: daily_forecast[temp][min], forecast_pop: daily_forecast.get(pop, 0) } df pd.DataFrame([record]) df.to_csv(weather_records.csv, modea, headerFalse, indexFalse)这里有一个容易忽略的细节天气API免费版通常限制调用次数比如每分钟60次、每天1000次。对于个人项目一天拉一次根本跑不满但如果你手动测试时反复刷新很容易触发限流。稳妥的做法是每次请求之间加上sleep并且把抓到的原始数据完整保留一份万一后续要换特征、改目标变量不用重新拉历史数据。2.3 数据落库CSV够用但SQLite更安心初期我只用了CSV追加写入简单直接。但跑了几天后发现问题重复跑脚本导致重复记录、日期字段格式不统一、同时读写容易丢数据。后来我改成SQLite建表写数据查询和去重都方便多了。对于这种规模的项目SQLite比MySQL更合适零安装、单文件、Python内置sqlite3模块直接操作。import sqlite3 conn sqlite3.connect(weather.db) c conn.cursor() c.execute( CREATE TABLE IF NOT EXISTS weather_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, record_time TEXT, temp_current REAL, humidity INTEGER, pressure REAL, wind_speed REAL, weather_desc TEXT, forecast_max_temp REAL, forecast_min_temp REAL, forecast_pop REAL, UNIQUE(record_time) ) ) conn.commit()建表的时候我加了一个UNIQUE约束在record_time上插入用INSERT OR IGNORE天然去重。这个习惯值得养成——凡是带时间戳的数据追加入库都先设计好唯一键不然“重复抓取”会成为你排查问题时的噩梦。3. 数据清洗和特征工程真正决定预测精度的环节很多初学者拿到数据就急着调模型其实整个项目里数据清洗和特征工程花的时间占比最大也最影响最终效果。天气数据表面干净但细看全是坑传感器断线导致的缺测、极端值毛刺、时区不一致、前后两条记录时间间隔不均等。3.1 缺失值与异常值处理对于时间序列数据缺失值不能用全局均值填充那样会把时间上的渐变趋势抹掉正确做法是前后插值。比如温度是连续变量用线性插值就够了天气现象是分类变量缺失时用前一条记录填充forward fill更合理因为天气状态在短时间内一般不会突变。异常值的识别比填充更重要。我遇到过一条记录温度直接从25度跳到40度再跳回25度的情况原因是设备故障或临时过热。针对这种突变我计算了相邻两小时温度变化率凡是变化率超过5度/小时的样本单独标记出来在训练集里剔除。这个思路其实就是最简单的“阈值型异常检测”对气象数据来说足够了。3.2 目标变量定义和特征列表模型要预测的目标我定义为某个回归点之后24小时内的实况最高温度。这里的“某个回归点”是建模范式里最需要注意的地方——也就是你生成一条训练样本的时间点。特征只能用这个时间点之前已知的信息目标是这个时间点之后未知的结果。如果时间边界没划清楚数据泄漏就会让模型在验证时“预测很准”、上线后全面拉胯。我用到的特征分成四类特征类别具体特征说明实时观测当前温度、湿度、气压、风速、天气现象对应当前大气状态时序滞后项前1h、前3h、前6h、前12h的温度变化趋势捕捉短时演变趋势周期性特征小时、星期几、一年中的第几天反映昼夜和季节周期统计特征过去24h平均温度、最高温度、最低温度反映当日整体冷暖背景周期特征很重要很多初学者会忽略。温度有非常明显的日周期和年周期规律直接丢一个“小时”整数给模型模型会认为12和13是两个无关的数值但本质上23点和0点都是深夜、非常接近。把小时、天数转成sin/cos组合才能让模型理解周期性。3.3 时序数据切分为什么不能随机打乱这是整个项目中最容易翻车的步骤。常规机器学习流程里训练集和测试集是随机切分的但时序数据一旦随机打乱就完蛋了——模型偷偷看到了未来的数据验证集上的分数会虚假地高。正确做法是严格按时间顺序切分比如前80%的时间段作为训练集后20%作为测试集。更严谨一点可以用时间序列交叉验证也就是滑动窗口式地训练多次每次用更晚的数据做验证。当时我用的是手动实现训练集取连续365天数据验证集取紧接其后的30天数据绝不交叉。边界处我留了一个空隙避免滞后特征跨越时间边界造成泄漏。4. 模型选型过程从线性基线到非线性模型的梯度4.1 先从最简单的基线开始建模的第一步不是直接上LightGBM而是先构造一个朴素基线。对天气预测来说最经典的基线就是“持续性预测”——明天的最高气温等于今天的最高气温。听起来荒谬但实际预测效果意外地好因为在没有强冷空气或锋面过境的稳定天气条件下温度日际变化本来就很小。如果你的模型连持续性基线都打不过那说明特征或模型设计有问题没必要继续优化。我同时跑了两个基线一个是用“今天最高温度”直接作为“明天最高温度”预测值的持续性基线一个是简单的线性回归。线性回归在特征工程做扎实之后效果并不差而且可解释性极强可以清楚看到每个特征对温度的贡献方向。比如湿度系数为负代表湿度越大、最高温越倾向于偏低这在梅雨季节是符合直觉的。4.2 为什么最终选了梯度提升树线性模型加了一些交互特征之后我开始试常见的非线性模型。LightGBM是我最后固定下来的选择对比结果如下模型测试集MAE温度℃训练时间说明持续性基线2.310秒每天预测值等于当天值线性回归1.87秒级特征标准化后效果稳定随机森林1.76秒级比线性回归略有提升LightGBM1.62秒级相对最优调参空间大LSTM2.20分钟级数据量不够反而不如树模型看到这个结果我挺意外的LSTM在这个问题上输给了LightGBM。原因不复杂LSTM需要大量序列数据来学习长期依赖而我手上只有几百天的日频样本序列太短、噪声太大强行上深度学习只会过拟合。这也给后来的项目定了规矩模型复杂度要和数据量匹配不是为了酷炫而上复杂模型。4.3 训练代码和参数心得训练部分代码很常规但有两个细节值得拿出来说。第一是特征标准化线性模型必须做树模型做不做影响不大但统一做一遍省心第二是目标值如果有明显的季节性趋势可以按月份拆分训练让每个月的模型只学当月的温度规律这个技巧效果提升显著。from lightgbm import LGBMRegressor from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline feature_cols [temp_current, humidity, pressure, wind_speed, temp_trend_1h, temp_trend_3h, temp_trend_6h, hour_sin, hour_cos, day_sin, day_cos, avg_temp_24h, max_temp_24h, min_temp_24h] X df[feature_cols] y df[target_max_temp] model Pipeline([ (scaler, StandardScaler()), (lgbm, LGBMRegressor( n_estimators300, learning_rate0.05, max_depth5, num_leaves31, subsample0.8, colsample_bytree0.8, random_state42 )) ]) model.fit(X_train, y_train) y_pred model.predict(X_test) print(fTest MAE: {mean_absolute_error(y_test, y_pred):.2f} °C)LightGBM的调参核心是max_depth和num_leaves要一起控制否则很容易过拟合。我的经验是先用默认参数训练一遍var(X_test)看看如果训练集MAE远小于测试集说明过拟合再降深度和叶节点数。天气预测场景不需要追求极高精度MAE在1.5-1.8°C之间已经足够日常参考了。5. 预测结果评估如何判断模型真的可用模型训练完不能只看一个MAE就收工这个数字会骗人。我当时踩过最大的坑是整体MAE看着还行但一点开分月统计夏季和冬季误差远大于春秋季雨天更是重灾区。5.1 误差分层分析我把测试集按月份、按天气现象、按温度区间分了组分别计算MAE发现几个规律温度越高的日子误差越大。因为高温天气多伴随午后热对流温度波动剧烈预测难度天然更高。雨天误差比晴天大约高20%-30%原因类似云量和降水带来的辐射平衡变化很难从历史数据中精确还原。夜间最低温度的预测误差比白天最高温度要低因为夜间变量少云量影响相对可控。通过这些分组分析我明确了一个认知单点MAE是“平均之下的幸存者偏差”分组误差分布才是模型改进的指挥棒。如果你的模型在某个细分场景下误差明显偏大优先补那个场景的特征而不是盲目调参。5.2 可视化诊断三张图可视化不是给汇报用的是用来发现问题的。我每次迭代模型后必画三张图预测值与真实值的散点图、残差分布图、误差随时间的变化图。散点图能看出模型是否系统性偏低或偏高。如果散点在低温端偏上、高温端偏下说明模型有“回归均值”倾向——预测结果偏向历史平均极端温度被拉平。残差图如果呈现漏斗形说明方差不稳定小时段和稳定时段混在一起了。画图代码很简单import matplotlib.pyplot as plt plt.figure(figsize(10, 5)) plt.scatter(y_test, y_pred, alpha0.5) plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], r--) plt.xlabel(真实最高温度) plt.ylabel(预测最高温度) plt.title(预测值与真实值对比) plt.show() residual y_test - y_pred plt.figure(figsize(10, 4)) plt.plot(residual) plt.axhline(0, colorr, linestyle--) plt.xlabel(样本序号) plt.ylabel(误差) plt.show()如果散点图显示模型系统性偏低可以在最终预测上加上一个校正量但这个校正量必须通过验证集计算不能是肉眼估计的。5.3 降水概率怎么评估温度是回归问题降水概率则是分类或概率预测问题。我这里用的方法很朴素按历史天气现象文本是否包含“雨”字作为真实标签模型输出一个0到1的概率值再画ROC曲线算AUC。AUC能到0.85以上对于个人项目就算不错了。因为降水概率本质上是一个极不平衡问题晴天样本远多于雨天样本所以看准确率没有意义必须看AUC或看不同概率阈值下的命中率。6. 部署运行和长期维护中的实际问题6.1 定时任务和数据连续性模型训练好之后真正让它每天自动跑起来靠的是APScheduler或者操作系统的定时任务。我最终选择了服务器上的cron每天清晨6点拉一次数据、重训练一次模型、生成当天预报。为什么每天重训练而不是训练一次一直用因为天气的季节特性明显模型需要持续看到最新数据才能跟上季节转换比如入冬后第一次寒潮来临如果模型还是用上个月的数据训练预测就会明显偏高。# 每天6点执行预测 0 6 * * * cd /path/to/project python predict.py logs/predict.log 21重训练的频率不能太高对个人项目来说一天一次正好数据量也会积少成多。6.2 时间戳时区问题这是我在项目中期才发现的一个大坑。天气API返回的时间多数是UTC时间我本地存储时如果直接用北京时间会导致某些特征错位。当时我在拉历史数据时把UTC时间直接存成了字符串等到构造滞后特征时才发现“前6小时温度”这个特征取到的其实是同一时刻的乱序数据。后来我统一在入数据库之前把时间全部转成北京时间并加后缀特征构造时全部以解析后的datetime对象为准彻底解决了这个问题。这个坑影响很大因为它不是报错的只是让你预测悄悄变差。如果你也做类似项目建议第一步就确认所有时间字段的时区并统一转换成目标时区的datetime对象。6.3 极端天气预测的系统性偏差还有一个很难根除的问题模型在极端高温和极端低温出现时总是预测得不够极端。这是我前面提到的“回归均值”现象。统计学上这是正常的因为训练数据本身就围绕平均分布极端值样本量少、模型没有足够证据去预测极端。如果要改善可以做分位数回归让模型输出预测区间的上下边界而不是单一数值。我当时没继续深挖但对“预测区间”这个概念印象很深——给决策用的预测单点值永远不如区间实用。7. 最后想说的几条实在话这个项目从开始萌生想法到跑通全流程前后经历了几轮迭代。我最深的体会是做数据分析项目最大的乐趣不是模型准确率从1.9降到1.62的那一刻而是把一条完整链路搭建起来、让它每天自动运行、并能够稳定输出可读结果的那种掌控感。纯Python、requests、pandas、LightGBM这些工具单个拎出来都不难难的是如何把它们串成一个能长期运行的系统。如果你也想做这类项目我的建议是把“预测准不准”这个执念先放下集中精力把数据记录、清洗、特征构造这三个环节做得越扎实越好。基础打牢之后哪怕你用的是最简单的线性回归也能得到一个比大多数天气预报App在特定点位上更符合你实际体感的参考结论。天气数据只是起点同样的方法换成股票价格、电力负荷、空气质量逻辑完全一样——把历史数据变成结构化特征再让模型挖掘规律这件事本身就值得做一遍。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑