news 2026/9/26 6:47:25

气象数据分析大作业指南:Python数据清洗到可视化全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
气象数据分析大作业指南:Python数据清洗到可视化全流程

简介:这是一份基于Python的气象数据分析与可视化项目源码,面向需要完成毕业设计、课程设计或期末大作业的学习者,尤其适合有Python基础、想借鉴高分项目快速搭建完整系统的新手,可直接覆盖气象数据获取、分析与展示等核心需求。压缩包共90个文件,以47个Python脚本为核心,包含数据处理、接口与爬虫逻辑;搭配8个Vue前端页面、8个JavaScript文件、JSON配置及图片样式等,整体包体仅604KB,轻量易部署。项目按功能拆分为前端展示、后端服务、Spark数据处理与Scrapy爬虫采集等模块,覆盖了气象数据从获取、清洗、分析到可视化展示的完整链路,代码含有详细注释,便于读懂和二次开发。目前已有310人浏览学习,作为高分大作业项目,能够帮助读者清晰理解项目结构,并迁移应用到自己的课题或实战练习中。

1. 气象数据分析大作业:为什么用Python做,以及它到底在做什么

说到气象数据分析大作业,很多人第一反应是“又要画温度折线图了”。但真正拿到高分的关键,不在于你画了多少张图,而在于你用什么方法处理了哪些数据,回答了什么气象问题。基于Python进行气象数据分析与可视化,本质上是一条从数据获取、清洗、统计建模到可视化表达完整走通的链路,它的价值在于:气象数据量大、字段丰富、规律明确,非常适合展示数据处理能力和图表审美,也正好踩中数据分析落地项目的常见考核点。

这个方案适合三类人:拿到课程大作业要求但不知道从哪下手的本科生、打算用气象数据练手数据分析项目的新手、以及想把可视化作品塞进作品集找工作的转行者。你不需要会深度学习,只要掌握Python基础、pandas和matplotlib,就能做出一个体面且经得起追问的分析项目。下面我按自己做这类项目时的顺序,把从数据到成图的整条路拆开讲,包括每步的参数设计和最容易让人翻车的坑。

2. 数据从哪来、怎么清洗:气象数据获取与预处理的三件套

2.1 首选数据源:国家气象科学数据中心与本地CSV的取舍

做气象数据分析大作业,第一步永远不是写代码,而是想清楚拿什么数据。常见做法有三种:一是直接下载中国气象数据网或国家气象科学数据中心公开的站点日值数据集,二是用爬虫抓天气后报之类的历史天气网站,三是用库里的模拟数据或老师提供的Excel。我一般优先推荐第一种,因为原始气象数据是带站点编号和缺测标记的,更能体现你的数据清洗能力;而爬虫虽然能拿到近十年的数据,但涉及反爬和字段解析,花的时间可能比写分析还多,对大作业性价比不高。

下面这段代码演示最常见的读取方式:用pandas读取一份站点日值数据,顺便把日期列解析成标准格式,并看一眼数据的前几行和缺失情况。

import pandas as pd # 读取原始CSV,指定编码和分隔符 df = pd.read_csv( "weather_station_daily.csv", encoding="utf-8", sep=",", parse_dates=["date"], # 把date列解析为datetime类型 na_values=["999999", "NaN", ""] # 把缺测值和异常值统一转成NaN ) print(df.shape) print(df.head()) print(df.isnull().sum())

这段代码的核心是na_values参数。气象数据里缺测通常不是空单元格,而是类似“999999”“32744”这类特殊值,如果不提前声明,这些值会被当成真实数据参与统计,画出来的图会出现一坨诡异的尖峰。parse_dates则保证日期可以参与时间序列的切片和重采样,否则按字符串排序会出现“2月”排在“10月”后面这种荒唐结果。我通常还会顺手打印df.dtypes,确认温度、降水量是不是数值类型,如果读出来是object,就说明原始文件里混进了脏字符,需要进一步处理。

2.2 清洗与插补:缺测值不只是删掉那么简单

气象站数据的缺失率通常在1%~5%之间,直接删行会让时间序列出现空洞,后续做逐日滑动平均时会断掉。更稳妥的办法是分别处理:如果缺失比例低于1%,可以用前后均值插补;缺得太多,则要判断是不是站点迁移或设备故障造成的系统性缺失,那种情况整段删掉比硬补更诚实。

下面是一个常用的插补与异常值处理片段:

# 对温度列进行线性插值,并过滤超出物理阈值的记录 df["temp"] = df["temp"].interpolate(method="linear", limit_direction="both") # 设定合理的气温范围,比如 -40℃ 到 45℃,超出则标记并剔除 valid_range = (-40, 45) df.loc[df["temp"] < valid_range[0], "temp"] = None df.loc[df["temp"] > valid_range[1], "temp"] = None df["temp"] = df["temp"].interpolate(method="linear")

interpolate(method="linear")会自动利用缺失点前后的值补一条直线,limit_direction="both"表示序列开头和结尾的缺失也能补上,这对连续缺测两天以内的数据效果很好。物理阈值过滤要按你选的城市调整,像北方的冬季低温、南方盛夏高温,范围设得太死会把真实极值也删掉。我的习惯是先画一版原始数据的直方图,看到明显离群点再设阈值,而不是一上来就套全球气温范围。

2.3 重采样与日期特征:让数据从“每天三条”变成“每天一条”

原始日值数据里同一站点一天可能会有多次观测记录,做分析之前必须先按天聚合成一行。pandas的groupby配上resample是标准做法,但要注意时区和日期索引问题。我常用下面的代码把数据规整成逐日序列,同时扩展出年份、月份、季节等辅助列:

df["date"] = pd.to_datetime(df["date"]) df = df.set_index("date").sort_index() # 按天聚合:温度取均值,降水量取累计值 daily = df.groupby(df.index.date).agg( temp_mean=("temp", "mean"), temp_max=("temp", "max"), temp_min=("temp", "min"), precip_sum=("precip", "sum"), wind_mean=("wind", "mean") ) # 补上月份、季节、年份字段,为后续分组统计做准备 daily["month"] = daily.index.month daily["season"] = daily["month"].apply( lambda m: "DJF" if m in [12, 1, 2] else "MAM" if m in [3, 4, 5] else "JJA" if m in [6, 7, 8] else "SON" ) daily["year"] = daily.index.year

这段代码里groupby(df.index.date)是一个容易被忽略的细节:如果索引是带时分秒的datetime,直接用resample("D")虽然也能聚合,但遇到跨时区或夏令时数据时会傻掉。用.date先归一化到日期,再groupby,逻辑更干净。聚合策略上,温度和风速用mean,降水和极端温度用max/sum,这个你要根据自己的问题来定,而不是所有列都取均值。

写完这一步,你手头就有一个干净的逐日气象数据集,后续画趋势图、算气候态、跑相关性都基于它。很多新手在清洗环节偷懒,结果后面画出来图里总有几个孤立的离群点,答辩时被老师一指就哑火。千万别跳过这节。

3. 分析什么才有高分:统计量、趋势与相关性分析的设计

3.1 气候平均态与极值统计:让别人看到你懂“气候”不只是“天气”

如果只画全年温度曲线,那叫天气展示,不叫气候分析。高分作业通常要求你回答“某地近若干年的平均气温是多少、最热月是哪月、极端降水事件有没有增多”。这就需要做气候态统计,也就是对多年同期数据求平均,并和单个年份对比。

下面这段代码计算每个月的多年平均气温,并标出历史最热月:

monthly_clim = daily.groupby("month").agg( temp_mean=("temp_mean", "mean"), temp_max_hist=("temp_max", "max"), precip_sum_hist=("precip_sum", "sum") ).reset_index() # 找出最热月和最大降水月 hottest_month = monthly_clim.loc[monthly_clim["temp_mean"].idxmax()] wettest_month = monthly_clim.loc[monthly_clim["precip_sum_hist"].idxmax()] print("最热月:", int(hottest_month["month"]), ",月均温:", round(hottest_month["temp_mean"], 1), "℃") print("最湿月:", int(wettest_month["month"]), ",累计雨量:", round(wettest_month["precip_sum_hist"], 1), "mm")

注意我用的是temp_mean(日均温)再求月平均,而不是直接把日最高温求平均。两种口径会差出至少2℃,老师问到“你的温度是怎么定义的”时,你要说得清。idxmax()用来返回索引,比max()更实用,因为它同时告诉你“哪个月”这个标签。如果你想看单年同比,就用daily[daily["year"] == 2023].groupby("month")["temp_mean"].mean()和气候态对比,这样就能写“2023年夏季平均气温较常年偏高1.2℃”这类有结论的话。

3.2 趋势检验:别用一眼看出来的斜率糊弄人

判断气温是否上升、降水是否增多,最朴素的方式是对年份和年均温做线性回归。但高分作业往往要求你进一步做显著性检验,否则“趋势”只是抽样误差。SciPy的linregress能同时给出斜率、截距、相关系数和p值,非常够用。

from scipy.stats import linregress # 计算每一年平均气温 yearly_temp = daily.groupby("year")["temp_mean"].mean().reset_index() # 做线性回归 result = linregress(yearly_temp["year"], yearly_temp["temp_mean"]) slope = result.slope # 单位:℃/年 p_value = result.pvalue # 显著性 print(f"升温速率:{slope * 10:.2f} ℃/10年,p值:{p_value:.3f}") # 用回归线生成预测趋势序列 yearly_temp["trend"] = result.intercept + result.slope * yearly_temp["year"]

这里一个关键参数是“单位”:linregress给出的斜率是“每年℃”,但气象报告里习惯写“每10年℃”,所以我会在输出时乘10。p值小于0.05才能说趋势显著,否则只能写“无明显趋势”。很多同学拿到斜率0.008就惊呼升温了,其实p值0.4,完全不能下结论。另外,linregress对异常年份敏感,如果某一年因为厄尔尼诺特别热,斜率会被拉大,所以我通常还会用rolling("5Y").mean()平滑之后再回归一次,两个结果放一起讨论。

3.3 要素相关性:温度和湿度的组合拳

除了单要素趋势,气象数据分析大作业里很加分的一环是要素间相关性,比如“温度与相对湿度呈负相关”“风速与降水的关系”。做法是选几个连续变量,计算皮尔逊相关系数并画热力图。

# 选取需要的数值列 cols = ["temp_mean", "temp_max", "precip_sum", "wind_mean", "pressure_mean"] corr = daily[cols].corr(method="pearson") # 只保留相关系数大于0.3或小于-0.3的单元格,突出强相关 mask = (corr.abs() < 0.3) corr_filtered = corr.mask(mask) import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize=(8, 6)) sns.heatmap( corr_filtered, annot=True, fmt=".2f", cmap="RdBu_r", center=0, vmin=-1, vmax=1, linewidths=0.5, cbar_kws={"label": "Pearson r"} ) plt.title("气象要素间相关性热力图(绝对值<0.3已隐藏)") plt.tight_layout() plt.savefig("corr_heatmap.png", dpi=300)

corr(method="pearson")只适合线性关系;如果你怀疑有单调非线性关系,可以换method="spearman"。热力图里的center=0让正负相关性以白色为分界,颜色对比更直观;vmin/vmax固定为-1和1,否则色标会跟着数据范围漂,不同作业之间就没法比较了。mask隐藏弱相关,是为了让图面更干净,答辩时可以解释为“过滤噪声”。我见过不少作业把相关性矩阵全画出来,红一块蓝一块,解释起来反而混乱。

4. 可视化怎么做才不翻车:从基础图表到多图组合

4.1 趋势图与年内变化图:折线图别只画一条线

气象可视化的第一个基本功是时间序列图。最容易翻车的画法是直接把十年逐日温度画一条线,密密麻麻像心电图,老师根本看不清趋势。正确做法是先分两层:第一层画多年逐日平均的平滑曲线,第二层画单年或季节平均,叠在同一个坐标系里做对比。

import matplotlib.dates as mdates # 计算多年逐日均值(相对日序) daily["dayofyear"] = daily.index.dayofyear clim_day = daily.groupby("dayofyear")["temp_mean"].mean() clim_day_smooth = clim_day.rolling(15, center=True, min_periods=5).mean() plt.figure(figsize=(10, 5)) plt.plot(clim_day_smooth.index, clim_day_smooth.values, color="#d62728", linewidth=2, label="多年日均温(15日滑动平均)") # 画出2023年同期作为对比 daily_2023 = daily[daily["year"] == 2023] plt.plot(daily_2023["temp_mean"].values, color="#1f77b4", alpha=0.6, linewidth=1, label="2023年逐日温度") plt.xlabel("日序(第几天)") plt.ylabel("气温(℃)") plt.legend() plt.savefig("annual_cycle.png", dpi=300)

这里rolling(15, center=True, min_periods=5)是整张图不翻车的关键:滑动窗口设15天,可以消掉天气尺度波动,只留下季节信号;center=True让平滑值对齐到窗口中心,图形不会出现相位偏移;min_periods=5让首尾不足15天的位置也能算出值。如果你不设min_periods,首尾会出现一段空白,映在大作业里很显眼。另外,坐标轴用mdates做日期刻度时要小心,如果x轴是dayofyear,直接默认刻度会输出“1.0, 50.5, 100.0”这种小数,需要手动设置plt.xticks([1, 60, 120, 180, 240, 300, 360], labels=["1月", "3月", "5月", "7月", "9月", "11月", "12月"]),否则答辩会被问“你的横轴单位是什么”。

4.2 空间与概率分布:直方图、箱线图与玫瑰图

光有趋势图还不够,高分作品通常还会覆盖“分布”的维度。例如夏季温度的箱线图能看出极端热事件,降水量的对数直方图能看出偏态分布。下面是一组典型的组合图:左图画各季节温度箱线,右图画冬季风速风向玫瑰图(如果数据里有风向)。

fig, axes = plt.subplots(1, 2, figsize=(14, 5)) # 左图:季节温度箱线 daily["season"] = daily.index.month.map(...) daily.boxplot(column="temp_mean", by="season", ax=axes[0], patch_artist=True, boxprops=dict(facecolor="lightblue"), flierprops=dict(marker="o", markerfacecolor="red", markersize=3)) # 右图:风玫瑰图(需要风向和风速) from windrose import WindroseAxes ws = daily["wind_mean"].values wd = daily["wind_direction"].values ax_wind = WindroseAxes.from_ax(ax=axes[1]) ax_wind.bar(wd, ws, normed=True, opening=0.8, edgecolor="white") ax_wind.set_legend(title="风速 (m/s)")

箱线图的patch_artist=True必须配boxprops,否则画出来是空心箱体,颜色参数不生效,这是matplotlib老版和新版都容易踩的坑。flierprops里的markersize=3把离群点调小,避免异常年份把点画成一大团黑斑。右侧的风玫瑰图用windrose库,如果你没有这个库,可以用pip install windrose安装,但要注意它依赖matplotlib的旧接口,在较新版本里可能报Axes相关错误,属于典型的“库版本冲突”。如果不想引入额外依赖,也可以直接画风速极坐标柱状图,不涉及风向。

4.3 让图表有“可读性”:字体、配色与dpi的统一规范

高分作业和普通作业的视觉差距,往往不在图形复杂度,而在细节统一。我一般会建一个全局matplotlib配置文件,避免每张图都重写一遍字体和样式。

plt.rcParams["font.sans-serif"] = ["SimHei"] # 处理中文显示 plt.rcParams["axes.unicode_minus"] = False # 解决负号显示为方块 plt.rcParams["figure.dpi"] = 120 # 屏幕显示分辨率 plt.rcParams["savefig.dpi"] = 300 # 保存图片分辨率 plt.rcParams["axes.grid"] = True # 默认开网格 plt.rcParams["grid.alpha"] = 0.3 plt.rcParams["axes.spines.top"] = False # 去掉顶部右边框 plt.rcParams["axes.spines.right"] = False

这组参数里最容易被忽略的是axes.unicode_minus,尤其在你用了SimHei等中文字体之后,负号会显示成方框,老师截图给朋友看的时候特别掉价。savefig.dpi=300是硬性要求,很多同学的图在屏幕上看着清楚,插进文档后被压缩得一坨糊,就是因为在代码里没指定输出分辨率。axes.spines去掉两个边框,图更接近现代数据新闻风格,这不是必须的,但能让整套图风格统一。

5. 气象数据可视化避坑指南:5条血泪经验

5.1 现象:中文标签全部显示成方块

原因:matplotlib默认字体不支持中文,且没有正确指定字体名。解决:不要只设plt.rcParams["font.sans-serif"] = ["SimHei"],还要先检查系统里是否真有这个字体。在Linux服务器上,SimHei通常不存在,需要先安装中文字体或改用Noto Sans CJK。更稳妥的办法是用字体管理器动态加载:

import matplotlib.font_manager as fm fm.fontManager.addfont("path/to/your_font.ttf") plt.rcParams["font.family"] = fm.FontProperties(fname="path/to/your_font.ttf").get_name()

这样写的好处是打包作业时连同字体文件一起交,老师在别的机器上跑也能显示中文,而不是一换电脑就翻车。

5.2 现象:时间轴上的日期顺序错乱,12月出现在1月前面

原因:日期列在读取时被当成字符串,直接plt.plot()会按字符串排序。解决:在pd.read_csv里用parse_dates,并在聚合前再做一次pd.to_datetime,并且显式sort_index()。如果你用的是日序而不是日期,要检查dayofyear的取值范围,跨年数据里1月1日和12月31日的日序分别是1和365,画图时要按日序排列,不要按日期字符串排列。

5.3 现象:相关性热力图全是红色,看不出差别

原因:colorbar的范围没有固定,默认跟随数据最小值和最大值,如果所有相关系数都在0.2左右,色标就会从0.2到0.3,导致看起来都是深红。解决:设置vmin=-1, vmax=1,让色标反映理论最大范围,这样相关系数0.2和0.8在颜色上才会有明显差异。同时,center=0能确保正负相关用不同色系展示。

5.4 现象:滑动平均后的曲线比原始数据还毛糙

原因:窗口设置太小,或者没有center=True。我用降水和风速这类波动大的要素时,如果窗口小于7天,平滑效果约等于零。解决:温度用15天,湿度用15~21天,降水量用30天滑动累计。另一个翻车点是rolling之后没有重新索引,导致曲线前后错位,记得加align或直接用center=True。

5.5 现象:代码能在pandas 1.5跑,换到pandas 2.0就报错

原因:新版pandas里resample的how参数废弃,apply对时间序列的传参方式也变了。解决:写大作业时在文件头标明依赖版本,并且用教科书里最保守的写法,不要用骚操作。比如聚合用agg(),不要用apply(lambda x: x.mean());读CSV时encoding直接写utf-8而不是auto。这样在老师的一键运行环境里更容易通过,毕竟他不一定会帮你装一个老版本pandas。

6. 让作业更进一步:交互式大屏与温度预测的尝试

如果你的分析和基础图表都已经成型,还想拉开差距,可以顺手做一页基于Pyecharts的交互式大屏。Pyecharts后端的生态很成熟,能生成HTML报告,不用部署服务,双击就能打开,老师看着会觉得“这人确实下了功夫”。

from pyecharts.charts import Line, Bar, Grid from pyecharts import options as opts # 用已聚合好的月度气候态数据生成交互折线图 line_chart = ( Line() .add_xaxis(monthly_clim["month"].astype(str).tolist()) .add_yaxis( "月均温", monthly_clim["temp_mean"].round(1).tolist(), is_smooth=True, markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_="max", name="最热月")]) ) .set_global_opts( title_opts=opts.TitleOpts(title="某站月均温气候态"), tooltip_opts=opts.TooltipOpts(trigger="axis"), yaxis_opts=opts.AxisOpts(name="℃") ) ) line_chart.render("monthly_clim_interactive.html")

这里is_smooth=True让折线变成曲线,markpoint_opts标出最热月,是答辩时容易被提问的细节:你得能解释“最热月是通过idxmax算出来的,不是肉眼看的”。trigger="axis"让鼠标悬停时同时显示所有序列数值,比默认的item体验好。

如果你还有余力,可以做一个简单的一元线性回归预测未来N年温度,并把预测区间画进图里。注意不要过度承诺“气候预测”,在作业里写清楚“这是基于历史线性趋势的简单外推,非物理模型”。用Scikit-Learn的LinearRegression或者前面用的linregress都行,特征用年份,标签用年均温,预测未来十年,然后画成带置信带的时间序列图。

最后唠叨一句我的习惯:每次交大作业前,我会新建一个干净的环境,按requirements.txt安装依赖,然后一键跑完整套脚本,再把生成的图片名字核对一遍。很多翻车发生在“我电脑上能跑”这个幻觉里。给自己留出至少半天做环境复现,比多画两张图更值。希望这个思路能帮到你,祝你拿到分。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 6:47:07

Vuep:实时编辑与预览Vue组件的神器

Vuep&#xff1a;实时编辑与预览Vue组件的神器 【免费下载链接】vuep &#x1f3a1; A component for rendering Vue components with live editor and preview. 项目地址: https://gitcode.com/gh_mirrors/vu/vuep Vuep 是一个使用 JavaScript 编写的开源项目&#xff…

作者头像 李华
网站建设 2026/9/26 6:47:03

Qlib AI量化实战:从数据到回测的全链路拆解指南

Qlib AI量化实战&#xff1a;从数据到回测的全链路拆解指南 【免费下载链接】qlib Qlib is an AI-oriented Quant investment platform that aims to use AI tech to empower Quant Research, from exploring ideas to implementing productions. Qlib supports diverse ML mod…

作者头像 李华
网站建设 2026/9/26 6:44:31

IPFS与以太坊存证集成:从CID到链上哈希的完整链路

简介&#xff1a;面向区块链与分布式存储初学者&#xff0c;这份资源围绕健康记录跟踪场景&#xff0c;演示以太坊智能合约与IPFS集成的基础链路&#xff0c;涵盖Truffle与Ganache环境配置、MetaMask和MyEtherWallet调用流程&#xff0c;并让医生通过合约检索健康记录IPFS ID、…

作者头像 李华
网站建设 2026/9/26 6:44:30

软件上线实操 checklist:需求落地、质量卡点与闭环交付

1. 这不是教科书&#xff0c;而是一份被删掉37次又重写的上线 checklist“软件开发全流程&#xff1a;从需求到上线的完整指南”——看到这个标题&#xff0c;我第一反应是关掉页面。太虚了。就像说“如何做饭”&#xff0c;结果通篇讲“火候很重要”“食材要新鲜”&#xff0c…

作者头像 李华
网站建设 2026/9/26 6:44:16

从埋点失控到规则校验:Chrome DevTools Panel实战全记录

做了三年数据平台&#xff0c;我最怕听到的一句话不是"这个需求做不了"&#xff0c;而是"埋点好像没生效"。排了半天发现事件名字典里根本没有这个 event&#xff0c;或者字段名大小写对不上&#xff0c;又或者 pv 和 uv 的量级离谱。这种问题&#xff0c;…

作者头像 李华
网站建设 2026/9/26 6:44:15

PID控制结构选型:位置式、增量式、PI、PD与前馈的工程决策指南

1. 控制算法选型不是填空题&#xff0c;而是工况诊断题你手头有个电机要调速&#xff0c;或者温控箱要稳温&#xff0c;又或者机械臂关节要精确定位——第一反应是不是直接上网搜“PID参数整定”&#xff1f;抄几组Kp、Ki、Kd往控制器里一填&#xff0c;调半天发现超调大、响应…

作者头像 李华