我正在使用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
中的某个地方,该函数试图通过一组可迭代的函数来调用函数(expDist
和uniDist
)。值,但我无法弄清楚expDist
函数如何能够在不崩溃的情况下进行任何迭代?
答案 0 :(得分:2)
你的怀疑是部分正确的。 curve_fit
确实将迭代传递给函数,但不仅仅是迭代:numpy.ndarray
。这些碰巧拥有矢量化算术运算符,所以
a*(exp(-(x/x0))/x0)
将简单地在输入数组上以元素方式工作而没有任何错误(并且具有正确的输出)。涉及的内容甚至没有多少:对于函数的每次评估,参数a
和x0
都是标量,只有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: ...
,与大多数语言不同,这在功能上与你写的相当(但更漂亮)。