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

时间:2017-01-28 00:04:09

标签: python numpy scipy mathematical-optimization curve-fitting

我正在使用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"

哪个运行正常,但是当我修改它以做同样的事情以在a&之间进行均匀分布。 b

    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"

代码崩溃,

  

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

问题似乎是curve_fit中的某个地方,该函数试图通过一组可迭代的函数来调用函数(expDistuniDist)。值,但我无法弄清楚expDist函数如何能够在不崩溃的情况下进行任何迭代?

1 个答案:

答案 0 :(得分:2)

你的怀疑是部分正确的。 curve_fit确实将迭代传递给函数,但不仅仅是迭代:numpy.ndarray。这些碰巧拥有矢量化算术运算符,所以

a*(exp(-(x/x0))/x0)

将简单地在输入数组上以元素方式工作而没有任何错误(并且具有正确的输出)。涉及的内容甚至没有多少:对于函数的每次评估,参数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)

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

>>> 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()

错误消息有点令人困惑。它可以追溯到这样一个事实: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)

请注意,对于&情况,您总是需要对比较进行括号化,因为a>2&a<4对于程序员来说是不明确的而且是错误的(考虑到你想要的它要做)。由于布尔值的“二进制和”将完全按照您的预期运行,因此可以安全地重写函数以使用&代替and来比较两个比较。

但是,您仍需要更改一个步骤:如果ndarray输入,if的行为也会有所不同。 Python无法在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)

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

次要注意:即使对于标量情况,我也建议使用python的运算符链进行比较,这有助于达到此目的。我的意思是你可以简单地做if a <= x <= b: ...,与大多数语言不同,这在功能上与你写的相当(但更漂亮)。