气象数据分析实战:从数据清洗到趋势检验的完整实验流程 简介这份资源面向数据分析初学者与需要完成课程实验的学生围绕气象数据展开完整分析实践通过爬虫代码抓取中国天气网某一城市的天气数据再对数据做清洗与统计生成雷达图、条形图等多种可视化图表并配以分析说明帮助读者理解从数据获取到图表呈现的完整流程。压缩包共39个文件约1.69MB以20个png图表截图、15个xml与4个rels文档结构文件为主其中png直观展示各类分析结果xml与rels构成实验报告的文档骨架源代码可直接用Jupyter Notebook运行。已有1854人学习下载说明其作为课程实验参考具有一定认可度。读者可获得爬虫源码、可视化绘图代码与整理后的实验报告便于对照复现分析过程、理解图表选型与结论撰写适合作为数据分析入门练手或实验课作业的参考模板。1. 气象分析不是画折线图一份能跑通的数据分析报告实验包很多人对气象数据分析的第一印象就是把气温画成一条折线、把降水画成柱状图然后贴进报告交差。真到要复现一份完整分析报告时才发现卡住人的从来不是画图而是数据清洗、缺测值处理、时间序列对齐、异常年份识别这一整套流程。这份气象分析数据分析报告实验资源解决的正是“从原始观测数据到一份能自圆其说的分析报告”之间的断层。它适合两类人一类是刚接触数据分析、需要一份完整实验流程照着走的新手另一类是做过不少业务分析、但没系统处理过气象时序数据的从业者。下面按“资源是什么、怎么用、坑在哪”的顺序拆开讲每一步都落到可复现的操作上。2. 数据准备与清洗把原始观测表变成能算的 DataFrame气象数据的第一个门槛不在分析而在“能不能算”。原始观测表常见的问题是列名不统一、日期格式混乱、缺测值用特殊符号标记、单位不统一。这一章先把数据读进来、把类型和缺失处理干净后面所有分析才有意义。2.1 读入与列名规范化常见做法是用 pandas 读 CSV 或 Excel然后统一列名、统一日期索引。下面这段是资源里数据准备脚本的核心逻辑我按自己的习惯补了注释和容错import pandas as pd import numpy as np # 读入原始观测表encoding 按实际文件调整gbk 在部分导出文件里很常见 df pd.read_csv(raw_weather.csv, encodingutf-8) # 列名规范化去空格、统一小写、把中文列名映射成英文 col_map { 日期: date, 气温: temp, 最高气温: temp_max, 最低气温: temp_min, 降水量: precip, 相对湿度: humidity, 风速: wind_speed, 气压: pressure } df df.rename(columns{c: c.strip() for c in df.columns}) df df.rename(columnscol_map) # 日期列转 datetime无法解析的置为 NaT方便后面统计坏行 df[date] pd.to_datetime(df[date], errorscoerce) df df.dropna(subset[date]).sort_values(date).reset_index(dropTrue) # 数值列强制转 float非数字如 —、缺测会变成 NaN num_cols [temp, temp_max, temp_min, precip, humidity, wind_speed, pressure] for c in num_cols: df[c] pd.to_numeric(df[c], errorscoerce) print(df.shape, df[date].min(), df[date].max())这段代码的关键点有三个errorscoerce让无法解析的值变成NaT/NaN而不是直接抛异常便于批量统计坏数据dropna(subset[date])只丢掉日期缺失的行其他列的缺失留到后面按列处理sort_values(date)保证后续做时间序列时顺序正确。参数上encoding要根据实际文件试中文导出文件常见gbk和utf-8两种读出来乱码就换另一个。2.2 缺测值处理与单位统一气象观测里缺测是常态直接dropna()会把整行删掉导致样本量骤减。更稳妥的做法是按列判断缺失比例再决定插值还是剔除。资源里的处理策略是缺失比例低于 5% 的连续变量用线性插值高于 5% 的列保留缺失并在报告里注明。# 统计每列缺失比例 missing_ratio df[num_cols].isna().mean().sort_values(ascendingFalse) print(missing_ratio) # 缺失低于 5% 的列做线性插值limit_direction 保证首尾也能补 for c in num_cols: if missing_ratio[c] 0.05: df[c] df[c].interpolate(methodlinear, limit_directionboth) # 单位统一气温若为华氏度则转摄氏度降水若为英寸则转毫米 if df[temp].max() 60: # 粗略判断是否为华氏度 df[temp] (df[temp] - 32) * 5 / 9 if df[precip].max() 5: # 粗略判断是否为英寸 df[precip] df[precip] * 25.4插值方法选线性是因为气象要素在短时间缺口内近似连续变化limit_directionboth能补上序列首尾的缺口。单位判断用阈值是权宜之计正式报告里应该以数据字典为准这里只是给没有字典时的兜底。做完这一步数据才算“能算”后面所有统计和建模都建立在这个干净的 DataFrame 上。3. 描述统计与趋势分析让报告里的每个数字都有出处数据干净之后报告的主体是描述统计和趋势判断。这一章的重点不是算得多花哨而是每个结论都能追溯到具体计算避免“看起来在上升”这种没有依据的表述。3.1 分季节统计与同比对比气象分析报告几乎一定会按季节或月份分组统计。资源里的做法是先加时间维度列再分组聚合同时算均值和极值# 构造季节列北半球按 3-5 春、6-8 夏、9-11 秋、12-2 冬 def season(m): if m in [3, 4, 5]: return spring if m in [6, 7, 8]: return summer if m in [9, 10, 11]: return autumn return winter df[month] df[date].dt.month df[year] df[date].dt.year df[season] df[month].map(season) # 按年季节聚合同时输出均值、最大值、样本数 stat df.groupby([year, season]).agg( temp_mean(temp, mean), temp_max(temp_max, max), temp_min(temp_min, min), precip_sum(precip, sum), days(date, count) ).reset_index() # 计算各季节相对上一年的变化用于报告里的同比描述 stat[temp_yoy] stat.groupby(season)[temp_mean].diff() print(stat.head(10))agg里同时算均值和极值是为了报告里既能说“平均气温”也能说“极端最高温”两个指标回答的是不同问题。diff()算同比变化注意要先按季节分组再 diff否则跨季节相减没有意义。样本数days一定要带上报告里写“某年夏季均温偏高”时如果该季节样本只有十几天这个结论的可信度要打折扣这是很多报告忽略的点。3.2 趋势判断别只看斜率趋势分析常见翻车点是拿一条线性拟合的斜率就下结论。气象序列有季节周期和年际波动直接对整段数据拟合会得到误导性结果。资源里的处理是先做年际聚合再拟合同时给出相关系数from scipy import stats # 先按年聚合年均温消除季节周期 annual df.groupby(year)[temp].mean().reset_index() # 线性回归输出斜率、截距、相关系数、p 值 slope, intercept, r, p, se stats.linregress(annual[year], annual[temp]) print(fslope{slope:.4f} ℃/年, r{r:.3f}, p{p:.4f}) # 报告里同时给出十年变化幅度比“每年变化”更直观 print(f每十年变化约 {slope * 10:.2f} ℃)linregress返回的p值用来判断趋势是否显著r值反映拟合强度。报告里写“呈上升趋势”之前至少要看p是否小于 0.05否则只能说“未检测到显著趋势”。把斜率换算成“每十年变化”是气象报告的惯例表达读者更容易理解量级。这一步的坑在于年份样本太少时p值不可靠一般少于 20 年的序列不建议做强趋势结论。4. 可视化与报告输出图要能自己说话分析做完报告的说服力很大程度落在图上。这一章讲清楚图怎么选、参数怎么调、输出怎么组织让图本身能承载结论而不是靠正文去解释图。4.1 时序图与分布图的参数设置时序图看趋势和周期分布图看极值和偏态两张图承担不同任务。资源里的绘图脚本用 matplotlib关键在坐标轴、刻度和标注import matplotlib.pyplot as plt import matplotlib.dates as mdates plt.rcParams[font.sans-serif] [SimHei] # 中文显示 plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(12, 4)) ax.plot(df[date], df[temp], lw0.8, color#2b6cb0, label日均温) # 用滚动平均突出趋势窗口 30 天对应月尺度 ax.plot(df[date], df[temp].rolling(30).mean(), lw2, color#c53030, label30日滑动平均) ax.xaxis.set_major_locator(mdates.YearLocator()) ax.xaxis.set_major_formatter(mdates.DateFormatter(%Y)) ax.set_xlabel(年份); ax.set_ylabel(气温 (℃)) ax.legend(); ax.grid(alpha0.3) plt.tight_layout() plt.savefig(temp_timeseries.png, dpi150)rolling(30).mean()是让趋势线不被日波动淹没的常用手段窗口大小按分析尺度定月尺度用 30、季尺度用 90。dpi150保证报告里插图清晰tight_layout()避免标签被裁。分布图则用直方图叠加核密度重点看偏态和极端值位置参数上bins取 30 左右比较平衡太少看不出形状太多噪声大。4.2 报告结构组织一份能复现的气象分析报告结构上建议固定为数据来源与时段说明、清洗规则与缺失处理、描述统计表、趋势与异常分析、结论与局限。资源里的报告模板把每张图对应到一个小节图下必须有一句“这张图说明了什么”避免图堆砌。表格只放关键统计量完整统计表放附录。这样组织的好处是别人拿到报告能顺着数据来源一路查到结论复现时也知道每一步对应哪个输出。5. 避坑与排查气象时序分析里最容易翻车的五件事这一章是我自己踩过、也在别人报告里反复见到的坑按“现象 → 原因 → 解决”写照着排查能省不少返工。5.1 日期解析后顺序错乱现象时序图出现来回折返的锯齿趋势完全看不出来。原因原始日期格式不统一有的2020/1/1有的2020-01-01to_datetime部分解析失败后被丢掉或错位排序前索引已乱。解决解析后先dropna(subset[date])再sort_values(date)并打印df[date].is_monotonic_increasing确认单调递增。5.2 缺测值被当成 0 参与统计现象某年降水量明显偏低均值被拉低。原因缺测标记如-999、—被to_numeric转成 NaN 后又被fillna(0)填掉。解决缺测一律保留 NaN统计时用skipnaTruepandas 默认降水求和前先确认该时段缺测天数缺测多的时段在报告里标注而不是补 0。5.3 单位混用导致量级错误现象气温均值算出 50 多或者降水总和只有个位数。原因部分年份用华氏度、英寸部分用摄氏度、毫米合并后没统一。解决在清洗阶段按数据字典统一单位没有字典时用分位数和物理合理范围做校验气温合理范围约 -60 到 60 ℃超出就查单位。5.4 滚动平均窗口跨年断裂现象趋势线在每年 1 月出现断点。原因rolling按行计算跨年时窗口内混入了不同年份但时间不连续的数据。解决先按完整时间序列重采样到日频补齐日期再做滚动或者直接用resample(M).mean()做月尺度聚合避免窗口错位。5.5 趋势结论忽略样本量现象报告写“近五年显著升温”但被质疑。原因五年样本做线性回归p值偶然性大且未排除城市热岛等非气候因素。解决趋势分析至少用 20 年以上序列报告里注明样本年限和显著性水平结论措辞用“在该时段内呈上升趋势”而非绝对判断。6. 进阶技巧用重采样和异常检测把报告做深基础流程跑通后想让报告更有分量有两个方向值得加一是用重采样把日数据聚到月、季、年多尺度对比二是用简单异常检测标出极端事件。重采样这块df.set_index(date).resample(M).agg({temp:mean,precip:sum})一行就能出月尺度表注意resample要求 datetime 索引聚合函数要按变量选气温用均值、降水用求和混用会得出没意义的数。异常检测我一般用滚动 Z 分数窗口取 30 天阈值设 2.5 到 3超过就标为异常日roll_mean df[temp].rolling(30, min_periods15).mean() roll_std df[temp].rolling(30, min_periods15).std() df[z] (df[temp] - roll_mean) / roll_std df[is_anomaly] df[z].abs() 2.5 print(df.loc[df[is_anomaly], [date, temp, z]].head())min_periods15保证序列开头不会因为窗口不满而全是 NaN阈值 2.5 比 3 更敏感适合找极端天气过程但会带来更多误报报告里要结合天气背景人工复核。这套方法的好处是不依赖分布假设滚动窗口自动适应季节变化。我自己的习惯是每次出报告前都强制走一遍“日期单调性检查、缺失比例打印、单位范围校验”这三步哪怕数据看起来没问题也不跳过因为翻车往往就出在“看起来没问题”的地方。希望这份拆解能帮到你把这份实验资源真正跑成自己的一份报告。本文还有配套的精品资源点击获取