Python 拟合除中心点尖峰外处处为零的高斯数据

Python 拟合除中心点尖峰外处处为零的高斯数据,python,numpy,scipy,Python,Numpy,Scipy,此类型数据的测试代码: import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit x = np.linspace(0,1,20) y = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0]) n = np.size(x) mean = sum(x*y)/n sigma = np.sqrt

此类型数据的测试代码:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

x = np.linspace(0,1,20)
y = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0])

n = np.size(x)
mean = sum(x*y)/n
sigma = np.sqrt(sum(y*(x-mean)**2)/n)

def gaus(x,a,x0,sigma):
    return a*np.exp(-(x-x0)**2/(2*sigma**2))

popt,pcov = curve_fit(gaus,x,y,p0=[max(y),mean,sigma])

plt.plot(x,y,'b+:',label='data')
plt.plot(x,gaus(x,*popt),'ro:',label='fit')
plt.legend()
我需要拟合大量的数据,就像上面给出的y数组一样,服从高斯分布

使用scipy.optimize使用标准高斯拟合例程可获得此类拟合:

我尝试过许多不同的初始值,但无法得到任何拟合

有没有人知道如何将这些数据拟合到高斯分布

谢谢

不要使用常规的“a”参数,请使用适当的参数:

问题 你的根本问题是你有一个严重的不确定的拟合问题。这样想:你有三个未知,但只有一个数据点。这类似于只有一个方程时求解
x,y,z
。由于高斯分布的高度可以独立于其宽度而变化,因此存在无限多个分布,所有分布都具有不同的宽度,以满足拟合的约束

更直接地说,您的
a
sigma
参数都可以更改分布的最大高度,这几乎是实现良好匹配的唯一重要因素(至少在分布居中且相当窄的情况下)。因此,Scipy中的拟合例程无法确定在任何给定步骤中要更改哪些

修复 解决此问题的最简单方法是锁定一个参数。你不需要改变你的等式,但你需要至少使
a
x0
sigma
中的一个成为常数。要修正的参数的最佳选择可能是
x0
,因为仅通过获取y中非零的一个数据点的x坐标来确定数据的平均值/中值/模式是很简单的。你还需要更聪明一点,知道如何设置最初的猜测。下面是它的样子:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

x = np.linspace(0,1,20)
xdiff = x[1] - x[0]
y = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0])

# the mean/median/mode all occur at the x coordinate of the one datapoint that is non-zero in y
mean = x[np.argmax(y)]
# sigma should be tiny, since we want a narrow distribution
sigma = xdiff
# the scaling factor should be roughly equal to the "height" of the one datapoint
a = y.max()

def gaus(x,a,sigma):
    return a*np.exp(-(x-mean)**2/(2*sigma**2))

bounds = ((1, .015), (20, 1))
popt,pcov = curve_fit(gaus, x, y, p0=[a, sigma], maxfev=20000, bounds=bounds)
residual = ((gaus(x,*popt) - y)**2).sum()

plt.figure(figsize=(8,6))

plt.plot(x,y,'b+:',label='data')

xdist = np.linspace(x.min(), x.max(), 1000)
plt.plot(xdist,gaus(xdist,*popt),'C0', label='fit distribution')

plt.plot(x,gaus(x,*popt),'ro:',label='fit')
plt.text(.1,6,"residual: %.6e" % residual)

plt.legend()
plt.show()
输出:

更好的办法 你不需要拟合就能得到你想要的高斯函数。您可以使用一个简单的闭式表达式来计算所需的参数,如下面代码中的
fitonegauss
函数:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def gauss(x, a, mean, sigma):
    return a*np.exp(-(x - mean)**2/(2*sigma**2))

def fitonegauss(x, y, fwhm=None):
    if fwhm is None:
        # determine full width at half maximum from the spacing between the x points
        fwhm = (x[1] - x[0])

    # the mean/median/mode all occur at the x coordinate of the one datapoint that is non-zero in y
    mean = x[np.argmax(y)]

    # solve for sigma in terms of the desired full width at half maximum
    sigma = fwhm/(2*np.sqrt(2*np.log(2)))

    # max(pdf) == 1/(np.sqrt(2*np.pi)*sigma). Use that to determine a
    a = y.max() #(np.sqrt(2*np.pi)*sigma)

    return a, mean, sigma

N = 20
x = np.linspace(0,1,N)
y = np.zeros(N)
y[N//2] = 10

popt = fitonegauss(x, y)

plt.figure(figsize=(8,6))
plt.plot(x,y,'b+:',label='data')

xdist = np.linspace(x.min(), x.max(), 1000)
plt.plot(xdist,gauss(xdist,*popt),'C0', label='fit distribution')

residual = ((gauss(x,*popt) - y)**2).sum()
plt.plot(x, gauss(x,*popt),'ro:',label='fit')
plt.text(.1,6,"residual: %.6e" % residual)

plt.legend()
plt.show()
输出:

这种方法的优点很多。它的计算效率比任何拟合都要高,它(在大多数情况下)永远不会失败,并且它让您能够更好地控制最终得到的分布的实际宽度


设置了
fitonegauss
函数,以便您可以直接设置拟合分布的参数。如果不设置它,代码将根据x数据的间距自动猜测它。这似乎为您的应用程序产生了合理的结果。

我可以看到这个问题在这里被否决了,也许可以在这里尝试一下。我认为这不是一个纯粹的编程问题,所以这就是为什么它会发生的原因。@petrch非常感谢!我希望你明白,从数学上讲,你的拟合没有任何意义。我认为它确实有意义,只要你以某种方式指定所需的宽度。当然,对于OP来说,要想得到他们想要的东西,可能有比健身更简单的方法。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def gauss(x, a, mean, sigma):
    return a*np.exp(-(x - mean)**2/(2*sigma**2))

def fitonegauss(x, y, fwhm=None):
    if fwhm is None:
        # determine full width at half maximum from the spacing between the x points
        fwhm = (x[1] - x[0])

    # the mean/median/mode all occur at the x coordinate of the one datapoint that is non-zero in y
    mean = x[np.argmax(y)]

    # solve for sigma in terms of the desired full width at half maximum
    sigma = fwhm/(2*np.sqrt(2*np.log(2)))

    # max(pdf) == 1/(np.sqrt(2*np.pi)*sigma). Use that to determine a
    a = y.max() #(np.sqrt(2*np.pi)*sigma)

    return a, mean, sigma

N = 20
x = np.linspace(0,1,N)
y = np.zeros(N)
y[N//2] = 10

popt = fitonegauss(x, y)

plt.figure(figsize=(8,6))
plt.plot(x,y,'b+:',label='data')

xdist = np.linspace(x.min(), x.max(), 1000)
plt.plot(xdist,gauss(xdist,*popt),'C0', label='fit distribution')

residual = ((gauss(x,*popt) - y)**2).sum()
plt.plot(x, gauss(x,*popt),'ro:',label='fit')
plt.text(.1,6,"residual: %.6e" % residual)

plt.legend()
plt.show()