如何将参数传递给其他函数(通常通过 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)
传递附加参数似乎不起作用,因为附加参数从第一次调用开始就保持在固定值。这不是您想要的:您想同时优化 mu
和 sigma
。
鉴于这些简化,我们有以下(肯定是非常不符合 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] )
一样。但我也将mu
和lambda
传递给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)?的主要内容,如果未能解决你的问题,请参考以下文章
当我们将数组作为参数传递给其他函数时,数组的值如何变化? [复制]
如何在使用 LowLevelCallable cfunc 时使用 scipy.integrate.nquad 并将参数传递给它