法国七月的高温和干旱在近年多次刷新历史纪录。“打破纪录”这个说法放到气候数据分析里并不是修辞,而是一组可计算、可检验的统计结论:某年 7 月的气温或降水指标,是否超过了该地区有气象记录以来的对应阈值。要复现这样的分析,需要完成数据获取、质量清洗、气候态基准期计算、距平分析、异常检测和干旱指数计算等一系列步骤。下面这套流程从公开气象数据出发,搭建一套可运行的 Python 分析链路,覆盖从原始数据到“是否破纪录”结论的完整闭环,适合正在学习 pandas、xarray、时间序列分析,或者从事气象、农业、环境数据分析的读者。
1. 先理解气候“破纪录”是怎么被定义出来的
1.1 “比往年热”“干旱”不等于破纪录
“比往年热”是对当前温度与近期记忆的比较,属于主观感受。“破纪录”则是一个统计判断,必须放到足够长的历史序列里进行对比。比如某个站点 7 月平均气温比当地 1991-2020 年基准值高了 1.5 摄氏度,可以说当季气温明显偏高,但只有这个值超过该站点自建站以来的历史最大值,才能说明该站点层面的气温纪录被刷新。
干旱的情况更复杂。降水偏少不一定直接等于干旱,因为干旱还取决于温度、风速、太阳辐射等因素对水分蒸散的影响。高温天气可能让土壤水分快速消耗,即使降水量接近常年,作物或自然植被也可能出现水分亏缺。因此在分析“七月干旱破纪录”时,不能只看降水量,还应该看蒸散、土壤湿度、径流等变量。
1.2 气候异常分析依赖三类基础数据
要分析法国七月高温干旱这类问题,通常需要把数据分成三类:站点观测数据、格点再分析数据、衍生干旱数据。三类数据各有适用场景,落地前要先想清楚自己需要哪一种。
| 数据类别 | 常见字段 | 典型来源 | 分析用途 |
|---|---|---|---|
| 站点观测数据 | 平均气温、最高气温、最低气温、降水量 | NOAA GHCN、ECAD、各国气象机构公开接口 | 计算站点尺度的月均值、距平、历史纪录 |
| 格点再分析数据 | 2 米气温、总降水、蒸散量、土壤湿度 | ERA5、CRU TS | 空间覆盖完整,适合画区域分布图和面积加权统计 |
| 干旱衍生数据 | SPI、SPEI、PDSI、土壤湿度指数 | 官方干旱监测数据或基于再分析数据自算 | 描述气象干旱、农业干旱程度 |
站点数据的优点是时间序列长、物理意义直接,缺点是空间分布不均。格点数据的优点是空间连续,缺点是不同版本之间存在差异,历史序列长度也有限。对于初学者,建议先用站点数据完成指标计算,理解“距平”和“历史排序”的逻辑,再切换到格点数据做空间分析。
1.3 一次理清关键术语:距平、历史百分位、气候基准期
“距平”是观测值与气候态基准值之间的差。比如某站 7 月平均气温为 24.3 摄氏度,而当地 1991-2020 年 7 月平均气温为 22.8 摄氏度,则气温距平为 +1.5 摄氏度。距平描述的是相对于历史平均状态的偏离,并不是绝对温度本身。
“历史百分位”用于衡量某个值在历史序列中的位置。如果某年 7 月的区域平均气温排在历史序列第 99 百分位,说明只有约 1% 的年份像当年一样热,这是判断“极端”和“破纪录”的重要依据。要注意“破纪录”和“超过 99 分位”并不是同一个概念。破纪录要求超过历史最大值,而 99 分位只是超过绝大多数年份,二者阈值不同。
“气候基准期”是计算气候态的参考时间段。WMO 推荐使用连续的 30 年,例如 1981-2010 或 1991-2020。选择不同基准期会直接改变距平值,因为基准期本身也受到气候变化影响。分析中必须固定基准期,并在结果报告里写清楚,否则别人无法复现你的结论。
2. 环境准备:用 Python 搭建一套可复现的分析环境
2.1 Python 依赖安装与版本说明
这套分析主要依赖 pandas、numpy、xarray、matplotlib、scipy。如果处理 netCDF 格点数据,还需要 netCDF4。下面是一个相对稳定的依赖组合,实际安装时可以根据自己的 Python 版本微调。
# requirements.txt 示例 pandas==2.1.4 numpy==1.26.3 xarray==2024.1.1 netCDF4==1.6.5 matplotlib==3.8.2 scipy==1.12.0pip install -r requirements.txt如果机器上已经装了较新的 pandas 或 numpy,不要盲目降级。优先用虚拟环境隔离项目,避免影响其他工程。创建虚拟环境的常用命令如下。
python -m venv .venv source .venv/bin/activate pip install -r requirements.txt代码示例中的 API 基于 pandas 2.x,如果使用 pandas 1.x,部分行为可能略有差异,例如parse_dates、groupby.apply的结果类型会更依赖旧版实现。学习阶段建议直接使用 2.x。
2.2 公开气候数据源怎么选
不同数据源适合不同任务。如果只想验证方法,优先选下载简单、格式稳定、字段清晰的数据;如果要处理真实的法国区域事件,则要检查数据的时间范围和空间覆盖。
| 数据源 | 数据形态 | 空间范围 | 获取方式 | 适合场景 |
|---|---|---|---|---|
| NOAA GHCN-Daily | 站点观测 CSV | 全球 | FTP 或 API | 按站点做长序列分析 |
| ECA&D | 欧洲站点观测 | 欧洲 | 页面下载或 API | 法国及欧洲站点级分析 |
| Copernicus ERA5 | 格点再分析 netCDF | 全球 | CDS API | 空间分布、区域面积加权统计 |
| CRU TS | 格点插值降水气温 | 全球陆地 | 页面下载 | 月尺度快速分析,字段简单 |
获取时序数据时要重点确认三件事:数据是否更新到目标年份、站点在法国区域内是否有足够的连续记录、缺失值标记是什么。很多数据集用-9999表示缺失,直接用均值计算会把缺失值当成真实数值,导致结果严重失真。
2.3 下载前的数据字段检查清单
在写任何分析代码之前,先统计一份字段检查清单,避免程序写着写着才发现单位或时间频率不对。
- 时间频率是日值、月值还是小时值。
- 日期是否已经统一到 UTC,是否需要转换到欧洲时区。
- 温度单位是摄氏度还是开尔文。
- 降水单位是毫米还是千克每平方米。
- 站点经纬度是十进制度还是度分秒。
- 缺省标记是 NaN、-9999 还是 32767。
- 数据版本号是什么,是否会影响和历史数据的拼接。
注意:不要只检查前几行数据。要检查整个时间范围的最小日期、最大日期、站点数量和缺失值比例,这些信息决定了后续计算的可靠性。
3. 一个最小分析案例:处理法国区域 7 月气温与降水数据
3.1 项目目录结构和数据文件布局
推荐把原始数据、处理结果、脚本、图表分开存放,方便追踪数据版本和分析步骤。一个最小项目结构如下。
climate-france-july/ ├── data/ │ ├── raw/ │ │ ├── temperature_station.csv │ │ └── precipitation_station.csv │ ├── processed/ │ └── external/ ├── scripts/ │ ├── 01_load_and_clean.py │ ├── 02_climate_anomaly.py │ └── 03_drought_index.py ├── output/ │ ├── fig/ │ └── report/ └── README.mddata/raw下面的文件要保持只读,所有清洗和派生结果都写入data/processed。这样一旦计算结果异常,可以直接回到原始文件重新处理,不会因为中间过程被覆盖而丢失线索。
3.2 读取与清洗:把杂乱的气象数据整理成统一 DataFrame
假设下载到的站点数据包含日期、站点编号、站点名称、平均气温、降水量等字段。实际文件里列名可能叫tavg、tmean、tg,也可能叫time、date,所以第一步是统一列名。
import pandas as pd # 假设原始 CSV 包含 date, station_id, station_name, tavg, prcp 等字段 df_raw = pd.read_csv("data/raw/temperature_station.csv", parse_dates=["date"]) print(df_raw.head()) # 统一列名:真实项目按数据源元数据确认 column_map = { "date": "date", "time": "date", "tavg": "tavg", "tmean": "tavg", "tg": "tavg", "prcp": "prcp", "rr": "prcp", } rename_map = {k: v for k, v in column_map.items() if k in df_raw.columns} df_raw = df_raw.rename(columns=rename_map) # 只保留 7 月数据 df_july = df_raw[df_raw["date"].dt.month == 7].copy() df_july["year"] = df_july["date"].dt.year # 去重:同一站点同一日期出现多次时,保留最后一条 df_july = df_july.drop_duplicates(subset=["station_id", "date"], keep="last") # 缺失值标记统一转成 NaN df_july["tavg"] = df_july["tavg"].replace(-9999.0, pd.NA) df_july = df_july.dropna(subset=["tavg"])这一步最容易出错的地方是只按月份筛选,没有考虑年份。date.dt.month == 7会选出所有年份的 7 月,这是正确的。但如果后续要按日聚合,就要同时保留 year、month、day 三个字段,避免直接按字符串月份过滤。
3.3 计算月均值和距平:破纪录判断的核心指标
站点数据通常是日值。要做“法国 7 月气温破纪录”分析,需要先把日值按站点和年份聚合为月均值,再选择气候基准期,计算距平。
# 按站点和年份计算 7 月平均气温 july_tavg = ( df_july.groupby(["station_id", "station_name", "year"])["tavg"] .mean() .reset_index() ) # 选择气候基准期,例如 1991-2020 base_start, base_end = 1991, 2020 base = july_tavg[(july_tavg["year"] >= base_start) & (july_tavg["year"] <= base_end)] clim = base.groupby("station_id")["tavg"].mean().rename("tavg_clim") # 合并并计算距平 july_tavg = july_tavg.merge(clim, on="station_id", how="left") july_tavg["anomaly"] = july_tavg["tavg"] - july_tavg["tavg_clim"]这里有两个关键点。第一,基准期选择会影响所有距平结果,所以必须显式定义变量base_start、base_end,而不是在代码里写死多个地方。第二,groupby聚合时默认会忽略 NaN,但如果某年 7 月有大量缺失日数,算出来的月均值会有偏,需要设定最小有效日数检查。比如某站点 7 月只有 5 天有观测,这 5 天的平均不能代表整月。
3.4 用历史百分比排名判断是否“创纪录”
拿到每个站点每年的 7 月均值后,可以用rank计算该值在历史序列中的百分位,再判断是否为历史最大值。需要注意,这里计算的是“月平均气温”序列,而不是“日最高气温”序列。这是初学者最容易混淆的地方。
# 为每个站点计算历史百分位和历史最高值 def judge_record(frame): frame = frame.sort_values("year").copy() frame["rank_pct"] = frame["tavg"].rank(pct=True) * 100 frame["is_hist_max"] = frame["tavg"] == frame["tavg"].max() return frame station_result = ( july_tavg.groupby("station_id", group_keys=False) .apply(judge_record) .reset_index(drop=True) ) # 查看单个站点结果 one_station = station_result[station_result["station_id"] == "FR000123"] print(one_station.tail(5))rank(pct=True)计算的是百分位排名。如果一个站点的 2023 年 7 月均温排名是 100,说明它超过了历史上所有年份,可以被认定为该站点层面的“破纪录”。如果是 99,说明它超过了 99% 的年份,属于极端高温,但还没有刷新历史纪录。
对于“法国七月破纪录”这种区域级结论,不能只凭一个站点判断。需要先计算区域内所有站点的平均值或面积加权平均值,生成一条区域时间序列,再在区域序列上做历史排名。站点数不均匀也会造成偏差,比如某个地区站点特别多,简单平均会高估该地区的权重。
4. 干旱维度:不只降水少,更要看蒸散和土壤湿度
4.1 为什么只看降水量不够
降水偏少是干旱的常见触发因素,但不是唯一因素。高温会造成潜在蒸散增大,让土壤和植被失去更多水分。如果某年 7 月降水接近常年,但气温显著偏高,实际可用水量可能仍然不足。因此在干旱分析中,要引入水分收支的概念:降水是收入项,蒸散是支出项。
在气象干旱分级里,常见指标包括 SPI 和 SPEI。SPI 只基于降水,计算简单;SPEI 同时考虑降水和潜在蒸散,更贴近高温驱动的干旱。法国夏季高温干旱场景中,SPEI 通常比 SPI 更能反映水分亏缺。
4.2 用标准化降水蒸散指数描述干旱
SPEI 的基本思路是把逐月降水和潜在蒸散的差值,也就是气候水平衡,累积到指定时间尺度,然后拟合概率分布,转换成标准正态分布的分位数。负值代表干旱,正值代表湿润。
直接使用成熟的库会比手写可靠。科学计算里常见的选择是 Python 的spei、climindvis,或者 R 语言相关包。如果只是学习原理,也可以先写一个简化版,用正太分布近似代替复杂分布拟合。
4.3 把 SPEI 计算封装成通用函数
下面是一个用于说明计算思路的简化函数,它接受月降水和月潜在蒸散序列,返回近似 SPEI 值。真实项目建议替换为成熟实现,并做分布拟合检验。
import numpy as np import pandas as pd from scipy.stats import gamma, norm def spei_like(prcp, pet, timescale=1, fit_period=None): """简化版标准化降水蒸散指数计算。 参数: prcp: 月降水序列 pet: 月潜在蒸散序列 timescale: 累积月尺度,例如 1、3、6 fit_period: 分布拟合所用的时间范围,例如 ("1991-01-01", "2020-12-31") """ bal = prcp - pet if timescale > 1: bal = bal.rolling(timescale, min_periods=1).sum() valid = bal.dropna() if fit_period is not None: valid = valid.loc[fit_period[0]:fit_period[1]] # 去掉非正值,避免 gamma 分布拟合失败 pos = valid[valid > 0] if len(pos) < 12: return pd.Series(np.nan, index=bal.index, name="spei") shape, loc, scale = gamma.fit(pos) prob = gamma.cdf(bal, shape, loc=loc, scale=scale) spei = norm.ppf(prob.clip(1e-6, 1 - 1e-6)) return pd.Series(spei, index=bal.index, name="spei")使用示例:
# 假设已有逐月数据,precip 和 pet 都是 pandas Series,索引为月份 spei_series = spei_like(precip, pet, timescale=1) print(spei_series.tail(6))注意:这个简化版不具备严格统计学意义。SPEI 的正式计算需要选择合适的概率分布,并对 0 值做专门处理。只有理解了原始算法,才能判断简化版在什么情况下可用。
5. 数据可视化与结果解释
5.1 画时间序列和距平柱状图
分析结果如果只用表格呈现,很难快速定位“破纪录”的年份。推荐至少画两张图:一张是某站点或区域 7 月均温的时间序列,另一张是所有年份相对基准期的距平柱状图。时间序列图适合看长期趋势和极值,距平图适合看正负偏离。
import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(10, 5)) ax.plot(one_station["year"], one_station["tavg"], marker="o", linewidth=0.8) max_year = one_station.loc[one_station["is_hist_max"], "year"].iloc[-1] ax.scatter( [max_year], one_station.loc[one_station["year"] == max_year, "tavg"], color="red", zorder=3, label=f"record: {int(max_year)}", ) ax.axhline(one_station["tavg_clim"].iloc[-1], color="gray", linestyle="--", label="clim 1991-2020") ax.set_xlabel("year") ax.set_ylabel("July mean temp (C)") ax.set_title("July mean temperature at one station") ax.legend() plt.tight_layout() plt.savefig("output/fig/july_tavg_station.png", dpi=150)绘图时要注意单位。如果数据源里温度是开尔文,必须先减 273.15 转成摄氏度,否则图上纵轴会显示 300 左右的数值,容易误导。
5.2 空间分布图怎么处理
如果使用的是 netCDF 格点数据,可以用 xarray 读取变量,再通过 contourf 或 pcolormesh 绘制空间分布。法国区域需要保留合适的经纬度范围,通常只裁剪到法国本土范围,避免把大西洋和北非大范围区域也画出来。
import xarray as xr ds = xr.open_dataset("data/raw/era5_july_t2m.nc") # 根据实际变量名修改 tas = ds["t2m"] - 273.15 july_mean = tas.sel(time=tas["time"].dt.month == 7).groupby("time.year").mean("time") # 提取某一年与基准期距平 anomaly = july_mean.sel(year=2023) - july_mean.sel(year=slice(1991, 2020)).mean("year")如果没有安装 cartopy,格点空间图也可以先用站点散点图代替,把站点气温距平按颜色画到经纬度坐标上。这样做虽然不如格点图平滑,但也能看出空间差异。
5.3 结果报告应包含哪些内容
任何“破纪录”结论都必须能被复核,所以最终报告至少包含五部分:数据来源与版本、处理脚本位置、指标定义、阈值标准、不确定性说明。缺少任何一部分,读者都无法判断结论成立的条件。
一个可复用的输出结构是:
- 数据源名称、下载时间、数据版本。
- 站点筛选条件,如最少有效观测天数、最长连续缺失时间。
- 气候基准期,如 1991-2020。
- 指标定义,如“站点 7 月平均气温”或“区域面积加权平均气温距平”。
- 破纪录判定规则,如“超过历史序列最大值”。
- 结果表,包括站点编号、年份、月均值、距平、百分位、是否破纪录。
6. 常见问题与排查路径
6.1 数据下载失败或字段对不上
现象是脚本读取 CSV 时报KeyError或ParserError。常见原因是数据源更新了列名、文件编码不是 UTF-8、或者下载的文件实际是 JSON 格式。
排查顺序是先看文件头,再查数据源文档,最后再改代码。
head -20 data/raw/temperature_station.csv| 问题现象 | 常见原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 读取报 UnicodeDecodeError | 文件编码不是 UTF-8 | 检查文件头,查看编码声明 | 用encoding="latin1"或encoding_errors="ignore"调整 |
| 列名对不上 | 数据源更新字段名 | print(df_raw.columns) | 维护 column_map 映射表,不要按位置取列 |
| 时间列解析失败 | 日期格式不统一 | 查看原始字符串样例 | 指定date_format,或用pd.to_datetime的多种格式尝试 |
| 下载后文件只有几 KB | 请求失败或需要登录 | 查看文件大小和首行内容 | 检查网络请求状态码、认证参数,重新下载 |
6.2 月份时间戳错位一整天或一个月
现象是每年 7 月的数据里混入了 6 月末或 8 月初的数据,或者月均值偏低。常见原因有两个:数据源使用 UTC 时间,而法国本土在东一时区,夜间 0 点到 1 点的数据在 UTC 下仍然显示为前一天;另一个原因是重采样时使用了M而不是ME,造成日期偏移。
处理方式是把时间统一到本地时区,并明确“日数据交换时间”。很多气象日值数据以当地 06 时为日界,而不是自然日 0 点。
# 示例:把 UTC 时间校正到欧洲/巴黎时区 df["date_paris"] = df["date_utc"].dt.tz_convert("Europe/Paris") # 如果数据源每日 06 UTC 交换,代表当地当日数据 df["date_local"] = (df["date_utc"] - pd.Timedelta(hours=6)).dt.tz_localize(None)时间错位问题通常不会报错,只会在最终结果里表现为“1 月数据偏少”或“7 月均值偏低”,这类隐性错误最需要警惕。建议在聚合后输出每个站点的有效天数分布,如果发现某年 7 月只有 28 天,就说明日界或过滤逻辑有问题。
6.3 结果出现 NaN 或极端离群值
NaN 过多时,先确认缺失值标记是否已经统一。极端离群值则需要区分是真实极端天气还是单位错误。例如温度数据混入了开尔文和摄氏度两种单位,会造成 300 左右的巨大离群值。
# 查看温度分布,快速发现单位问题 print(df_july["tavg"].describe()) # 统计缺失比例 missing_ratio = df_july["tavg"].isna().groupby(df_july["station_id"]).mean() print(missing_ratio.sort_values(ascending=False).head(10))如果某个站点的温度超过 55 摄氏度或低于 -20 摄氏度,就要检查是否站点搬迁、传感器故障或单位转换遗漏。不要轻易删除离群值,而是先结合站点元数据判断。
6.4 “破纪录”被误判的三种情况
第一种误判是用单日极值代替月均值纪录。“7 月某天最高气温达到 42 摄氏度”和“7 月平均气温达到历史最高”是两个不同指标,不能混用。新闻标题经常说高温破纪录,指的可能是单日最高温,但气候统计里的“7 月破纪录”通常指月均值。
第二种误判是混用不同气候基准期。一个脚本用 1981-2010 计算距平,另一个脚本用 1991-2020 计算距平,两者比较时会出现系统性偏差。必须在所有图表和表格中统一基准期。
第三种误判是数据序列长度不一致。有的站点只有 10 年历史,有的站点有 80 年历史,把它们放在一起比百分位并不公平。处理办法是设定最小年份数,比如要求至少 30 年完整记录,才参与“历史纪录”排名。
7. 生产环境与长期监测建议
7.1 从研究脚本到定时任务
分析脚本在 notebook 里跑通只是第一步。如果要做长期监测,比如每年 8 月自动发布上一月的高温干旱简报,就需要把脚本结构化为可执行文件,并配置定时调度。
生产环境下可以参考如下流程:
- 把数据下载、清洗、指标计算、图表输出拆成独立 Python 文件。
- 每个文件都使用
if __name__ == "__main__":入口,方便命令行执行。 - 使用 cron 或调度平台定时运行,并保留每次运行的日志。
- 输出文件带上日期或数据版本号,避免覆盖历史结果。
- 增加运行状态检查,例如数据行数小于预期时发送告警。
# 示例:每天凌晨 2 点检查并下载最新数据 0 2 * * * cd /path/to/climate-france-july && /path/to/.venv/bin/python scripts/01_download_data.py >> logs/fetch.log 2>&17.2 数据版本管理和元数据
气候数据会不断更新,同一份 7 月数据在不同时间下载,可能因为延迟订正而略有不同。为了让“破纪录”结论可追溯,必须在输出目录中记录数据源版本和下载时间。
推荐把元数据写入一个 JSON 文件,随结果一起保存。
{ "dataset": "example_climate_station", "download_time": "2025-07-01T06:00:00Z", "version": "v2", "base_period": "1991-2020", "variable_unit": { "tavg": "celsius", "prcp": "mm" }, "missing_flag": "-9999" }生产环境还需要考虑数据源接口是否稳定、是否需要登录认证、是否限制每秒请求次数。不要把下载逻辑和分析逻辑混在同一个脚本里,否则一旦数据源变更,会影响整个流程。
7.3 分析结论的确定性表述
气候分析结论要谨慎,尤其是涉及公共传播时。与其说“法国七月已经破纪录”,不如说“在某数据集、某区域范围内,某年 7 月区域平均气温超过了该数据集历史最高值”。限定条件越清晰,结论越稳健。
建议在报告中使用以下表述模板:
- “基于 A 数据集,覆盖 B 区域,使用 C 指标,某年 7 月达到历史第 1 位。”
- “历史序列长度为 N 年,站点有效记录比例超过 90%。”
- “干旱指数采用 SPEI-1,负值代表气象干旱。”
这样即使数据源后续更新,也能定位是数据问题还是计算问题。
8. 扩展方向:从月尺度到事件归因
8.1 聚焦热浪事件
“7 月均温破纪录”是月尺度统计,而热浪事件研究通常关注连续高温日数、热浪频次、热浪强度。两者可以结合:某年 7 月均温很高,可能是若干天极端高温拉高的结果,也可能是整月持续偏高。如果只看月均值,会掩盖热浪过程的细节。
常用指标包括:
- 连续高温日数:最高气温连续超过阈值的天数。
- 热浪强度:超过阈值部分与持续时间的累积。
- 热浪频次:某年夏季出现热浪过程的次数。
- 热浪覆盖面积:高温格点占区域总面积的比例。
8.2 从统计异常走向机制分析
统计异常回答“是否破纪录”,机制分析回答“为什么破纪录”。后者通常需要看大气环流背景,例如副热带高压位置、急流形态、海温异常等。这部分涉及更多再分析数据,比如位势高度场、海平面气压场、海表温度,分析方法也从时间序列走向物理过程诊断。
对于初学者,不建议一上来直接做归因。先完成“观测事实”层面的分析,把气温、降水、干旱指数的时间序列和空间分布做扎实,再引入环流背景检查。
8.3 给学习者的实践清单
如果要从零上手这类气候事件分析,可以按下面清单逐项完成。
- 下载一个站点 30 年以上的逐月气温和降水数据。
- 清洗缺失值并统一日期格式。
- 计算每年的 7 月平均值和相对基准期的距平。
- 计算历史百分位,标记是否破纪录。
- 下载或计算潜在蒸散序列,计算 SPEI-1。
- 绘制时间序列图、距平图和干旱指数图。
- 在报告里写清数据来源、基准期、缺失值处理和判定规则。
这套流程完成后,整个项目就能覆盖从数据到结论的完整链路,并且具备可复现性。月平均气温或降水距平只是气候描述的第一步,真正对“破纪录”结论负责的是阈值定义、数据版本和时空口径。一个可靠的气候异常分析脚本,必须让这三者都能回溯。后续遇到法国七月高温干旱或类似事件,直接按同一套方法分析即可,不需要重新设计框架。