Python 使用scipy拟合给定直方图的分布

Python 使用scipy拟合给定直方图的分布,python,numpy,scipy,probability,curve-fitting,Python,Numpy,Scipy,Probability,Curve Fitting,我想使用scipy(在我的例子中,使用weibull_min)将分布拟合到我的数据中。在给定直方图而不是数据点的情况下,是否可以这样做?在我的例子中,因为直方图有大小为1的整数箱子,我知道我可以用以下方式推断我的数据: 将numpy导入为np orig_hist=np.数组([10,5,3,2,1]) ext_data=reduce(lambda x,y:x+y,[[i]*x表示i,x表示枚举(原始历史)]) 在这种情况下,ext_数据将保存以下内容: [0, 0, 0, 0, 0, 0, 0

我想使用scipy(在我的例子中,使用weibull_min)将分布拟合到我的数据中。在给定直方图而不是数据点的情况下,是否可以这样做?在我的例子中,因为直方图有大小为1的整数箱子,我知道我可以用以下方式推断我的数据:

将numpy导入为np
orig_hist=np.数组([10,5,3,2,1])
ext_data=reduce(lambda x,y:x+y,[[i]*x表示i,x表示枚举(原始历史)])
在这种情况下,ext_数据将保存以下内容:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4]
并使用以下方法构建直方图:

np.histogram(ext_data, bins=5)
将等同于原始历史


然而,鉴于我已经建立了直方图,我希望避免外推数据,并使用orig_hist拟合分布,但我不知道是否有可能在拟合过程中直接使用它。此外,是否有一个numpy函数可以用来执行类似于我所展示的外推的操作?

我可能误解了一些事情,但我相信拟合直方图正是你应该做的:你试图近似概率密度。直方图尽可能接近基本概率密度。您只需对其进行规格化,以获得1的积分,或者允许拟合模型包含任意预因子

import numpy as np
import scipy.stats as stats
import scipy.optimize as opt
import matplotlib.pyplot as plt

orig_hist = np.array([10, 5, 3, 2, 1])
norm_hist = orig_hist/float(sum(orig_hist))

popt,pcov = opt.curve_fit(lambda x,c: stats.weibull_min.pdf(x,c), np.arange(len(norm_hist)),norm_hist)

plt.figure()
plt.plot(norm_hist,'o-',label='norm_hist')
plt.plot(stats.weibull_min.pdf(np.arange(len(norm_hist)),popt),'s-',label='Weibull_min fit')
plt.legend()
当然,对于给定的输入,Weibull拟合远远不能令人满意:

更新 正如我上面提到的,Weibull_min不适合您的样本输入。更大的问题是,它也不适合您的实际数据:

orig_hist = np.array([ 23., 14., 13., 12., 12., 12., 11., 11., 11., 11., 10., 10., 10., 9., 9., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 6., 6., 6., 6., 6., 6., 6., 6., 6., 6., 6.], dtype=np.float32)

这个直方图有两个主要问题。第一,正如我所说的,它不可能对应于威布尔分布:它是接近零的最大分布,并且有一个长尾,因此它需要一个非平凡的威布尔参数组合。此外,您的直方图显然只包含分布的一部分。这意味着我上面的正常化建议肯定会失败。无法避免在拟合中使用任意比例参数

我手动定义了一个比例威布尔拟合函数:

在这个函数中,
x
是自变量,
l
lambda
(比例参数),
c
k
(形状参数),
A
是比例预因子。引入
A
的微弱优势在于,您不必规范化直方图

现在,当我把这个函数放到
scipy.optimize.curve\u fit
中时,我发现了你所做的:它实际上并不执行拟合,而是坚持初始拟合参数,不管你设置什么(使用
p0
参数;默认猜测是每个参数都是1)。而
曲线拟合
似乎认为拟合收敛

在一个多小时的与墙壁相关的头部撞击之后,我意识到问题在于
x=0
的奇异行为使非线性最小二乘算法失效。通过排除第一个数据点,可以获得与数据的实际拟合。我怀疑如果我们设置
c=1
,但不允许安装,那么这个问题可能会消失,但允许安装可能会提供更多信息(因此我没有检查)

以下是相应的代码:

import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt

orig_hist = np.array([ 23., 14., 13., 12., 12., 12., 11., 11., 11., 11., 10., 10., 10., 9., 9., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 6., 6., 6., 6., 6., 6., 6., 6., 6., 6., 6.], dtype=np.float32)

my_weibull = lambda x,l,c,A: A*float(c)/l*(x/float(l))**(c-1)*np.exp(-(x/float(l))**c)

popt,pcov = opt.curve_fit(my_weibull,np.arange(len(orig_hist))[1:],orig_hist[1:]) #throw away x=0!

plt.figure()
plt.plot(np.arange(len(orig_hist)),orig_hist,'o-',label='orig_hist')
plt.plot(np.arange(len(orig_hist)),my_weibull(np.arange(len(orig_hist)),*popt),'s-',label='Scaled Weibull fit')
plt.legend()
结果:

最终拟合参数的顺序为
(l、c、A)
,形状参数约为
0.88
。这对应于发散的概率密度,这解释了为什么会出现一些错误

RuntimeWarning:电源中遇到无效值

以及为什么配件中没有
x=0
的数据点。但从数据和拟合之间的视觉一致性判断,您可以评估结果是否可以接受


如果你想做得过火,你可能可以尝试使用带有这些参数的
np.random.weibull
生成点,然后将生成的直方图与你自己的直方图进行比较。

我添加了一个基于
scipy.optimize.curve\u fit
的答案,但后来我意识到你想使用
stats.weibull\u min.fit
。如果我理解正确,您需要后者的
ext\u数据。碰巧,直方图对于前者来说已经足够了。我的答案对你有用吗?老实说,我不这么认为。我遵循了你答案中的步骤,但使用了我的数据,结果拟合得不好。
optimize.cuver\u fit
似乎有问题,因为无论我使用什么数据作为输入,返回的popt值都是
1.00000001
。谢谢,这似乎与我正在尝试做的很接近。我的问题是,我试图使用stats.weibull_min.fit进行拟合。但这种特殊的方法将要拟合的数据作为输入。如果我理解正确的话,在您的例子中,您使用optimize.curve_fit函数拟合数据,并将您想要拟合的函数(weibull_min.pdf)以及X和Y值传递给它。@AlbertoA我使用
curve_fit
将概率密度拟合到直方图,这是基于原始数据的概率密度的近似值。您是否尝试过在实际直方图上使用
曲线拟合
?它是否也返回了
popt=1.0001
?是的,它确实返回了相同的值。我将直方图标准化,除以它的总和,然后尝试
曲线拟合
,结果与我在使用问题中示例的虚拟直方图时得到的结果相同
1.00000001
。你知道拟合中应该是什么吗?至少一个大概的数字?您可以将其设置为曲线拟合的起点,这可能会有所帮助。您的实际数据是否更多地根据威布尔分布(即,您的直方图是否趋向于0,即x->0,至少如果威布尔_min是这样的话)?查看一些威布尔图,我会说形状参数
c
将小于1或等于1。下面的数组是my d的一个示例
import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt

orig_hist = np.array([ 23., 14., 13., 12., 12., 12., 11., 11., 11., 11., 10., 10., 10., 9., 9., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 8., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 7., 6., 6., 6., 6., 6., 6., 6., 6., 6., 6., 6.], dtype=np.float32)

my_weibull = lambda x,l,c,A: A*float(c)/l*(x/float(l))**(c-1)*np.exp(-(x/float(l))**c)

popt,pcov = opt.curve_fit(my_weibull,np.arange(len(orig_hist))[1:],orig_hist[1:]) #throw away x=0!

plt.figure()
plt.plot(np.arange(len(orig_hist)),orig_hist,'o-',label='orig_hist')
plt.plot(np.arange(len(orig_hist)),my_weibull(np.arange(len(orig_hist)),*popt),'s-',label='Scaled Weibull fit')
plt.legend()
In [631]: popt
Out[631]: array([  1.10511850e+02,   8.82327822e-01,   1.05206207e+03])