简介:面向气候与水文学研究者及干旱监测从业者,提供一套支持日、周、月多时间尺度、多分辨率与多种潜在蒸散发(PET)计算方式的SPEI指数Python计算代码。目前公开工具多局限于月分辨率,难以捕捉短期干旱,该资源补充了日分辨率SPEI的实现路径,参考Wang等(2015)与李军(2021)的研究思路,并配有测试数据与结果验证文件。包体共22个文件,约1.07MB,包括Python脚本、txt测试数据、csv结果文件及示意图,结构上分为输入数据、输出结果和核心工具模块,便于直接运行与二次开发。已有1532人学习/下载。使用该代码可计算不同时间尺度累积水平衡状况,进一步分析区域干湿趋势,并基于游程理论提取干旱事件,适合科研写作、业务监测及课程实验场景。
1. 干旱指标SPEI的计算代码与测试文件:难题不在标准化公式,而在前处理
SPEI(标准化降水蒸散指数)算起来比大多数人想得麻烦。麻烦不在那个Gamma分布拟合和正态标准化公式上,而是在它前面的两件事:用什么方式计算潜在蒸散PET,以及滑动累计窗口怎么跨过日/周/月三种时间分辨率。同一个降水温度序列,换一种PET算法,SPEI-1的极端干旱值可能差出0.3个标准差,这个数量级足以改变一场干旱事件的等级判定。标题里提到的这套计算代码与测试文件,核心目标就是把流程拆成可替换模块:时间尺度可设(日/周/月及其整数倍滑动累计)、气象数据分辨率可换(站点观测或网格再分析)、PET算法可选(Thornthwaite、Hargreaves、Penman-Monteith),最终输出标准化后的SPEI序列,用于干旱监测、作物受旱评估和历史旱情重建。适合谁用?水文气象方向的从业者、做生态与农业干旱评估的研究生,以及要批量处理几十个站点数据、不想反复调参的工程师。
2. 三种PET算法怎么选:数据门槛决定了你只能用哪个
2.1 公式差异与输入数据门槛:先看手里有什么要素
SPEI叫标准化降水蒸散指数,但真正把它和SPI区分开来的,是PET那一项。而PET的计算方式直接受制于气象观测要素,没有哪种算法是万能的。我一般先做一道选择题:手上有哪些连续的气象变量,再反过来定算法,而不是先定算法再找数据。
Thornthwaite法是门槛最低的,只需要平均气温与纬度,用一个基于温度的经验公式估算月潜在蒸散。它内部依赖一个温度有效指数和日长校正,本质是温度高则蒸散大的统计关系。在水分不紧缺的湿润区,它的偏差尚可接受;但在干旱半干旱区,夏季辐射强、风速大,实际蒸散会远超这个温度外推值,容易系统性低估干旱强度。
Hargreaves法需要最高温、最低温和大气顶层辐射,其中辐射项可以由纬度和日序推算,不需要实测辐射。它引入了温差(最高温减最低温)来间接表达云量和湿度影响,比Thornthwaite对干旱区更敏感。但它的短板也很明确:沿海强风区域、受平流雾影响的区域,温差信号钝化,结果会出现季节性偏移。
Penman-Monteith法的理论最完整,把净辐射、空气动力学项、饱和水汽压差、风速全部算进去,是FAO推荐的标准方法。问题是它同时需要气温、湿度、风速、太阳辐射四个要素,站点稍有缺测就得插补,插补本身又引入新的不确定性。对大多数只有降水与温度资料的站点来说,直接用Penman-Monteith并不现实。
表格把这三种PET方法按数据门槛、典型问题和适用场景放在一起,方便做取舍:
| PET方法 | 最低输入要素 | 经验/机理属性 | 典型误差方向 | 适合的数据条件 |
|---|---|---|---|---|
| Thornthwaite | 月均温、纬度 | 温度经验式 | 干旱区夏季低估蒸散 | 只有气温的站点或历史重建 |
| Hargreaves | 最高温、最低温、纬度 | 温差+辐射经验式 | 沿海湿润区高估蒸散 | 有日最高最低温的常规气象站 |
| Penman-Monteith | 气温、湿度、风速、辐射 | 物理机理模型 | 要素齐备时误差最小 | 完整气象站或再分析网格 |
所以标题里写「多PET计算方式」,不是让用户盲目追求某个算法的理论完美,而是让同一套SPEI流程能在不同数据条件下跑通,并且能横向比较各算法对干旱结论的影响。
2.2 测试文件暴露的第一个问题:PET算法换来换去,结论会不会变
拿到标题里提到的测试数据后,第一步不要急着算SPEI,先做一次PET敏感性测试。常见做法是:用同一个降水温度输入序列,分别用Thornthwaite和Hargreaves算出PET,再各自计算SPEI-1和SPEI-3,对比干旱等级的月份分布。如果某个月份的SPEI值从-0.8变成-1.3,说明这个时段的干旱等级从「轻度」翻成了「中度」,那么PET算法的选择在你这个区域就是结论敏感的。
我处理某站点数据时遇到过类似情况:夏季月均温约24℃,Thornthwaite估算的月PET只有110mm左右,而用Hargreaves算出来超过160mm。对降水不足50mm的月份,两种算法下SPEI-1相差0.4以上,直接导致该站点夏季干旱频率统计明显不同。这属于正常现象,因为干旱半干旱区辐射对蒸散的贡献远超温度本身。
此时不要急着判断谁对谁错,而是看本地是否有蒸发皿实测或参考作物蒸散数据。如果有多年实测,把三种PET的月均值与实测对比,哪个偏差小就用哪个;如果什么都没有,建议以Hargreaves为默认下限、Thornthwaite为参考上限,在报告里同时披露。这样至少不会让结论被某个PET算法「绑架」。
3. 把SPEI计算拆成四步:输入清洗、累计序列、Gamma拟合与标准化
3.1 数据处理第一步:把日/周/月降水与温度对齐到同一时间轴
SPEI的输入不复杂,但格式很讲究。降水量和平均气温两个变量必须落在同一个时间轴上,且时间轴必须等间距。拿到原始测试文件后,我通常先打印数据框的前五行和列名,确认降水单位是毫米、温度单位是摄氏度。如果降水是0.1mm精度、温度是0.1℃精度,也要一并确认,因为单位不一致会让PET直接偏离一个量级。
常见的原始数据是逐日观测,但标题里明确提到日/周/月三种尺度,所以第一步就是把逐日数据重采样成目标时间分辨率。需要注意一个细节:周尺度分成两种理解,一种是日历周(按ISO周一开始),另一种是任意起点起算的7日滑动块。SPEI的周尺度在农业干旱监测里常用「播种起算的连续7日」,而不是自然周,否则同一个星期会被截成两个累计窗口。
import pandas as pd # df_raw 至少包含 date、precip、tmean 三列 df_raw['date'] = pd.to_datetime(df_raw['date']) df_raw = df_raw.sort_values('date').set_index('date') # 重采样为日尺度:确保没有重复日期 daily = df_raw[~df_raw.index.duplicated(keep='first')] # 周尺度:按自然周聚合(周一起始) weekly_calendar = daily.resample('W-MON').agg({'precip': 'sum', 'tmean': 'mean'}) # 周尺度:若需要滑动7日窗口,则保留日数据,后续用 rolling 处理 weekly_sliding = daily.rolling('7D').agg({'precip': 'sum', 'tmean': 'mean'}) # 月尺度:按自然月聚合 monthly = daily.resample('MS').agg({'precip': 'sum', 'tmean': 'mean'})这里的逻辑是:降水和温度重采样规则不同,降水是区间累计量,所以用sum;温度是时点平均量,所以用mean。重采样之前必须去重日期索引,否则resample会报错。月尺度用MS作为起点,保证每个月的标签都是当月1号,后续做滑动窗口时对齐更直观。
参数说明:W-MON表示把每周的起点对齐到周一,如果原始数据是周日开始,改成W-SUN。如果数据源已经是月尺度,就不需要再重采样,直接跳过这一步。日尺度数据不需要重采样,但需要检查是否有一天多条记录的情况。
3.2 累积亏欠序列与时间尺度:滚动窗口为什么不能直接rolling
SPEI的核心变量不是降水本身,而是降水减去PET后的水分亏欠量,记为D序列。D_i = P_i - PET_i。这个序列可以是日尺度、周尺度或月尺度。然后根据目标时间尺度做滑动累计。比如SPEI-3在月尺度上就是过去3个月的累积亏欠量,SPEI-12就是过去12个月的累积量。
这里有个容易踩的坑:有些人直接调用rolling(window=3).sum()然后就算完了,但没有考虑窗口长度对序列有效长度的压缩。前3个值中第1个只有一个样本参与累计,第2个只有两个样本,这样拟合Gamma时的分布形状会发生偏移。处理办法是使用min_periods=1让窗口不满时也输出累计值,但后续计算时要意识到前几个样本的标准差天然偏小。
import numpy as np def build_accum_series(precip, pet, scale, freq='M'): """ 构建多时间尺度的累积亏欠序列。 precip/pet: pandas Series,索引为时间,值为毫米/月 scale: 时间尺度,月尺度下 scale=3 表示 SPEI-3 """ d = precip - pet # 水分亏欠序列 # 用滚动求和构建累计序列 accum = d.rolling(window=scale, min_periods=1).sum() # 记录有效样本量:每个时间点的参与月数 valid_count = d.rolling(window=scale, min_periods=1).count() # 丢弃样本量过小的起始段,比如累计月数不足 scale 一半的记录 accum = accum[valid_count >= max(2, scale // 2)] return accum滚动窗口的方向也很重要。默认rolling是向前取过去N个值,这是SPEI的标准做法。如果你不小心用了shift(-scale)再rolling,就会把未来数据混进当前累计值,导致整个SPEI序列在时间上超前,干旱事件判定全部错位。检查方法很简单:打印accum的前五个值,确认第一个非NaN值对应的时间点之后,指标值是否随时间往右推移。
另外要意识到:多时间尺度的含义不仅是一个scale参数。不同尺度的累积序列在统计性质上差异巨大。月尺度SPEI-1基本是白噪声性质,而SPEI-12有强烈的自相关,Gamma分布的形状参数完全不同。所以计算多尺度SPEI时,必须对每个scale分别拟合分布参数,不能共用一个。
3.3 Gamma分布的拟合与SPEI值计算:零值处理是核心
SPEI的标准化分两步:第一步对累积亏欠量序列拟合概率分布,第二步把累积概率映射到标准正态分位数上。传统SPEI假定D序列服从Gamma分布,这个假定在大多数气候区是适用的,但在零值比例很高的区域会出问题。以月尺度为例,如果目标区域有3个月以上的降水量接近零,那么D序列会出现大量0值,Gamma分布的极大似然拟合会直接失败或拟合出极端参数。
常见处理策略有两种:一种是所有D值整体平移一个常数,把最小值移到略大于0的地方再拟合Gamma;另一种是把0值和非0值分开建模,用「零值概率 + 非零值Gamma分布」的混合分布来求累积概率。我在代码里更倾向于第二种,因为它的理论一致性更好,而且在干旱区表现明显比平移法稳定。
import numpy as np from scipy import stats def spei_from_accum(accum, scale_label): # 去掉缺失值 x = accum.dropna().values n = len(x) # 统计零值比例 p0 = np.mean(x == 0) # 非零值序列 x_pos = x[x > 0] if len(x_pos) < 10: # 样本太少,退化为正态标准化 mu, std = np.mean(x), np.std(x, ddof=1) return (x - mu) / std # 对非零正值拟合Gamma分布(概率加权矩法更稳) fit_a, fit_loc, fit_scale = stats.gamma.fit(x_pos, method='MM') # 构造混合累积分布 def mixed_cdf(x_val): if x_val <= 0: return 0.0 return p0 + (1 - p0) * stats.gamma.cdf(x_val, fit_a, loc=fit_loc, scale=fit_scale) # 累积概率映射到标准正态分位数 probs = np.array([mixed_cdf(v) for v in x]) # 限制在开区间内,避免0和1取不到分位数 probs = np.clip(probs, 1e-6, 1 - 1e-6) spei = stats.norm.ppf(probs) return spei这段代码要说明几点。第一,stats.gamma.fit用method='MM'即矩估计,而不是默认极大似然,因为极大似然在样本量小于30时经常给出离谱的形状参数,矩估计虽然损失一点效率,但稳定性好得多。第二,零值概率p0直接作为累积分布函数的跳跃点,这样即使原始数据有20%的零值,标准化后SPEI的分布仍然接近正态,不会出现大量SPEI值堆积在-0.5附近的情况。第三,clip处理很重要,因为当概率接近1e-7时,标准正态分位数会冲到±5以上,这在干旱指数里属于异常值,通常需要裁剪。
拟合完成后,一个完整的SPEI序列就出来了。需要强调的是,这个序列是把全部历史数据一次性拟合,还是逐年滚动拟合,对结果影响很大。如果样本期30年以上,一次性拟合即可;如果只有10年,建议至少每年更新一次分布参数,否则后期年份的SPEI会产生趋势性偏差。
4. 代码落地:多尺度多分辨率多PET的配置化实现
4.1 主控参数:让时间尺度、分辨率、PET算法可切换
实际工程里不可能每换一个站点、每换一个尺度就改一遍代码。常见做法是把整个SPEI计算流程做成一个配置文件加一个主函数。配置项至少包含:输入文件路径、输出文件路径、时间分辨率(daily/weekly/monthly)、PET算法标识、尺度列表(如[1,3,6,12])、分布拟合方法、零值处理开关。
测试文件的价值在这里就体现出来了:拿一套已知的降水温度数据,跑通配置流程后输出SPEI序列,再用另一套测试数据验证配置是否被正确解析。我曾经把气象站点从月数据换成日数据之后,忘了把precip的聚合方式从sum改成sum(降水不变)但tmean的聚合从mean被误写成sum,结果PET全部放大24倍,SPEI几乎全变成极端干旱。这类问题只有靠格式明确的测试文件才能快速暴露。
下面是一个最小主控配置的示例:
config = { "input_file": "test_data/station_001.csv", "time_resolution": "M", # D=日, W=周, M=月 "pet_method": "hargreaves", # thornthwaite / hargreaves / penman "scales": [1, 3, 6, 12], # 多个时间尺度 "precip_col": "precip", "tmean_col": "tmean", "tmax_col": "tmax", # hargreaves 需要 "tmin_col": "tmin", # hargreaves 需要 "lat": 37.5, # 站点纬度 "zero_handling": "mixed", # mixed=零值混合分布, shift=平移 "fit_method": "mm", # mm=矩估计, mle=极大似然 "output_file": "spei_output.csv" }实际运行流程是:读配置 → 读取数据 → 按time_resolution重采样 → 按pet_method计算PET序列 → 计算亏欠序列 → 对每个scale构建累计序列 → 依次拟合分布 → 输出多列SPEI值。主函数只需要循环遍历scales列表,其他逻辑与尺度无关。配置化带来的直接好处是:换站点只需改输入路径和纬度;换PET算法只需改一个字符串;要算SPEI-24,直接把24加进scales列表,其余不用动。
4.2 PET计算模块的三种实现与调度
计算PET时要把三个算法封装成统一的函数签名:输入参数只有DataFrame和必要的静态变量,输出一个与输入时间轴对齐的PET序列。
def pet_thornthwaite(temp_c, lat): """ Thornthwaite 月尺度PET估算。 temp_c: 月均温(℃);lat: 纬度(度) 返回月PET(mm/月) """ lat_rad = np.deg2rad(lat) # 计算日长校正因子:简化的年度平均日长 # 常见做法:用月序号估算,此处按年中位数处理 day_length_factor = 1 + 0.033 * np.cos(2 * np.pi * np.arange(12) / 12 - 0.5) # 温度指数 I = sum((max(t,0)/5)^1.514) t_pos = np.clip(temp_c, 0, None) I = np.sum((t_pos / 5) ** 1.514) if I == 0: return np.zeros_like(temp_c) # 指数 a = 6.75e-7 * I^3 - 7.71e-5 * I^2 + 1.79e-2 * I + 0.49 a = (6.75e-7 * I**3 - 7.71e-5 * I**2 + 1.79e-2 * I + 0.49) # 未校正PET(mm/月) pet_unadj = 16 * (10 * t_pos / I) ** a pet = pet_unadj * day_length_factor[:len(temp_c)] return pet def pet_hargreaves(tmax, tmin, tmean, doy, lat): """ Hargreaves 日尺度/月尺度PET估算。 doy: 年积日;lat: 纬度 """ # 大气顶层辐射 Ra 用 FAO 方程估算 # 这里简化为与纬度正弦相关的季节项 lat_rad = np.deg2rad(lat) solar_dec = 0.409 * np.sin(2 * np.pi * (doy - 81) / 365) omega = np.arccos(-np.tan(lat_rad) * np.tan(solar_dec)) Gsc = 0.0820 # 太阳常数 MJ/m2/min dr = 1 + 0.033 * np.cos(2 * np.pi * doy / 365) Ra = (24 * 60 / np.pi) * Gsc * dr * (omega * np.sin(lat_rad) * np.sin(solar_dec) + np.cos(lat_rad) * np.cos(solar_dec) * np.sin(omega)) # Hargreaves 公式 pet = 0.0023 * (tmax - tmin) ** 0.5 * (tmean + 17.8) * Ra return pet三个PET方法共用同一个调度函数,本质上是一个字典映射:
def compute_pet(df, method): if method == 'thornthwaite': return pet_thornthwaite(df['tmean'].values, config['lat']) elif method == 'hargreaves': # 需要按年积日计算 doy = df.index.dayofyear if hasattr(df.index, 'dayofyear') else np.arange(1, len(df)+1) % 365 + 1 return pet_hargreaves(df['tmax'].values, df['tmin'].values, df['tmean'].values, doy, config['lat']) elif method == 'penman': # 需要 ta, rh, ws, rs 四要素 return pet_penman(df['tmean'].values, df['rh'].values, df['ws'].values, df['rs'].values) else: raise ValueError(f"Unsupported PET method: {method}")需要注意的坑:Thornthwaite的标准形式是针对月尺度设计的,如果原始数据是日尺度,直接套用会导致数值严重偏离。常见做法是先按月聚合温度,算出月PET,再按当月天数拆回日均值。Hargreaves则是天然支持日尺度,不需要额外聚合。Penman-Monteith的日尺度最自然,但月尺度需要把各要素按平均值或累计太阳辐射分别处理。
多分辨率在这里的含义有两层。一层是时间分辨率(日/周/月),另一层是空间分辨率(站点与网格)。空间尺度的处理通常是在网格数据上逐格点运行上述代码,再输出格点场。格点数据和站点数据的差异主要是:格点数据的纬度是一个二维数组,每个格点有各自纬度,而PET计算里的纬度参数需要逐格点传递。
4.3 测试文件怎么用:从站点测试数据到批量网格数据
标题里给出测试文件,我一般会按三种级别来使用。第一级是单元测试:用一小段模拟数据,比如12个月降水与温度,分别计算三种PET,检查数值是否在合理范围(Thornthwaite在温带月PET应该大致落在20-200mm)。第二级是端到端测试:用完整的站点数据运行SPEI计算,检查输出列数、时间范围、缺失值比例。第三级是批量压力测试:把配置里的input_file换成一个包含几百个站点的目录,观察程序在多站点循环下是否会内存溢出或出现异常值。
import glob import pandas as pd station_files = sorted(glob.glob('stations/*.csv')) results = {} for f in station_files: station_id = f.split('/')[-1].replace('.csv', '') try: # 假设 load_and_calc 是前面封装的完整流程 spei_df = load_and_calc(f, config) results[station_id] = spei_df except Exception as e: print(f"[skip] {station_id}: {e}") continue # 检查失败站点是否有共性原因,比如缺 tmax 列执行时如果有一批站点报错,不要逐站debug。先把所有报错信息按错误类型聚类,通常就是几类:缺列、缺测、纬度越界(南极站点纬度-90导致arccos取值范围问题)、降水负值(某些再分析产品的网格降水有负修正值)。我见过一个再分析数据集的降水变量在极端干燥地区输出-0.01mm的负值,直接把D序列搞出异常尖峰,SPEI全部失真。清洗阶段的处理方式是将负降水统一截断为0,或者在读取时检查最小值再做修正。
5. 常见问题与排查:SPEI计算最容易翻车的6个坑
5.1 所有SPEI值都是正的,没有负值
现象:输出序列全部大于0,历史明显干旱时段也没有负值出现。原因通常是标准化方向搞反了。标准SPEI定义中,累积概率小于0.5时,标准正态分位数为负,表示偏干。有些代码在映射时把概率p传入norm.ppf(1-p),导致干湿反转。解决方法是在输出前检查一个已知干旱月的符号,如果符号与常识相反,把norm.ppf(p)改成norm.ppf(1-p)。
5.2 降水为零的月份多,Gamma拟合报错
现象:scipy抛出的RuntimeWarning,gamma.fit返回的形状参数为NaN或极大值,SPEI序列出现大段NaN。原因是零值比例过高时,标准Gamma拟合无法收敛。解决方法是启用零值混合分布处理,把p0单独估计,只对正值部分做Gamma拟合。注意不要在零值很多时还坚持纯Gamma模型,那是某种「坚持理论」的代价。
5.3 周尺度SPEI比月尺度抖动幅度大得多
现象:SPEI-1周序列在相邻周之间从-2跳到+1,极端波动频繁。原因不是计算错误,而是周尺度降水的离散度天然比月尺度大,Gamma分布对高频数据的拟合更容易受到单周强降水的影响。处理方式不是强行滤波,而是把周尺度的建议尺度从1提高到2或3,也就是优先使用SPEI-2周或SPEI-3周。如果一定要保留SPEI-1周,建议对降水序列先做7日滑动平均再算。
5.4 不同数据分辨率下SPEI结果不可比
现象:同一区域,用0.5°网格数据算出的SPEI-3和用站点观测算出的SPEI-3趋势大一致,但具体月份差异明显。原因在于网格降水是被面积平均过的,强度峰值低于站点值,导致网格数据的亏欠序列方差更小。这不是bug,而是分辨率本身的属性。遇到这种情况,不要把两种SPEI混合使用。要么统一到站点尺度,要么统一到某一网格尺度。在对比季节性或年际趋势时,可以分别标准化后比较秩相关,而不是直接比较SPEI数值。
5.5 数据长度不足时SPEI-12严重失真
现象:只有8年数据,强行算SPEI-12,得到的序列几乎全部落在±0.5之间,区分度很差。原因很简单:SPEI-12的累计窗口为12个月,8年数据只有约8个独立的年度累计值,任何分布拟合在这个样本量下都会将尾部压缩。解决方法有两个:一是至少要20年以上数据再计算SPEI-6以上尺度;二是如果数据只能覆盖10年,改用经验分布替代Gamma假设,把每个年份的百分位直接映射到正态分位数,虽然后者在线性外推上弱一些,但至少不造假。
5.6 PET算法换来换去,结果突变
现象:同一份测试文件,改成Hargreaves后SPEI-3序列整体抬升0.3左右,干旱等级判定变化。原因不是代码错误,而是PET算法之间的系统性偏差在干旱半干旱区可达40-80mm/月。排查步骤:先输出PET的月均值对比,若差异超过30%则PET算法是主要影响因素。如果数据要素齐全,优先采用Penman-Monteith;如果只有温湿资料,建议固定用Hargreaves作为标准配置,避免不同批次分析结果因为PET算法不同而不可比。
6. 进阶经验:历史期验证法与批量并行的小习惯
计算SPEI不难,难在让结果可靠到可以写进报告。我现在的习惯是:拿到新站点的SPEI结果后,第一件事不是画趋势图,而是找三个历史干旱事件做对照。比如某区域在历史上出现过明显的农业减产年,我会看那一年的SPEI-3月值是否低于-1,且持续月份是否与灾情报道吻合。如果连已知事件都对应不上,那一定是前处理出了问题,通常集中在温度单位、降水单位或者PET算法的选择上。
第二个习惯是批量计算时优先按站点并行,而不是按时间并行。因为每个站点的纬度、海拔和缺测模式都不同,按站点拆分后各进程独立读数据、算PET、拟合分布,互不干扰。我用multiprocessing的Pool把站点列表分给多个worker,每个worker只负责一组站点,内存占用稳定。网格数据则反过来,按时间块切分更合适,因为同一时刻所有格点的纬度数组是一样的。
第三个习惯是把标准化所用的分布参数保留下来。每次拟合Gamma得到形状参数和尺度参数后,不要直接丢掉。把它们按站点、按尺度、按拟合年份写入一个参数文件。这样后续如果新增了两年数据,可以快速判断是否需要重拟合分布,还是沿用历史参数。干旱指数最怕的就是每个月的分析结果之间参数不同,导致同一场干旱在不同时间发布的等级对不上。保留参数至少让你能用前后一致的基准回看。
第四个技巧是给输出加一个「统计不稳定」标记。当累计样本量不足30个、零值比例超过40%、或分布拟合收敛时迭代次数超标,我会在输出的SPEI列旁边增加一列quality_flag,值有0和1两种:0表示该值可靠,1表示仅供参考。这个标记花不了多少代码量,但在后续写报告、投稿、做决策时能省掉大量解释成本。
如果你正在尝试把SPEI计算从单个站点扩展到区域网格,或者在不同PET算法之间犹豫,建议先用本标题对应的测试文件把所有配置跑通,记录三种PET算法的输出差异,再决定正式的算法配置。让数据和代码先说话,比在文档里辩论哪个算法更合理要快得多。希望以上的计算流程、参数选择和踩坑记录能帮到你,省去那些我当年反复试错的时间。
本文还有配套的精品资源,点击获取