scipy中的数学范围错误最小化

时间:2017-08-02 13:10:06

标签: python numpy scipy

我希望用最大似然法将六个参数拟合到非常难看的分布函数中。为此,我尝试使用scipy.optimize.minimize

这是一段代码

import math
form scipy.optimize import minimize
import numpy as np

#generate some data
xdata = np.random.lognormal(0,1,812)
#function for the Log likelihood
def mfpdf2(params):
    c1 = params[0]
    A1 = params[1]
    a1 = params[2]
    c2 = params[3]
    A2 = params[4]
    a2 = params[5]

    LL_vec = [math.log(c1*(math.exp(-A1*x) - math.exp(-a1*x))+c2*(math.exp(-A2*x) - math.exp(-a2*x))) for x in xdata]
    LL = -sum(LL_vec)


return LL

#try to find max likelihood (minimize negative loglikelihood)

start_params = [1,1,2,1,1,2]

pars = minimize(mfpdf2, start_params)

这段代码给我一个错误:

File "C:/Users/Robert/Desktop/python/pokusy_analyza_multiexp.py", line 79, in <listcomp>
LL_vec = [math.log(c1*(math.exp(-A1*x) - math.exp(-a1*x))+c2*(math.exp(-A2*x) - math.exp(-a2*x))) for x in xdata]

ValueError: math domain error

我做错了什么?

2 个答案:

答案 0 :(得分:1)

看起来你可能正在记录一个未定义的负数

答案 1 :(得分:0)

如果math.log为否定,x函数中的表达式将返回负数。负数的对数仅针对复数定义,因此常规日志函数将为您提供ValueError: math domain error。您可以将x_data转换为复数并使用np.log,如果这样可以从您的特定应用中获得minimize的可接受结果:

xdata = np.random.lognormal(0,1,812).astype(np.complex)
...
LL_vec = [np.log(c1*(math.exp(-A1*x) - m ...

或者您可以指定不同的最小化技术(并非所有支持定义的边界)并指定值的边界。这不支持A1 > a1之类的符号边界,因此您必须对变量进行重新排序才能建立关系:

from scipy.optimize import minimize
import numpy as np

xdata = np.random.lognormal(0,1,812)

def mfpdf2(params):

    c1 = params[0]
    A1 = params[1]
    offset1 = params[2] #this can obviously be condensed to one line
    a1 = A1 + offset1 #bound offset to be positive so a is always > A 
    c2 = params[3]
    A2 = params[4]
    offset2 = params[5]
    a2 = A2 + offset2

    LL_vec = [np.log(
                     c1*(np.exp(-A1*x) - np.exp(-a1*x))+
                     c2*(np.exp(-A2*x) - np.exp(-a2*x))
                 ) for x in xdata]
    #this will not account for the possibility that 
    #   one of c1*(...) or c2*(...) is negative but 
    #   the sum is still positive. This could conceiveably 
    #   be achieved with more ratios or offsets instead of 
    #   direct values, but would make the math real nasty.
    LL = -sum(LL_vec)
    return LL

start_params = [1,1,2,1,1,2]
pars = minimize(mfpdf2, 
                start_params, 
                method='L-BFGS-B',
                #now define your bounds
                bounds=((None, None), # c1
                        (None, None), # A1
                        (0, None),    # a1 - A1 (makes a1 always larger than A1)
                        (None, None), # c2
                        (None, None), # A2
                        (0, None)))   # a2