如何将参数传递给其他函数(通常通过 scipy)?

Posted

技术标签:

【中文标题】如何将参数传递给其他函数(通常通过 scipy)?【英文标题】:How do I pass through arguments to other functions (generally and via scipy)? 【发布时间】:2017-07-18 08:00:41 【问题描述】:

我正在尝试 minimize 一个通过 scipy 输出 chi-square 的函数,并找到最适合高斯叠加的 mu、sigma、normc。

from math import exp
from math import pi
from scipy.integrate import quad
from scipy.optimize import minimize
from scipy.stats import chisquare
import numpy as np

# guess intitial values for minimized chi-square
mu, sigma = np.mean(mydata), np.std(mydata) # mydata is my data points
normc = 1/(sigma * (2*pi)**(1/2)) 

gauss = lambda x: normc * exp( (-1) * (x - mu)**2 / ( 2 * (sigma **2) ) ) # Gaussian Distribution

# assume I have pre-defined bin-boundaries as a list called binbound

def expvalperbin(binbound,mu,sigma,normc):
    # calculates expectation value per bin
    ans = []
    for index in range(len(binbound)):
        if index != len(binbound)-1:
            ans.append( quad( gauss, binbound[index], binbound[index+1])[0] )
    return ans

expvalguess = expvalperbin(binbound,mu,sig,normc)
obsval = countperbin(binbound,mydata)
arglist = [mu,sig,norm]

def chisquareopt(obslist,explist):
    return chisquare(obslist,explist)[0]

chisquareguess = chisquareopt((obsval,expvalguess), expvalguess, args=arglist)

result = minimize( chisquareopt(obsval,expvalguess), chisquareguess   )
print(result)

运行此代码会出现此错误:

TypeError: chisquareopt() got an unexpected keyword argument 'args'

我有几个问题:

1) 如何编写函数以允许将参数传递给我的函数 chisquareopt?

2) 我如何判断 scipy 是否会优化给出最小卡方的参数 [mu, sigma, normc]?如何从优化中找到这些参数?

3) 很难知道我是否在这里取得了进展。我在正确的轨道上吗?

编辑:如果相关,我有一个输入 [mu, sigma, normc] 并输出子列表列表的函数,每个子列表包含 [mu, sigma, normc] 的可能组合(其中外部列表​​涵盖指定范围内所有可能的参数组合)。

【问题讨论】:

【参考方案1】:

我已经稍微简化了您的问题,以便您对问题 2) 有所了解。

首先,我将您的直方图obslist 和数据点数量N 硬编码为全局变量(这稍微简化了函数签名)。其次,我在expvalperbin 中硬编码了 bin 边界,假设 9 个 bin 具有固定宽度 5,第一个 bin 从 30 开始(因此直方图范围从 30 到 75)。

第三,我使用optimize.fmin (Nelder-Mead) 而不是optimize.minimize。使用fmin 而不是minimize 的原因是通过args=(x,y) 传递附加参数似乎不起作用,因为附加参数从第一次调用开始就保持在固定值。这不是您想要的:您想同时优化 musigma

鉴于这些简化,我们有以下(肯定是非常不符合 Python 的)脚本:

from math import exp
from math import pi
from scipy.integrate import quad
from scipy.optimize import fmin
from scipy.stats import chisquare


obslist = [12, 51, 144, 268, 264, 166, 75, 18, 2] # histogram, 1000 observations
N = 1000 # no. of data points


def gauss(x, mu, sigma):
    return 1/(sigma * (2*pi)**(1/2)) * exp( (-1) * (x - mu)**2 / ( 2 * (sigma **2) ) )

def expvalperbin(mu, sigma):
    e = []
    # hard-coded bin boundaries
    for i in range(30, 75, 5):
        e.append(quad(gauss, i, i + 5, args=(mu, sigma))[0] * N)
    return e

def chisquareopt(args):
    # args[0] = mu
    # args[1] = sigma
    return chisquare(obslist, expvalperbin(args[0], args[1]))[0]

# initial guesses
initial_mu = 35.5
initial_sigma = 14

result = fmin(chisquareopt, [initial_mu, initial_sigma])

print(result)

优化成功终止。

当前函数值:2.010966

迭代次数:49

功能评估:95

[50.57590239 7.01857529]

顺便说一句,obslist 直方图是来自N(50.5, 7.0) 正态分布的 1000 点随机样本。请记住,这些是我的第一个 Python 代码行,所以请不要以风格来评判我。我只是想告诉你问题的一般结构。

【讨论】:

函数expvalperbin中,'args=(mu, sigma))[0] * N)'有什么作用?我猜它复制了 (mu,sigma) N 次的元组,但下标 [0] 让我相信我没有看到完整的图片(类似于 'chisquareopt' 中的 args)?至于不是pythonic,我愿意接受建议。 和你的ans.append( quad( gauss, binbound[index], binbound[index+1])[0] )一样。但我也将mulambda 传递给gauss 函数。最后,要从概率中得到预期的count,你必须乘以N,即观察总数(我已经告诉过你了)。 啊,我现在明白了!感谢您的帮助。 在您的帮助下,我能够优化我的参数。但是出于好奇,我在您的指导之前阅读了文档并且感到困惑。您是否能够仅从文档中找出结构,如果可以,如何?我希望能够使我的理解适应未来的其他模块文档.. 正如我在你的另一个 question 中所说的,我对 Python/scipy 一无所知。所以args=(x) 的问题有点出乎意料。但是你需要的参数对我来说总是很清楚(你可以通过在那里重新阅读我的 cmets 来检查)。我想这与 Python/scipy 无关,而是以前没有做过(考虑一下)类似的事情。在你问这个问题之前,我已经用 Java 验证了我的设计,所以这或多或少是一个翻译问题。【参考方案2】:

通常,这些scipy 函数将值的args 元组传递给您的代码不变。我应该仔细检查代码,但是使用

minimize(myfunc, x0, args=(y,z))

def myfunc(x, y, z): 
   <do something>

minimize 采用变量x 的当前值(标量或数组,取决于x0 的样子)和args 参数,并构造

args = tuple(x) + args
myfunc(*args)

换句话说,它将args 元组与迭代变量连接起来,并将其传递给您的函数。因此,任何中间函数定义都需要使用该模式。

为了说明,定义一个接受通用 args 元组的函数。

In [665]: from scipy.optimize import minimize
In [666]: def myfunc(*args):
     ...:     print(args)
     ...:     return np.abs(args[0])**2
     ...: 
In [667]: myfunc(1,2,3)
(1, 2, 3)
Out[667]: 1
In [668]: myfunc(2,2,3)
(2, 2, 3)
Out[668]: 4
In [669]: minimize(myfunc, 10, args=(2,3))
(array([ 10.]), 2, 3)
(array([ 10.00000001]), 2, 3)
(array([ 10.]), 2, 3)
(array([ 8.99]), 2, 3)
....
(array([-0.00000003]), 2, 3)
Out[669]: 
      fun: 1.7161984122524196e-15
 hess_inv: array([[ 0.50000001]])
      jac: array([-0.00000007])
  message: 'Optimization terminated successfully.'
     nfev: 15
      nit: 4
     njev: 5
   status: 0
  success: True
        x: array([-0.00000004])

(删除了关于哪些参数被最小化的混淆的讨论。查看其他答案或我的编辑历史)

【讨论】:

所以我应该创建元组(mu,sigma,normc)?或者我应该创建一个 (mu,sigma,normc) 的所有可能组合的元组? 我添加了一个简单的例子。我将不得不更多地考虑组合问题。 看不懂mu,sigma,normc的作用。它们是控制最小化某个其他变量(binbound)的参数,还是您要优化的变量(选择最佳组合)? 我想选择产生最小卡方的 mu、sigma、normc 的最佳组合。基本上,我计算我的数据的平均值和标准差来估计每个参数的猜测值。然后将这些参数放入函数 expvalperbin 中,该函数计算期望值。每个 bin 的期望值等于每个 bin 曲线下的面积;在这里,每个 bin 定义了积分的界限,每个 bin 的期望值被附加到一个列表中。有一个单独的等长列表,每个元素是...的出现次数 ...我每个 bin 的数据点。有了一个对应于每个 bin 不变的观察到的多重性的期望猜测值列表,我现在可以计算我的卡方的猜测。我想通过改变 [mu,sigma,normc] 的值来最小化卡方,这很困难,因为它们会影响在期望值的边界上积分的分布函数(因此我的函数的结构)。我的直方图 bin 被定义为 binborders 列表,我想通过卡方覆盖最佳拟合。

以上是关于如何将参数传递给其他函数(通常通过 scipy)?的主要内容,如果未能解决你的问题,请参考以下文章

Scipy 最小化:如何将参数传递给目标和约束

如何通过原始指针将闭包作为参数传递给 C 函数?

如何将 url 参数传递给视图以供其他类函数和模板使用

当我们将数组作为参数传递给其他函数时,数组的值如何变化? [复制]

如何将引用函数传递给另一个函数

如何在使用 LowLevelCallable cfunc 时使用 scipy.integrate.nquad 并将参数传递给它