如何使用scipy.optimize求解Gumbel分布的参数

2 投票
1 回答
5555 浏览
提问于 2025-04-18 03:44

我计算了桥梁的负载,现在想用最大似然估计的方法,把Gumbel分布拟合到最高20%的负载上。我需要帮助来计算这个分布的参数。我看过scipy.optimize的文档,但不太明白怎么用里面的函数来估计两个参数的函数。

这里有一些理论,可能对你有帮助: 有两个似然函数(L1和L2),一个是针对超过某个阈值(x >= C)的值,另一个是针对低于这个阈值(x < C)的值。最有可能的参数就是这两个函数乘积的最大值,也就是max(L1*L2)。在这个情况下,L1仍然是概率密度函数在某些点xi的值的乘积,而L2则是超过阈值C的概率(1-F(C))。

这是我写的一些代码:

non_truncated_data = ([15.999737471905252, 16.105716234887431, 17.947809230275304, 16.147752064149291, 15.991427126788327, 16.687542227378565, 17.125139229445359, 19.39645340792385, 16.837044960487795, 15.804473320190725, 16.018569387471025, 16.600876724289019, 16.161306985203151, 17.338636901595873, 18.477371969176406, 17.897236722220281, 16.626465201654593, 16.196548622931672, 16.013794215070927, 16.30367884232831, 17.182106070966608, 18.984566931768452, 16.885737663740024, 16.088051117522948, 15.790480003140173, 18.160947973898388, 18.318158853376037])

threshold = 15.78581825859324

def maximum_likelihood_function(non_truncated_loads, threshold, loc, scale):
    """Calculates maximum likelihood function's value for given truncated data
    with given parameters.
    Maximum likelihood function for truncated data is L1 * L2. Where L1 is a 
    product of multiplication of pdf values at non-truncated known values
    (non_truncated_values). L2 is a the probability that threshold value will
    be exceeded.
    """
        is_first = True
    # calculates L1
    for x in non_truncated_loads:
        if is_first:
            L1 = gumbel_pdf(x, loc, scale)
            is_first = False
        else:
            L1 *= gumbel_pdf(x, loc, scale)

    # calculates L2
    cdf_at_threshold = gumbel_cdf(threshold, loc, scale)
    L2 = 1 - cdf_at_threshold

    return L1*L2

def gumbel_pdf(x, loc, scale):
    """Returns the value of Gumbel's pdf with parameters loc and scale at x .
    """
    # exponent
    e = math.exp(1)
    # substitute
    z = (x - loc)/scale

    return (1/scale) * (e**(-(z + (e**(-z)))))

def gumbel_cdf(x, loc, scale):
    """Returns the value of Gumbel's cdf with parameters loc and scale at x.
    """
    # exponent
    e = math.exp(1)

    return (e**(-e**(-(x-loc)/scale)))

1 个回答

1

首先,使用 scipy.optimize 来优化一个函数,最简单的方法是把目标函数构建成这样的形式:第一个参数是需要优化的参数列表,后面的参数可以是其他东西,比如数据和固定的参数。

其次,使用 numpy 提供的向量化功能会非常有帮助。

所以我们有这些内容:

In [61]:
#modified pdf and cdf
def gumbel_pdf(x, loc, scale):
    """Returns the value of Gumbel's pdf with parameters loc and scale at x .
    """
    # substitute
    z = (x - loc)/scale
 
    return (1./scale) * (np.exp(-(z + (np.exp(-z)))))
 
def gumbel_cdf(x, loc, scale):
    """Returns the value of Gumbel's cdf with parameters loc and scale at x.
    """
    return np.exp(-np.exp(-(x-loc)/scale))
In [62]:

def trunc_GBL(p, x):
    threshold=p[0]
    loc=p[1]
    scale=p[2]
    x1=x[x<threshold]
    nx2=len(x[x>=threshold])
    L1=(-np.log((gumbel_pdf(x1, loc, scale)/scale))).sum()
    L2=(-np.log(1-gumbel_cdf(threshold, loc, scale)))*nx2
    #print x1, nx2, L1, L2
    return L1+L2
In [63]:

import scipy.optimize as so
In [64]:
#first we make a simple Gumbel fit
so.fmin(lambda p, x: (-np.log(gumbel_pdf(x, p[0], p[1]))).sum(), [0.5,0.5], args=(np.array(non_truncated_data),))
Optimization terminated successfully.
         Current function value: 35.401255
         Iterations: 70
         Function evaluations: 133
Out[64]:
array([ 16.47028986,   0.72449091])
In [65]:
#then we use the result as starting value for your truncated Gumbel fit
so.fmin(trunc_GBL, [17, 16.47028986,   0.72449091],  args=(np.array(non_truncated_data),))
Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 25
         Function evaluations: 94
Out[65]:
array([ 13.41111111,  16.65329308,   0.79694   ])

trunc_GBL 函数中,我把你的概率密度函数(pdf)替换成了一个缩放后的概率密度函数。

enter image description here

这里的原因是因为你的 L1 是基于概率密度函数的,而 L2 是基于累积分布函数的:http://support.sas.com/documentation/cdl/en/statug/63033/HTML/default/viewer.htm#statug_lifereg_sect018.htm

然后我们注意到一个问题,在最后的输出中看到: Current function value: 0.000000。负对数似然函数的值为0。

这是因为:

In [66]:

gumbel_cdf(13.41111111,  16.47028986,   0.72449091)
Out[66]:
2.3923515777163676e-30

实际上是0。这意味着根据你刚刚描述的模型,当阈值足够低时,最大值总是会达到,这样 L1 就不存在(x < threshold 是空的),而 L2 是1(1-F(C)1,对于你数据中的所有项)。

因此,我觉得你的模型看起来不太对。你可能需要重新考虑一下。

编辑

我们可以进一步将 threshold 单独提出来,视为一个固定参数:

def trunc_GBL(p, x, threshold):
    loc=p[0]
    scale=p[1]
    x1=x[x<threshold]
    nx2=len(x[x>=threshold])
    L1=(-np.log((gumbel_pdf(x1, loc, scale)/scale))).sum()
    L2=(-np.log(1-gumbel_cdf(threshold, loc, scale)))*nx2
    #print x1, nx2, L1, L2
    return L1+L2

然后以不同的方式调用优化器:

so.fmin(trunc_GBL, [0.5, 0.5], args=(X, np.percentile(X, 20)))
Optimization terminated successfully.
         Current function value: 20.412818
         Iterations: 72
         Function evaluations: 136
Out[9]:
array([ 16.34594943,   0.45253201])

这样,如果你想要70%的分位数,你只需将其更改为 np.percentile(X, 30),依此类推。 np.percentile() 只是另一种实现 .quantile(0.8) 的方式。

撰写回答