
每次看到有人拿着gma算SPEI代码跑通了但结果一片NaN或者在群里追问“为什么我的SPEI和R包算出来对不上”我基本都能猜到问题出在哪——十有八九是Distribution和FitMethod这两个参数随手填了个默认值甚至压根没管它们。这两个参数看起来不起眼实际上决定了整个SPEI序列的形态和可靠性。我过去为这事踩过不少坑也专门把gma里这两组参数的所有常见组合翻来覆去试过这篇就把背后的原理和选参逻辑一次性说清楚。SPEI标准化降水蒸散指数是干旱监测里用得最多的指标之一。它用降水减潜在蒸散描述水分盈亏再经过概率标准化让不同气候区的干旱程度能直接相互比较。gma是国产的地理空间与气候分析Python库内置了SPEI等气候指数计算函数把从潜在蒸散到标准化的整条链路都封装好了。封装是好事但如果你不了解它内部做了什么一旦参数选错排查成本比自己手写还高。这篇文章适合用gma做干旱分析的水文、气象、农业遥感方向的研究生和工程师我把计算链路拆开讲清楚再给出可以直接抄作业的参数组合。1. 先把SPEI的计算链路拆开1.1 一条五步链路缺一步都不行SPEI的计算流程网上能搜到很多版本但核心其实是一条五步链路。第一步是计算潜在蒸散量PET常用Thornthwaite方法只需要月平均气温或Hargreaves方法需要最高、最低温甚至辐射数据。PET代表的含义是“在给定气象条件下大气理论上能蒸发多少水”。第二步是计算水分盈亏D公式很简单D P - PET。D大于0表示当月偏湿润D小于0表示当月偏干旱。第三步是把D按时间尺度累积比如我们要算12个月尺度的SPEI就是把当前月和之前11个月的D加总这个滑动累积操作决定了指数反映的是短期干旱还是长期干旱。第四步用概率分布去拟合历史D序列得到每个值对应的累积概率。第五步把累积概率逆变换成标准正态分布的值就是最终的SPEI正值代表湿润负值代表干旱。前三步本质上是算术问题同一个数据集谁算结果都不会差太多。第五步是查表或调用函数也不容易出错。真正有“模型选择自由度”的是第四步。也就是说SPEI结果的可靠性很大程度取决于第四步里你选了什么样的分布曲线、用了什么方法去确定曲线参数。1.2 为什么“分布加拟合方法”最容易翻车很多初学者以为SPEI就是个带参数的现成函数输入降水气温输出指数值。但只要把第四步放大看就知道这里藏着两个独立决策选哪条分布曲线来描述D序列的频率分布以及用哪种统计方法去估这条曲线的具体参数。这两个决策分别对应gma里的Distribution和FitMethod。它们必须匹配选错其中一个结果就废。我见过一个很典型的翻车场景有人在gma里把Distribution设成“Gamma”然后跑出来的SPEI几乎全部是NaN。原因是Gamma分布的概率密度函数只在正数区间有定义而SPEI的D序列大量出现负值负值根本没法代入Gamma分布。这种错法本质上就是把SPEI按SPI的思路算了。SPI处理的是累积降水降水恒为非负用Gamma没问题但SPEI处理的是“降水减蒸散”天然有正有负这时候还照着SPI的老路走就会撞在支撑集这个硬约束上。另一个容易翻车的是FitMethod。如果你选了MLE极大似然估计而数据又比较短、零值比例又高数值迭代很可能不收敛程序也不一定中断只是默默返回NaN或者给出一组明显不合理的拟合参数。很多人看到NaN第一反应是“数据有问题”其实是拟合方法没选对。这就是为什么要单独把这两个参数拎出来说。2. Distribution参数避坑别让SPEI算成SPI2.1 SPEI的“原配”是log-logistic分布这个结论不是我拍脑袋给的而是SPEI提出者Vicente-Serrano等人在2010年的原始文献里明确采用的三参数log-logistic分布也就是对数逻辑斯蒂分布。为什么偏偏是它关键就在于D序列的正负双性。log-logistic分布定义在整个实数轴上对负值和正值都友好而且它的形状正好能描述D序列常见的“中间集中、两端偏厚”的分布特征。相比之下Gamma分布支撑集只在正数区间理论上就处理不了负值如果你强行用某些实现会把负值截断成0或者让你给D序列做整体平移无论哪种都会扭曲数据本来的分布形状最后得到的SPEI就不再是水分盈亏的真实标度。gumbel分布耿贝尔分布也在实数轴上有定义所以它能跑出结果不会像Gamma那样直接报错。但gumbel并不是SPEI文献的标准选择用它拟合出的尾部概率和log-logistic有明显差异对极端干旱年份的判级容易产生系统性偏移。我在实际对比中看到的情况是gumbel和log-logistic算出的SPEI在-1.5以下的极端段经常偏差0.3甚至0.5个等级别小看这个偏差它足以把“重度干旱”降级成“中度干旱”。2.2 选错分布对结果的影响有多大用实际案例来感受一下。假设某站有20年逐月数据共240个月计算12个月尺度的SPEI。用log-logistic分布得到的SPEI序列均值在0附近标准差在1附近符合标准正态分布的统计性质。换成gumbel分布后整体均值可能偏到0.2以上最极端月份甚至偏了0.6个SPEI单位。如果你拿着这个结果去评估历史干旱事件结论可能完全不同。再极端一点如果选了Gamma分布要看gma内部怎么处理负值。有的版本会在负值处返回NaN于是你得到一整片缺失值有的版本会截断负值那么D序列的分布整体右移负的SPEI会被系统性“拉回”干旱区域全部失真。无论哪种结果都不能用于论文或业务分析。我自己的习惯是在算完SPEI之后会顺手画一张D序列的直方图叠加log-logistic的拟合曲线肉眼确认一下尾部是否贴合。如果尾部明显高估或低估说明分布可能不适合这个站点的数据特征。这一步用matplotlib几十行代码就能完成但能帮你省下大量“后面才发现的头痛问题”。3. FitMethod参数避坑MLE不是万能的3.1 三种参数估计方法的适用条件gma里FitMethod常见选项包括MLE极大似然估计、PWM概率加权矩估计也称L-矩估计和MoM普通矩估计三种。三者思路完全不同适用场景也不同。极大似然估计MLE是统计里通用性最强的估计方法很多分布都能用它估参数理论效率高。但对log-logistic这种三参数分布MLE需要数值迭代求极大值对初始值敏感。如果你的数据长度短或者包含大量重复值比如干旱区很多月份降水为0D序列堆积在某个值附近似然面会变得不平滑优化器很容易停在错误的位置表现就是不收敛、返回NaN或者给出一组肉眼可见不合理的参数。概率加权矩估计PWM的思路不一样。它先算低阶概率权重矩再通过显式公式解出分布参数。log-logistic分布的参数和L-矩之间恰好在数学上有漂亮的闭式关系这意味着不需要迭代稳定性高对异常值和小样本也更友好。SPEI领域的经典实现包括原作者的Fortran代码和后来R里的SPEI包默认参数估计方式都和PWM一脉相承。普通矩估计MoM在概念上最简单用样本均值和样本方差去匹配分布的理论矩。但问题是高阶矩对样本极值非常敏感而SPEI恰恰关心尾部极端干旱用普通矩估计很容易让分布尾部失真所以我不建议在SPEI计算里用它。3.2 怎么判断FitMethod选错了选错FitMethod的症状有时候很隐蔽并不是每次都像Gamma分布那样直接给你一片NaN。我总结过几个典型场景。场景一代码运行没报任何错结果也出来了但SPEI序列中间有一段或整体全是NaN。这种时候优先怀疑FitMethodMLE在某些样本上迭代失败gma没有中断程序只是返回缺失值。场景二结果没有NaN但SPEI序列几乎贴着0波动标准差只有0.3左右。这说明拟合参数有偏概率没能拉开整个序列的区分度极低拿去画图就是一条平淡的线跟实际干旱灾情完全对不上。场景三同样一份数据R的SPEI包算出来和gma差很多。R那个包里默认的参数估计方式与PWM同源而你在gma里如果用了MLE两边对参数的估计思路根本不同结果当然不同而且差异往往集中在极端尾部。遇到以上任何一个场景我的第一反应都是把FitMethod切到PWM重跑一遍。但要注意MLE也不是一无是处如果你的样本足够长比如300个月以上且数据质量好MLE也能给出理想结果。可对气候指数这种“稳健优先”的分析场景PWM明显更省心。4. 完整实操从数据准备到结果验证4.1 数据准备和gma调用实操从数据准备开始。先把逐月数据整理成包含时间、降水、平均气温或直接算好的PET列的表格。gma里SPEI的输入数据格式一般要求每一行是一个月列名最好规范比如“Year”“Month”“Prec”“Tem”避免后续参数识别出错。如果你没有气温数据需要先算PETgma里也有对应的气候函数也可以自己按Thornthwaite方法算好再作为独立列传入。PET算错后面所有环节全部白搭。调用代码大概是这样的import gma from gma import climates # df: 每一行是一个月至少包含 年、月、降水、平均气温或PET # 计算12个月尺度SPEI spei12 climates.SPEI( df, Scale12, DistributionLog-Logistic, FitMethodPWM, ) print(spei12.tail())跑之前我强烈建议先执行一次help(climates.SPEI)把参数说明完整看一遍。因为不同版本的gma在参数命名上可能略有变化比如大小写、短横线连接方式、或者别名。我看过网上有些老教程给的参数和当前版本对不上照抄必然报错。先看帮助文档就是五分钟的事能省下一个小时的排查时间。4.2 三步验证法怎么确定算对了算完SPEI不代表事情结束了验证结果是否合理我一般做三步检查。第一步和R的SPEI包做交叉验证。同一份数据gma用Log-Logistic PWMR里用spei()函数跑同尺度两者的结果应该在高精度下几乎一致差异在0.001级别属于正常舍入误差如果差异超过0.1说明某个环节的思路偏差了停下来排查。第二步统计特性自查。SPEI是标准正态变换的产物所以每次拿到结果我先看序列均值和标准差。均值应该在0附近正负0.05以内标准差应该在1附近0.9到1.1之间。如果标准差只有0.5或者直接飙到1.8那结果十有八九不能用于分析。第三步历史干旱事件回查。找本地历史上确有大旱记录的年份看SPEI在那个时段是否出现明显负值。12个月尺度下一个真实发生的严重干旱年份SPEI至少应该小于-1.5。如果完全对不上就要从头检查PET计算、时间索引是否对齐、数据有没有缺测。这三步走完我才敢把SPEI结果用于后续的制图或建模。见过太多人算完直接出图图上看着像模像样实际全是系统偏差后面写结论才发现问题返工成本高得多。5. 常见问题排查实录与最终选参建议5.1 常见问题速查表把过去遇到的实际问题整理成一张速查表按“现象、最可能原因、解决办法”来对照排查效率会高很多。现象最可能原因解决办法SPEI结果全是NaNDistribution用了Gamma等不支持负值的分布换成Log-Logistic结果与R包差异大Distribution或FitMethod与标准不一致统一Log-Logistic PWM代码不收敛或报警告FitMethod用了MLE且样本太短换成PWM检查样本长度均值偏离0太多PET计算错误或数据时间索引错位排查PET检查时间序列标准差远偏离1分布或拟合方法选错或数据缺测严重先查PET和缺测再换参数数值剧烈跳变数据缺测导致累积序列错位补齐或插值保持连续月份这里补充一个容易忽略的细节gma对缺测月份容忍度有限缺测会让累积序列错位。比如你算12个月尺度中间断了一个月滑动累积里可能混入了跨越多年的数据。所以进入SPEI计算前先把DataFrame按时间排好缺值尽量补齐至少保证时间索引不断开。5.2 不同场景下的最终选参建议如果你只想记住一组参数组合那我建议所有常规场景都用DistributionLog-Logistic配合FitMethodPWM这是最稳的起点。在此基础上不同数据场景再做微调。常规气象站点数据样本长度30年以上分布和拟合方法都按这个组合基本不会出问题。大范围网格化数据比如几十万个格点逐月计算算法上同样用这组参数但建议先随机抽5到10个代表性格点跑一遍确认没有NaN、统计特性正常再全量计算。网格化数据一旦批量算完才发现错浪费的算力就不是小事了。数据比较短的时候比如只有20个月逐月记录任何分布拟合都很勉强因为样本量不足以支撑三参数分布的可靠估计强行算SPEI的置信度很低。我建议至少保证30个月以上再做。如果数据在极端干旱区零值比例很高log-logistic加PWM仍然是最优选择若拟合后仍大面积NaN考虑缩短时间尺度比如从12个月降到6个月或3个月让D序列的分布形态不那么极端。最后再分享一个我自己的习惯。拿到新数据我从来不会直接跑SPEI而是先画D序列的直方图和经验累积分布曲线再叠上我准备用的理论分布曲线。这一步看着简陋却能在一分钟内帮我发现“分布根本不适合”这样的大问题。gma把参数封装得很友好但那条拟合曲线画得合不合理最终要靠自己的判断去兜底。参数选对只是开始验证才是让你始终不翻车的那道保险。