如何拟合散点图数据的正态分布

How to fit a normal distribution for scatter plot data

我有一个包含 x(x 列)和 y(第 1 列)值的数据框 下面我得到 meanstdev.

接下来我将它们一起绘制在一张图表上,但它看起来很不对劲,不仅仅是拟合曲线移动了,我不确定它有什么问题。

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

data_sample = {'x': [0,1,2,3,4,5,6,7,8,9,10], '1': [0,1,2,3,4,5,4,3,2,1,0]}  
def test_func(x, a, b): 
    return stats.norm.pdf(x,a,b)

params, cov_params = optimize.curve_fit(test_func, data_sample['x'], data_sample['1'])

print(params)

plt.scatter(data_sample['x'], data_sample['1'], label='Data')
plt.plot(data_sample['x'] , test_func(data_sample['x'], params[0], params[1]), label='Fitted function')

plt.legend(loc='best')

plt.show()

数据需要归一化,使得曲线下的面积为1。要计算面积,当所有x值相差1时,需要sum 的 y 值。如果 x 值之间的 space 大于或小于 1,则还应包括该因子。另一种计算面积的方法是 np.trapz().

拟合时需要使用归一化因子。而用原始数据绘制曲线时需要发生相反的情况。

当您尝试将高斯 pdf 函数拟合到非归一化点时,“最佳”拟合是一个非常窄、非常高的峰值。此峰值试图接近中心的 y=5 值。

下面的示例代码将列表转换为 numpy 数组,因此可以更轻松地编写函数。此外,为了绘制平滑曲线,使用了更详细的 x 值。

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

def test_func(x, a, b):
    return stats.norm.pdf(x, a, b)

data_sample = {'x': np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
               '1': np.array([0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0])}

# x_dist = (data_sample['x'].max() - data_sample['x'].min()) / (len(data_sample['x']) - 1)
# normalization_factor = sum(data_sample['1']) * x_dist
normalization_factor = np.trapz(data_sample['1'], data_sample['x'])  # area under the curve
params, pcov = optimize.curve_fit(test_func, data_sample['x'], data_sample['1'] / normalization_factor)

plt.scatter(data_sample['x'], data_sample['1'], clip_on=False, label='Data')
x_detailed = np.linspace(data_sample['x'].min() - 3, data_sample['x'].max() + 3, 200)
plt.plot(x_detailed, test_func(x_detailed, params[0], params[1]) * normalization_factor,
         color='crimson', label='Fitted function')

plt.legend(loc='best')
plt.margins(x=0)
plt.ylim(ymin=0)
plt.tight_layout()
plt.show()

PS:使用原始代码(未归一化),但使用更详细的 x 值,窄曲线会更明显:

x_detailed = np.linspace(min(data_sample['x']) - 1, max(data_sample['x']) + 1, 500)
plt.plot(x_detailed, test_func(x_detailed, params[0], params[1]), color='m', label='Fitted function')