函数传递给scipy.optimize.curve_fit需要满足哪些特定要求才能运行?

Bru*_*wso 5 python numpy mathematical-optimization curve-fitting scipy

我正在使用matplotlib hist函数将统计模型拟合到分布.例如,我的代码使用以下代码拟合指数分布:

    try:

        def expDist(x, a, x0):
            return a*(exp(-(x/x0))/x0)

        self.n, self.bins, patches = plt.hist(self.getDataSet(), self.getDatasetSize()/10, normed=1, facecolor='blue', alpha = 0.55)
        popt,pcov = curve_fit(expDist,self.bins[:-1], self.n, p0=[1,mean])
        print "Fitted gaussian curve to data with params a %f, x0 %f" % (popt[0], popt[1])
        self.a = popt[0]
        self.x0 = popt[1]

        self.fitted = True
    except RuntimeError:
        print "Unable to fit data to exponential curve"
Run Code Online (Sandbox Code Playgroud)

它运行很好,但是当我修改它做同样的事情之间的均匀分布ab,

    def uniDist(x, a, b):
        if((x >= a)and(x <= b)):
            return float(1.0/float(b-a))
        else:
            return 0.000

    try:



        self.n, self.bins, patches = plt.hist(self.getDataSet(), self.getDatasetSize()/10, normed=1, facecolor='blue', alpha = 0.55)
        popt,pcov = curve_fit(uniDist,self.bins[:-1], self.n, p0=[a, b])
        print "Fitted uniform distribution curve to data with params a %f, b %f" % (popt[0], popt[1])
        self.a = popt[0]
        self.b = popt[1]

        self.fitted = True
    except RuntimeError:
        print "Unable to fit data to uniform distribution pdf curve"
Run Code Online (Sandbox Code Playgroud)

代码崩溃了

ValueError:具有多个元素的数组的真值是不明确的.使用a.any()或a.all()

这个问题似乎是在某处curve_fit,函数试图调用该函数安装(expDistuniDist在这种情况下)与可迭代的一组值,但我无法弄清楚如何expDist的功能是能够采取任何迭代没有崩溃?

And*_*eak 2

你的怀疑部分正确。curve_fit确实将一个可迭代对象传递给函数,但不仅仅是任何可迭代对象: a numpy.ndarray。这些恰好拥有向量化算术运算符,所以

a*(exp(-(x/x0))/x0)
Run Code Online (Sandbox Code Playgroud)

将简单地按元素方式处理输入数组,没有任何错误(并且输出正确)。甚至没有涉及太多魔法:对于函数的每次评估,参数ax0将是标量,仅x一个数组。

现在的问题uniDist是它不仅包含算术运算符:它还包含比较运算符。只要仅将单个数组与标量进行比较,这些就可以正常工作:

>>> import numpy as np
>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> a>2
array([False, False, False,  True,  True], dtype=bool)
Run Code Online (Sandbox Code Playgroud)

上面演示了在数组和标量上使用比较运算符将再次产生按元素的结果。当您尝试将逻辑运算符应用于其中两个布尔数组时,会出现您看到的错误:

>>> a>2
array([False, False, False,  True,  True], dtype=bool)
>>> a<4
array([ True,  True,  True,  True, False], dtype=bool)
>>> (a>2) and (a<4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Run Code Online (Sandbox Code Playgroud)

错误消息有点令人困惑。可以追溯到这样一个事实:Python 会尝试得出单个结果array1 and array2(在本机 Python 中会根据其空性返回任一数组)。然而,numpy 怀疑这不是你想要做的,并且抵制住猜测的诱惑。

由于您希望函数对两个布尔数组(来自比较操作)按元素进行操作,因此您必须使用运算&符。这在本机 python 中是“二进制与”,但对于 numpy 数组,这为您提供了数组的按元素“逻辑与”。您还可以使用numpy.logical_and(或在您的情况下scipy.logical_and)更明确:

>>> (a>2) & (a<4)
array([False, False, False,  True, False], dtype=bool)
>>> np.logical_and(a>2,a<4)
array([False, False, False,  True, False], dtype=bool)
Run Code Online (Sandbox Code Playgroud)

请注意,对于这种&情况,您总是必须将比较放在括号中,因为这a>2&a<4对于程序员来说又是不明确的并且是错误的(考虑您想要它做什么)。由于布尔值的“二进制与”的行为完全符合您的预期,因此可以安全地重写您的函数来使用&而不是and比较两个比较。

然而,您仍然需要更改一个步骤:在ndarray输入的情况下,其if行为也会有所不同。Python 情不自禁地在 an 中做出单一选择if,如果您将数组放入其中,也是如此。但您真正想要做的是(再次)按元素约束输出的元素。因此,您要么必须循环遍历数组(不要),要么以矢量化方式再次进行此选择。后者是使用 numpy/scipy 的惯用法:

import scipy as sp
def uniDist(x, a, b):
    return sp.where((a<=x) & (x<=b), 1.0/(b-a), 0.0)
Run Code Online (Sandbox Code Playgroud)

这(即numpy.where)将返回一个与 大小相同的数组x。对于条件为的元素True,输出值为1/(b-a)。其余的输出是0. 对于 scalar x,返回值是 numpy 标量。请注意,我删除了float上面示例中的转换,因为尽管1.0您使用 python 2,但分子中的转换肯定会给您带来真正的除法。尽管我建议使用 python 3,或者至少使用from __future__ import division.


小提示:即使对于标量情况,我也建议使用 python 的运算符链接进行比较,这适合此目的。我的意思是,您可以简单地执行if a <= x <= b: ...,并且与大多数语言不同,这在功能上与您编写的内容相同(但更漂亮)。