【问题标题】:Issue with plotting normal distribution curve with available set of values使用可用值集绘制正态分布曲线的问题
【发布时间】:2020-06-04 18:36:53
【问题描述】:

我正在尝试为一组值绘制正态分布曲线。不幸的是,下面的代码(取自post)似乎没有在直方图上正确绘制曲线(请参阅附图)。我确定我错过了什么或做了一些愚蠢的事情,但似乎无法弄清楚。有人可以帮忙吗?我在下面包含了我的代码 - 我从数据框中获取值,但为了方便起见,将这些值作为列表包含在 s 中:

import numpy as np
import scipy
import pandas as pd
from scipy.stats import norm
import matplotlib.pyplot as plt
from matplotlib.mlab import normpdf
mu = 0
sigma = 1
n_bins = 50
s = [8, 8, 4, 4, 1, 14, 0, 10, 1, 4, 21, 9, 5, 2, 7, 6, 7, 9, 7, 3, 3, 4, 7, 9, 9, 4, 10, 8, 10, 10, 7, 10, 1, 8, 7, 8, 1, 7, 4, 15, 8, 1, 1, 6, 7, 3, 8, 8, 8, 4][![enter image description here][1]][1]
fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True)

#histogram
n, bins, patches = axes[1].hist(s, n_bins, normed=True, alpha=.1, edgecolor='black' )
pdf = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(bins-mu)**2/(2*sigma**2))
print(pdf)
median, q1, q3 = np.percentile(s, 50), np.percentile(s, 25), np.percentile(s, 75)

#probability density function
axes[1].plot(bins, pdf, color='orange', alpha=.6)

#to ensure pdf and bins line up to use fill_between.
bins_1 = bins[(bins >= q1-1.5*(q3-q1)) & (bins <= q1)] # to ensure fill starts from Q1-1.5*IQR
bins_2 = bins[(bins <= q3+1.5*(q3-q1)) & (bins >= q3)]
pdf_1 = pdf[:int(len(pdf)/2)]
pdf_2 = pdf[int(len(pdf)/2):]
pdf_1 = pdf_1[(pdf_1 >= norm(mu,sigma).pdf(q1-1.5*(q3-q1))) & (pdf_1 <= norm(mu,sigma).pdf(q1))]
pdf_2 = pdf_2[(pdf_2 >= norm(mu,sigma).pdf(q3+1.5*(q3-q1))) & (pdf_2 <= norm(mu,sigma).pdf(q3))]

#fill from Q1-1.5*IQR to Q1 and Q3 to Q3+1.5*IQR
#axes[1].fill_between(bins_1, pdf_1, 0, alpha=.6, color='orange')
#axes[1].fill_between(bins_2, pdf_2, 0, alpha=.6, color='orange')

#add text to bottom graph.
axes[1].annotate("{:.1f}%".format(100*norm(mu, sigma).cdf(q1)), xy=((q1-1.5*(q3-q1)+q1)/2, 0), ha='center')
axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q3)-norm(mu, sigma).cdf(q1))), xy=(median, 0), ha='center')
axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q3+1.5*(q3-q1)-q3)-norm(mu, sigma).cdf(q3))), xy=((q3+1.5*(q3-q1)+q3)/2, 0), ha='center')
axes[1].annotate('q1', xy=(q1, norm(mu, sigma).pdf(q1)), ha='center')
axes[1].annotate('q3', xy=(q3, norm(mu, sigma).pdf(q3)), ha='center')

axes[1].set_ylabel('Probability Density')

#top boxplot
axes[0].boxplot(s, 0, 'gD', vert=False)
axes[0].axvline(median, color='orange', alpha=.6, linewidth=.5)
axes[0].axis('off')

plt.rcParams["figure.figsize"] = (10,10)

plt.subplots_adjust(hspace=0)
plt.show()

【问题讨论】:

  • 由于from matplotlib.mlab import normpdf 的问题,此答案需要更新。请参阅issue。您可以找到受原始答案启发的更新答案here

标签: python matplotlib normal-distribution


【解决方案1】:

您已将musigma 分别任意设置为01,但您应该根据您的实际数据计算它:

data = pd.Series(s)
mu = data.mean()
sigma = data.std()


更新完整的工作示例:
import numpy as np
import scipy
import pandas as pd
from scipy.stats import norm
import matplotlib.pyplot as plt
n_bins = 50
s = [8, 8, 4, 4, 1, 14, 0, 10, 1, 4, 21, 9, 5, 2, 7, 6, 7, 9, 7, 3, 3, 4, 7, 9, 9, 4, 10, 8, 10, 10, 7, 10, 1, 8, 7, 8, 1, 7, 4, 15, 8, 1, 1, 6, 7, 3, 8, 8, 8, 4]
fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True)

#histogram
n, bins, patches = axes[1].hist(s, n_bins, density=True, alpha=.1, edgecolor='black' )
data = pd.Series(s)
mu = data.mean()
sigma = data.std()
pdf = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(bins-mu)**2/(2*sigma**2))
median, q1, q3 = np.percentile(s, 50), np.percentile(s, 25), np.percentile(s, 75)

#probability density function
axes[1].plot(bins, pdf, color='orange', alpha=.6)

#fill from Q1-1.5*IQR to Q1 and Q3 to Q3+1.5*IQR
iqr = 1.5 * (q3-q1)
x1 = np.linspace(q1 - iqr, q1)
x2 = np.linspace(q3, q3 + iqr)
pdf1 = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(x1-mu)**2/(2*sigma**2))
pdf2 = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(x2-mu)**2/(2*sigma**2))
axes[1].fill_between(x1, pdf1, 0, alpha=.6, color='orange')
axes[1].fill_between(x2, pdf2, 0, alpha=.6, color='orange')

#add text to bottom graph.
axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q1)    -norm(mu, sigma).cdf(q1-iqr))), xy=(q1-iqr/2, 0), ha='center')
axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q3)    -norm(mu, sigma).cdf(q1)    )), xy=(median  , 0), ha='center')
axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q3+iqr)-norm(mu, sigma).cdf(q3)    )), xy=(q3+iqr/2, 0), ha='center')
axes[1].annotate('q1', xy=(q1, norm(mu, sigma).pdf(q1)), ha='center')
axes[1].annotate('q3', xy=(q3, norm(mu, sigma).pdf(q3)), ha='center')

axes[1].set_ylabel('Probability Density')

#top boxplot
axes[0].boxplot(s, 0, 'gD', vert=False)
axes[0].axvline(median, color='orange', alpha=.6, linewidth=.5)
axes[0].axis('off')

【讨论】:

  • 感谢您的解决方案! x 轴似乎显示了列表值 (0, 5,10,15..) - 这不应该从平均值 (..-2,-1,0,1,2..) 中显示标准差在这篇文章中 - stackoverflow.com/questions/49630427/…?另外,当我取消注释axes[1].fill_between(bins_1, pdf_1, 0, alpha=.6, color='orange') 时,我收到一个错误Argument dimensions are incompatible,当s = np.random.normal(mu, sigma, 50) 时我没有收到这个错误 - 知道为什么会发生这种情况,有没有办法解决这个问题?谢谢
  • 这不应该从平均值显示标准差 (..-2,-1,0,1,2..) --> 不,你为什么这么认为?在链接的示例中,std dev 设置为 1,均值设置为 0,因此它似乎显示了 std dev,但实际上它显示了 x 值
  • 我之所以这么认为是因为在链接的示例中,s 中的值介于 0 和 1 之间,但 x 轴的范围从 -3 到 3,并且直方图和曲线是沿着该值绘制的跨度>
  • ...知道为什么会发生这种情况 --> 这是因为 bins_1 和 pdf_1 的长度不同(bins_2 和 pdf_2 的长度相同)。原因是pdf范围被划分在中间,这对于对称链接示例是可以的,但在一般情况下不是
  • 非常感谢完整的工作示例。它就像一个魅力!我建议的唯一编辑是在底部添加 plt.subplots_adjust(hspace=0) plt.show() 以显示情节。谢谢!
【解决方案2】:

把它全部放在一个函数中:

# import warnings filter
from warnings import simplefilter
# ignore all future warnings
simplefilter(action='ignore', category=FutureWarning)

def CTD(df):
    for col in df.columns:
        n_bins = 50

        fig, axes = plt.subplots(nrows=2, ncols=1, sharex=True)

        #histogram
        n, bins, patches = axes[1].hist(boston[col], n_bins, density=True, alpha=.1, edgecolor='black' )
        #data = pd.Series(s)
        mu = boston[col].mean()
        sigma = boston[col].std()
        pdf = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(bins-mu)**2/(2*sigma**2))
        median, q1, q3 = np.percentile(boston.age, 50), np.percentile(boston[col], 25), np.percentile(boston[col], 75)

        #probability density function

        axes[1].plot(bins, pdf, color='orange', alpha=.6)
        #axes[1].figsize=(10,20)
        #fill from Q1-1.5*IQR to Q1 and Q3 to Q3+1.5*IQR
        iqr = 1.5 * (q3-q1)
        x1 = np.linspace(q1 - iqr, q1)
        x2 = np.linspace(q3, q3 + iqr)
        pdf1 = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(x1-mu)**2/(2*sigma**2))
        pdf2 = 1/(sigma*np.sqrt(2*np.pi))*np.exp(-(x2-mu)**2/(2*sigma**2))
        axes[1].fill_between(x1, pdf1, 0, alpha=.6, color='orange')
        axes[1].fill_between(x2, pdf2, 0, alpha=.6, color='orange')
        #add text to bottom graph.
        axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q1)    -norm(mu, sigma).cdf(q1-iqr))), xy=(q1-iqr/2, 0), ha='center')
        axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q3)    -norm(mu, sigma).cdf(q1)    )), xy=(median  , 0), ha='center')
        axes[1].annotate("{:.1f}%".format(100*(norm(mu, sigma).cdf(q3+iqr)-norm(mu, sigma).cdf(q3)    )), xy=(q3+iqr/2, 0), ha='center')
        axes[1].annotate('q1', xy=(q1, norm(mu, sigma).pdf(q1)), ha='center')
        axes[1].annotate('q3', xy=(q3, norm(mu, sigma).pdf(q3)), ha='center')

        #dashed lines
        plt.axvline(df[col].quantile(0),color='b', linestyle='-.')
        plt.axvline(df[col].quantile(0.25),color='g', linestyle='--')
        plt.axvline(df[col].quantile(0.50),color='g', linestyle='--')
        plt.axvline(df[col].quantile(0.75),color='b', linestyle='--')
        plt.axvline(df[col].quantile(1),color='r', linestyle='-.')

        axes[1].set_ylabel('Probability Density')

        #top boxplot
        axes[0].boxplot(df[col], 0, 'gD', vert=False)
        axes[0].axvline(median, color='orange', alpha=.6, linewidth=.5)
        axes[0].axis('off')
        plt.rcParams["figure.figsize"] = (18,10)

调用函数:

CTD(boston)

如果这对你不起作用:

试试这个:

# import warnings filter
from warnings import simplefilter
# ignore all future warnings
simplefilter(action='ignore', category=FutureWarning)

def CTD(df):
    for col in df.columns:
        sns.set(rc={'figure.figsize':(24,6)})
        plt.figure()
        plt.subplot(121)
        sns.distplot(df[col])
        plt.axvline(np.mean(df[col]),color='b', linestyle='--') # Blue line for mean
        plt.axvline(np.median(df[col]),color='r', linestyle='--')# Red line for Median
        plt.subplot(122)
        sns.distplot(df[col])
        plt.axvline(df[col].quantile(0),color='b', linestyle='-.')
        plt.axvline(df[col].quantile(0.25),color='g', linestyle='--')
        plt.axvline(df[col].quantile(0.50),color='g', linestyle='--')
        plt.axvline(df[col].quantile(0.75),color='b', linestyle='--')
        plt.axvline(df[col].quantile(1),color='r', linestyle='-.')

这在具有分位数的 KDE 图上创建虚线。

【讨论】:

    猜你喜欢
    • 2016-03-06
    • 2019-11-04
    • 1970-01-01
    • 2017-02-18
    • 1970-01-01
    • 2014-07-15
    • 2012-02-21
    • 2011-02-08
    • 1970-01-01
    相关资源
    最近更新 更多