如何通过一系列嵌套函数传递参数来计算结果?

时间:2017-03-03 03:07:17

标签: python-3.x scipy arguments parameter-passing minimization

我的问题很快,但我提供了大量的代码来更好地说明我的问题,因为我还没有从阅读相关帖子中理解答案。

下面的代码是选择属于args列表的优化参数。 args列表应该是单个条目like x0 on scipy docs。我希望找到正确的args组合以最好地拟合数据。 scipy优化模块应该使我的args的值波动,以找到最小化我的错误的组合。但是,我无法将args从一个函数传递到另一个函数。

有时我会设置***,但我的成功率更高,而不是命中率。我想知道如何将args从一个函数传递到另一个函数,同时允许它们更改值以便找到它们的优化值。 (优化值可减少误差,如下所述)。我有一些函数作为其他函数的输入,在这里缺少一个关键概念。这样的事情需要kwargs吗?如果args是一个元组,它们是否仍然可以更改值以查找优化参数?我知道这里有一些类似的问题,但我还没有能够用这些资源来解决这个问题。

下面解释了代码(导入后)。

import numpy as np
import random
import matplotlib.pyplot as plt
from math import exp
from math import log
from math import pi
from scipy.integrate import quad ## integrate f(x)dx from x_i to x_i+1
from scipy.stats import norm
from scipy.stats import chisquare
from scipy.optimize import basinhopping
from scipy.stats import binned_statistic as bstat

我生成了1000个数据点的随机高斯分布样本,平均μ= 48,标准差sigma = 7.我可以对数据进行直方图,我的目标是找到参数mu,sigma和normc(缩放因子或归一化常数),它们最适合样本数据的直方图。有许多误差分析方法,但出于我的目的,最佳拟合被确定为最小化卡方的拟合(在下面进一步描述)。我知道代码很长(甚至太长),但我的问题需要一些设置。

## generate data sample
a, b = 48, 7 ## mu, sigma
randg = []
for index in range( 1000 ):
    randg.append( random.gauss(a,b) )
data = sorted( randg )

small = min( data )
big = max( data )
domain = np.linspace(small,big,3000) ## for fitted plot overlay on histogram of data

然后我为直方图组织了我的箱子。

numbins = 30 ## number of bins

def binbounder( small , big , numbins ):
    ## generates list of bound bins for histogram ++ bincount
    binwide = ( big - small ) / numbins ## binwidth
    binleft = [] ## left edges of bins
    for index in range( numbins ):
        binleft.append( small + index * binwide )
    binbound = [val for val in binleft]
    binbound.append( big ) ## all bin edges
    return binbound

binborders = binbounder( small , big , numbins )
## useful if one performs plt.hist(data, bins = binborders, ...)

def binmidder( small , big , numbins ):
    ## all midtpts of bins
    ## for x-ticks on histogram
    ## useful to visualize over/under -estimate of error
    binbound = binbounder( small , big , numbins )
    binwide = ( big - small ) / numbins
    binmiddles = []
    for index in range( len( binbound ) - 1 ):
        binmiddles.append( binwide/2 + index * binwide )
    return binmiddles

binmids = binmidder( small , big , numbins )

要进行卡方分析,必须输入每箱的期望值(E_i)和每个箱的观测值的乘数(O_i)和它们的差的平方的output the sum over all the bins超过每箱的期望值。

def countsperbin( xdata , args = [ small , big , numbins ]):
    ## calculates multiplicity of observed values per bin
    binborders = binbounder( small , big , numbins )
    binmids = binmidder( small , big , numbins )
    values = sorted( xdata ) ## function(xdata) ~ f(x)
    bincount = []
    for jndex in range( len( binborders ) ):
        if jndex != len( binborders ) - 1:
            summ = 0
            for val in values:
                if val > binborders[ jndex ] and val <= binborders[ jndex + 1 ]:
                    summ += 1
            bincount.append( summ )
        if jndex == len( binborders ) - 1:
            pass
    return bincount

obsperbin = countsperbin( binborders , data ) ## multiplicity of observed values per bin

计算和最小化Chi Squared所需的每个bin的每个期望值被定义为从x_i = left binedge到x_i + 1 = right binedge的分布函数的积分。

我想对我的优化参数进行合理的初始猜测,因为这些可以让我对最小化的Chi Squared进行合理的猜测。我选择mu,sigma和normc接近但不等于它们的真值,以便我可以测试最小化是否有效。

def maxbin( perbin ):
    ## perbin is a list of observed data per bin
    ## returns largest multiplicity of observed values with index
    ## useful to help guess scaling factor "normc" (outside exponential in GaussDistrib)
    for index, maxval in enumerate( perbin ):
        if maxval == max( perbin ):
            optindex = index
    return optindex, perbin[ optindex ] 

mu, sigma, normc = np.mean( data ) + 30, np.std( data ) + 20, maxbin( obsperbin )

由于我们正在整合f(x)dx,因此数据点(或xdata)与此无关。

def GaussDistrib( xdata , args = [ mu , sigma , normc ] ): ## G(x)
    return normc * exp( (-1) * (xdata - mu)**2 / (2 * sigma**2) )

def expectperbin( args ):
    ## calculates expectation values per bin
    ## needed with observation values per bin for ChiSquared
    ## expectation value of single bin is equal to area under Gaussian curve from left binedge to right binedge
    ## area under curve for ith bin = integral G(x)dx from x_i (left edge) to x_i+1 (right edge)
    ans = []
    for index in range(len(binborders)-1): # ith index does not exist for rightmost boundary
        ans.append( quad( GaussDistrib , binborders[ index ] , binborders[ index + 1 ], args = [ mu , sigma , normc ])[0])
    return ans

我定义的函数chisq从scipy模块调用chisquare以返回结果。

def chisq( args ):
    ## args[0] = mu
    ## args[1] = sigma
    ## args[2] = normc
    ## last subscript [0] gives chi-squared value, [1] gives 0 ≤ p-value ≤ 1
    ## can also minimize negative p-value to find best fitting chi square
    return chisquare( obsperbin , expectperbin( args[0] , args[1] , args[2] ))[0]

我不知道如何,但我想在我的系统上设置约束。具体而言,分箱数据高度列表的最大值必须大于零(由于分辨后保留的指数项,因此必须为Chi Square)。

def miniz( chisq , chisqguess , niter = 200 ):
    minimizer = basinhopping( chisq , chisqguess , niter = 200 )
    ## Minimization methods available via https://docs.scipy.org/doc/scipy-0.18.1/reference/optimize.html
    return minimizer

expperbin = expectperbin( args = [mu , sigma , normc] )
# chisqmin = chisquare( obsperbin , expperbin )[0]
# chisqmin = result.fun

""" OPTIMIZATION """

print("")
print("initial guess of optimal parameters")

initial_mu, initial_sigma, initial_normc = np.mean(data)+30 , np.std(data)+20 , maxbin( (obsperbin) )
## check optimized result against:  mu = 48, sigma = 7 (via random number generator for Gaussian Distribution)

chisqguess = chisquare( obsperbin , expectperbin( args[0] , args[1] , args[2] ))[0]
## initial guess for optimization

result = miniz( chisqguess, args = [mu, sigma, normc] )
print(result)
print("")

最小化的目的是找到最佳拟合的优化参数。

optmu , optsigma , optnormc = result.x[0], abs(result.x[1]), result.x[2]

chisqcheck = chisquare(obsperbin, expperbin)
chisqmin = result.fun
print("chisqmin --  ",chisqmin,"        ",chisqcheck," --   check chi sq")

print("""
""")

## CHECK
checkbins = bstat(xdata, xdata, statistic = 'sum', bins = binborders) ## via SCIPY (imports)
binsum = checkbins[0]
binedge = checkbins[1]
binborderindex = checkbins[2]
print("binsum",binsum)
print("")
print("binedge",binedge)
print("")
print("binborderindex",binborderindex)
# Am I doing this part right?

tl; dr:我想要result,它调用函数minimiz,调用scipy模块以使用猜测值最小化Chi Squared。 Chi Squared和猜测值各自调用其他函数等。如何通过正确的方式传递我的args?

1 个答案:

答案 0 :(得分:3)

您可以访问optimize.basinhopping返回的OptimizeResult中的所有信息。

我抽象出随机样本的生成,并将函数的数量减少到运行优化所需的5个函数。

参数传递中唯一“棘手”的部分是将参数musigma传递给{{里面的 GaussDistrib 函数1}}调用,但这在quad doc中很容易解释。除此之外,我没有看到参数传递的真正问题。

长时间使用quad是错误的。你没有从高斯那里得到正确的值(当2就足够时,不需要改变3个独立的参数)。此外,要获得卡方的正确值,您必须将高斯的概率乘以样本计数(您将normc箱中的绝对计数与高斯下的概率进行比较 - 这显然是错误的)。

obsperbin
  

最初猜测的chisquare:3772.70822797

     

优化后的chisquare:26.351284911784447

     

mu,sigma优化后:48.2701027439,7.046156286

顺便说一下,basinhopping对于这类问题来说太过分了。我会留在fmin(Nelder-Mead)。