将高斯拟合绘制到displot或histplot中。

15

我决定尝试使用 seaborn 版本 0.11.0! 我正在玩弄 displot 函数,它将替换 distplot 函数,据我所知。 我只是试图弄清楚如何将高斯拟合绘制到直方图上。 这是一些示例代码。

import seaborn as sns
import numpy as np
x = np.random.normal(size=500) * 0.1

使用distplot,我可以做到:
sns.distplot(x, kde=False, fit=norm)

在这里输入图片描述

但如何在 displothistplot 中实现呢?

到目前为止,我最接近的方法是:

sns.histplot(x,stat="probability", bins=30, kde=True, kde_kws={"bw_adjust":3})

进入图像描述

但我认为这只是增加了绘制的kde的平滑性,而这并不是我想要的。


@static_rtti 从现有的原始答案中具体缺少什么?你的悬赏目标明确了两个互斥的目标,即“寻找一个规范答案”和“一个好的通用答案对每个人都非常有益”。openai提供了一个出色的描述,解释了通用答案和规范答案之间的区别。 - Trenton McKinney
3个回答

10

我也非常想念fit参数。看起来在弃用distplot函数时,他们没有替换该功能。在他们填补这个漏洞之前,我创建了一个简短的函数,将正态分布叠加到我的histplot中。我只需将该函数与导入一起粘贴到文件顶部,然后只需添加一行即可在需要时添加覆盖。

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats

def normal(mean, std, color="black"):
    x = np.linspace(mean-4*std, mean+4*std, 200)
    p = stats.norm.pdf(x, mean, std)
    z = plt.plot(x, p, color, linewidth=2)

data = np.random.normal(size=500) * 0.1    
ax = sns.histplot(x=data, stat="density")
normal(data.mean(), data.std())

enter image description here

如果您更愿意使用stat="probability"而不是stat="density",您可以使用以下方式对拟合曲线进行归一化:
def normal(mean, std, histmax=False, color="black"):
    x = np.linspace(mean-4*std, mean+4*std, 200)
    p = stats.norm.pdf(x, mean, std)
    if histmax:
        p = p*histmax/max(p)
    z = plt.plot(x, p, color, linewidth=2)

data = np.random.normal(size=500) * 0.1    
ax = sns.histplot(x=data, stat="probability")
normal(data.mean(), data.std(), histmax=ax.get_ylim()[1])

5

distplot源代码关于fit=参数与这里其他答案已经提到的非常相似;初始化一些支持数组,使用给定数据的均值/标准差计算PDF值,并在直方图上面叠加线性图。我们可以直接将代码的相关部分“转录”为自定义函数,并用它来绘制高斯拟合(不一定是正态分布;也可以是任何连续分布)。

一个示例实现如下所示。

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats

def add_fit_to_histplot(a, fit=stats.norm, ax=None):

    if ax is None:
        ax = plt.gca()

    # compute bandwidth
    bw = len(a)**(-1/5) * a.std(ddof=1)
    # initialize PDF support
    x = np.linspace(a.min()-bw*3, a.max()+bw*3, 200)
    # compute PDF parameters
    params = fit.fit(a)
    # compute PDF values
    y = fit.pdf(x, *params)
    # plot the fitted continuous distribution
    ax.plot(x, y, color='#282828')
    return ax

# sample data
x = np.random.default_rng(0).normal(1, 4, size=500) * 0.1

# plot histogram with gaussian fit
sns.histplot(x, stat='density')
add_fit_to_histplot(x, fit=stats.norm);

first iteration

如果你不喜欢黑色边缘颜色或者整体的颜色,我们可以改变条形图的颜色、边缘颜色和透明度参数,使得histplot()的输出与已弃用的distplot()的默认样式输出相同。
import numpy as np

# sample data
x = np.random.default_rng(0).normal(1, 4, size=500) * 0.1

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10,4))

# left subplot
sns.distplot(x, kde=False, fit=stats.norm, ax=ax1)
ax1.set_title('Using distplot')

# right subplot
sns.histplot(x, stat='density', color='#1f77b4', alpha=0.4, edgecolor='none', ax=ax2)
add_fit_to_histplot(x, fit=stats.norm, ax=ax2)
ax2.set_title('Using histplot+fit');

result


这个答案与现有的答案(12)不同,因为它在直方图上拟合了一个高斯分布(或任何其他连续分布,如伽玛分布),其中存在数据(这也是在distplot()中绘制拟合的方式)。目标是尽可能地复制distplot()的拟合功能。
例如,假设您有遵循泊松分布的数据,绘制其直方图并对其进行高斯拟合。使用add_fit_to_histplot(),由于支持与数据端点相关联(并使用Scott的规则进行带宽计算),所得到的高斯拟合图仅在直方图上存在相应的数据时绘制,这也是使用distplot()绘制的方式(下面的左子图)。另一方面,ohtotaschenormal()函数即使没有相应的数据也会绘制,即正态概率密度函数的左尾部分完全绘制出来(下面的右子图)。
data = np.random.default_rng(0).poisson(0.5, size=500)

fig, (a1, a2) = plt.subplots(1, 2, facecolor='white', figsize=(10,4))

# left subplot
sns.histplot(data, stat='density', color='#1f77b4', alpha=0.4, edgecolor='none', ax=a1)
add_fit_to_histplot(data, fit=stats.norm, ax=a1)
a1.set_title("With add_fit_to_histplot")

# right subplot
sns.histplot(x=data, stat="density", ax=a2)
normal(data.mean(), data.std())
a2.set_title("With ohtotasche's normal function")

difference


1
@TrentonMcKinney 我编辑了这个函数,以减少对内部 seaborn 方法的依赖。 - cottontail
就个人而言,我会忽略掉关于边缘颜色的部分,因为它与实际问题无关。或者可以在现有的答案中添加一个参考链接 answer,https://stackoverflow.com/q/46087192/7758804,https://dev59.com/uMDqa4cB1Zd3GeqPg5Uv,https://dev59.com/scDqa4cB1Zd3GeqPneF2。 - Trenton McKinney

4

对不起,我迟到了。请检查一下这是否符合您的要求。

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

data = np.random.normal(size=500) * 0.1
mu, std = norm.fit(data)

# Plot the histogram.
plt.hist(data, bins=25, density=True, alpha=0.6, color='g')

# Plot the PDF.
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, mu, std)
plt.plot(x, p, 'k', linewidth=2)
plt.show()

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接