我尝试拟合直方图,但拟合仅适用于标准化数据,即直方图中的选项normed=True
。有没有办法用scipy stats(或其他方法)这样做?这是一个使用均匀分布的MWE:
import matplotlib.pyplot as plt
import numpy as np
import random
from scipy.stats import uniform
data = []
for i in range(1000):
data.append(random.uniform(-1,1))
loc, scale = uniform.fit(data)
x = np.linspace(-1,1, 1000)
y = uniform.pdf(x, loc, scale)
plt.hist(data, bins=100, normed=False)
plt.plot(x, y, 'r-')
plt.show()
我也试过定义我自己的功能(下面),但我感觉不合适。
import matplotlib.pyplot as plt
import numpy as np
import random
from scipy import optimize
data = []
for i in range(1000):
data.append(random.uniform(-1,1))
def unif(x,avg,sig):
return avg*x + sig
y, base = np.histogram(data,bins=100)
x = [0.5 * (base[i] + base[i+1]) for i in xrange(len(base)-1)]
popt, pcov = optimize.curve_fit(unif, x, y)
x_fit = np.linspace(x[0], x[-1], 100)
y_fit = unif(x_fit, *popt)
plt.hist(data, bins=100, normed=False)
plt.plot(x_fit, y_fit, 'r-')
plt.show()
答案 0 :(得分:1)
请注意,将分布拟合到直方图通常是个坏主意。与原始数据相比,直方图包含的信息较少,因此拟合最可能更差。因此,问题中的第一个MWE实际上包含最佳方法。只需标准化直方图,它就会匹配数据的分布:plt.hist(data, bins=100, normed=True)
。
但是,您似乎确实希望使用非标准化的直方图。在这种情况下,采用直方图通常使用的标准化,并将其倒置应用于拟合分布。 documentation将规范化描述为
N /(LEN(x)的`DBIN)
将除以观察次数乘以箱宽,这很冗长。
将分布乘以此值会产生每箱的预期计数:
loc, scale = uniform.fit(data)
x = np.linspace(-1,1, 1000)
y = uniform.pdf(x, loc, scale)
n_bins = 100
bin_width = np.ptp(data) / n_bins
plt.hist(data, bins=n_bins, normed=False)
plt.plot(x, y * len(data) * bin_width, 'r-')
第二个MWE很有趣,因为你描述了一个不合适的行,但实际上它是非常合适 :)。您只需过度拟合直方图,因为虽然您希望水平线(一个自由度)适合任意线(两个自由度)。
所以,如果你想要一条水平线适合水平线,如果你适合别的东西,不要惊讶于得到别的东西......
def unif(x, sig):
return 0 * x + sig # slope is zero -> horizontal line
然而,有一种更简单的方法来获得非标准化均匀分布的高度。只需平均所有箱柜的直方图:
y, base = np.histogram(data,bins=100)
y_hat = np.mean(y)
print(y_hat)
# 10.0
或者,甚至更简单地使用len(data) / n_bins == 10
的理论值。