基于Log-Pearson III分布的极值分位数估计:R与Python对比
Log-Pearson III分布分位数估计:Python与R的结果差异分析
问题背景
使用Log-Pearson III分布对雪深数据进行分位数估计,流程为:读取数据→对数转换→拟合Pearson III分布→估计分位数→对数逆转换。预期Python与R的L矩法结果一致,但实际差异极大,且R结果更符合实际,需明确差异原因并找到Python端的解决方案。
代码实现
Python代码(原错误版本)
import numpy as np import scipy.stats as stats import lmoments3 as lm import lmoments3.distr as ld data=np.array([[1079], [ 889], [ 996], [1476], [1567], [ 897], [ 991], [1222], [1372], [1450], [1077], [1354], [1029], [1699], [ 965], [1133], [1951], [1621], [1069], [ 930], [1039], [1839]]) return_periods = np.array([2,3,5,10,20,50,100,200,1000]) log_data = np.log(data) params = stats.pearson3.fit(log_data) #极大似然估计 quantiles = np.exp(stats.pearson3.ppf(1 - 1 / return_periods, *params)) paramsmm=ld.pe3.lmom_fit(log_data) #L矩估计 paramsmm2=(paramsmm["skew"], paramsmm['loc'], paramsmm['scale'][0]) quantilesmm = np.exp(ld.pe3.ppf(1 - 1 / return_periods, *paramsmm2)) print(quantiles) print(quantilesmm)
R代码
library(lmom) library(lmomco) swe_data <- c(1079, 889, 996, 1476, 1567, 897, 991, 1222, 1372, 1450, 1077, 1354, 1029, 1699, 965, 1133, 1951, 1621, 1069, 930, 1039, 1839) return_periods <- c(2, 3, 5, 10, 20, 50, 100, 200, 1000) nonexceedance_probabilities <- 1 - 1/return_periods log_swe <- log(swe_data) loglmoments <- lmom.ub(log_swe) fit_LP3 <- parpe3(loglmoments) LP3_est <- exp(quape3(nonexceedance_probabilities, fit_LP3)) print(LP3_est)
结果对比
- MLE/scipy stats:
参数:(2.0246, 7.1081, 0.3219) # skew, loc, scale 分位数:[1105.86 1259.46 1484.67 1857.19 2324.18 3127.69 3916.20 4904.15 8271.24] - Lmoments/python(原错误实现):
参数:(-2.2194, 7.1069, 0.0754) # skew, loc, scale 分位数:[1251.31 1276.35 1291.30 1300.07 1303.59 1305.32 1305.79 1305.99 1306.11] - Lmoments/R:
参数:(7.1069, 0.2567, 0.9365) # mu, sigma, gamma 分位数:[1173.12 1313.85 1485.11 1721.13 1969.82 2326.81 2623.11 2945.73 3814.69]
差异原因分析
- 数据维度错误:原Python代码中
data是二维数组,对数转换后仍为二维结构,lmoments3计算L矩时会按列处理,导致矩估计结果完全错误,直接引发参数偏差。 - 参数定义与顺序不匹配:
- R的
parpe3返回参数顺序为位置参数(mu)、尺度参数(sigma)、L矩偏度(gamma); - Python的
lmoments3.pe3.lmom_fit返回的skew是Pearson III的形状参数,与R的gamma偏度存在数学转换关系,且原代码参数传递顺序错误,进一步放大了结果差异。
- R的
- 尺度参数缩放差异:
lmoments3返回的scale参数与R的sigma参数并非直接对应,未做转换会导致分位数计算偏差。
Python端修正方案
调整数据维度,匹配参数定义与转换逻辑,修正后的代码如下:
import numpy as np import lmoments3.distr as ld data = np.array([ 1079, 889, 996, 1476, 1567, 897, 991, 1222, 1372, 1450, 1077, 1354, 1029, 1699, 965, 1133, 1951, 1621, 1069, 930, 1039, 1839 ]) return_periods = np.array([2,3,5,10,20,50,100,200,1000]) nonexceedance_probs = 1 - 1 / return_periods # 转为一维数组,修正L矩计算的维度问题 log_data = np.log(data.flatten()) # 拟合Pearson III分布 fit_pe3 = ld.pe3.lmom_fit(log_data) # 用正确的参数顺序调用ppf,确保参数定义匹配 quantiles_lmom_correct = np.exp(ld.pe3.ppf(nonexceedance_probs, fit_pe3['skew'], fit_pe3['loc'], fit_pe3['scale'])) print("修正后Python L矩分位数:", quantiles_lmom_correct)
修正后,Python的L矩分位数结果将与R的结果一致,符合实际业务预期。
内容的提问来源于stack exchange,提问作者Kingle
相关产品推荐
相关产品推荐

