Python 正态分布曲线不';t使用matplotlib在子批次中很好地拟合直方图

Python 正态分布曲线不';t使用matplotlib在子批次中很好地拟合直方图,python,matplotlib,histogram,subplot,normal-distribution,Python,Matplotlib,Histogram,Subplot,Normal Distribution,我使用“plt.subplot(2,2,sharex=True,sharey=True)”来绘制一个2*2的子图。每个子批次有两个Y轴,并包含直方图上的正态分布曲线。注意,我在这里特别设置了“sharex=True,sharey=True”,以使所有子地块共享相同的X轴和Y轴 运行我的代码后,除了第二、三和第四个子图之外,一切都很好,其中正态分布曲线与柱状图拟合得不是很好(请参见这里的图) 我用谷歌搜索了一下,但没能解决这个问题。但是,如果我在代码中设置了“sharex=True,sharey

我使用“plt.subplot(2,2,sharex=True,sharey=True)”来绘制一个2*2的子图。每个子批次有两个Y轴,并包含直方图上的正态分布曲线。注意,我在这里特别设置了“sharex=True,sharey=True”,以使所有子地块共享相同的X轴和Y轴

运行我的代码后,除了第二、三和第四个子图之外,一切都很好,其中正态分布曲线与柱状图拟合得不是很好(请参见这里的图)

我用谷歌搜索了一下,但没能解决这个问题。但是,如果我在代码中设置了“sharex=True,sharey=False”,那么该图看起来是正确的,但是所有子地块都使用自己的Y轴,这不是我想要的。请看这里的数字

希望StackOverflow的专家能解决这个问题。非常感谢

下面是我的代码:

import matplotlib.pyplot as plt

from scipy.stats import norm

def align_yaxis(ax1, v1, ax2, v2):
    #adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1
    _, y1 = ax1.transData.transform((0, v1))
    _, y2 = ax2.transData.transform((0, v2))
    inv = ax2.transData.inverted()
    _, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
    miny, maxy = ax2.get_ylim()
    ax2.set_ylim(miny+dy, maxy+dy)
    
def drawSingle(myax, mydf , title, offset):
    
    num_bins = 200
    xs = mydf["gap"]
    x = np.linspace(-1,1,1000)
    
    mu =np.mean(x) 
    sigma =np.std(xs)
    n, bins, patche =  myax.hist(xs, num_bins, alpha=0.8, facecolor='blue', density=False) 
    

    myax.set_ylabel('frequency',color="black",fontsize=12, weight = "bold")
    myax.set_xlabel('X', fontsize=12, weight = "bold",horizontalalignment='center')

    ax_twin = myax.twinx()
    y_normcurve = norm.pdf(bins, mu, sigma)
    ax_twin.plot(bins, y_normcurve, 'r--') 

    align_yaxis(myax,0,ax_twin,0)
    peakpoint = norm.pdf(mu,loc=mu,scale=sigma)
    plt.vlines(mu, 0, peakpoint, 'y', '--', label='example')
    
    ax_twin.set_ylabel("probablility dense",color="black",fontsize=12, weight = "bold")
    
         
def drawSubplots(mydf1,mydf2,mydf3,mydf4, pos1,pos2,pos3,pos4, title, filename):
    plt.rcParams['figure.figsize'] = (18,15 )
    
    my_x_ticks = np.arange(-0.8, 0.8,0.1)
   
    rows, cols = 2, 2
    fig, ax = plt.subplots(2, 2, sharex=True, sharey=True)

    drawSingle(ax[0][0], mydf1, "Subplot1", pos1)
    drawSingle(ax[0][1], mydf2, "Subplot2", pos2)
    drawSingle(ax[1][0], mydf3, "Subplot3", pos3)
    drawSingle(ax[1][1], mydf4, "Subplot4", pos4)
    
    plt.text(-1, -1, title, horizontalalignment='center', fontsize=18)
    
    plt.show()

    
drawSubplots(df1, df2,df3,df4,3.2,3.1,2.7,2.85,"test9", "test9")
以下是一个尝试:

  • 使左y轴为“频率”(在当前箱子宽度的情况下,这是非常没有信息的),并在4个子批次之间共享
  • 右y轴是否为“概率密度”;注意所有高斯函数的顶部是如何在y=0.02左右的(双轴只能在末尾设置,因为共享的y轴可以通过后面的子批次进行更新)
  • 使直方图和法线曲线对齐
导入matplotlib.pyplot作为plt
作为pd进口熊猫
将numpy作为np导入
从scipy.stats导入norm
def drawSingle(myax、mydf、标题):
数量=200
xs=mydf[“差距”]
x=np.linspace(-1,1,1000)
mu=np.平均值(x)
西格玛=np.std(xs)
n、 容器,补丁=myax.hist(xs,num_容器,alpha=0.8,facecolor='blue',density=False)
myax.set_ylabel('frequency',color=“black”,fontsize=12,weight=“bold”)
myax.set_xlabel('X',fontsize=12,weight=“bold”,horizontalalignment='center'))
归一化系数=len(xs)*(料仓[1]-料仓[0])
y_normcrove=norm.pdf(x,mu,sigma)*归一化系数
myax.plot(x,y_曲线,'r--')
myax.vlines(mu,0,y_normcrove.max(),'y','--',color='lime',label='example')
返回归一化因子
def绘图子批次(mydf1、mydf2、mydf3、mydf4、标题):
plt.rcParams['figure.figsize']=(18,15)
图,ax=plt.子批次(nrows=2,ncols=2,sharex=True,sharey=True)
dfs=[mydf1、mydf2、mydf3、mydf4]
标准系数=[drawSingle(ax_i,df,title)
对于ax_i,df,zip中的标题(ax.ravel(),dfs,[“Subplot1”、“Subplot2”、“Subplot3”、“Subplot4”])]
对于ax_i,zip中的norm_因子(ax.ravel(),norm_因子):
ax_twin=ax_i.twinx()
ymax=ax_i.get_ylim()[1]
ax_twin.set_ylim(0,ymax/norm_因子)
plt.suptitle(标题,字体大小=18)
plt.紧_布局()
plt.show()
df1,df2,df3,df4=[6000,4000,1800,1200]中n的[pd.数据帧({“gap”:np.random.normal(0,0.2,n)}]
图纸子图(df1、df2、df3、df4,“标题”)

非常感谢约翰,你太棒了

基于您的代码,我刚刚在DrawSubplot函数中添加了几行代码,以便使95%的高斯曲线区域在每个子图的下限和上限之间着色。以下是我的尝试。看来ax_twin.fill_between在这里不正常工作。从图中可以看出,阴影区域超出了高斯曲线。我想要的只是在高斯曲线下的下限和上限之间对区域进行着色。如果你不介意的话,请你检查一下我的错误好吗?多谢各位

import matplotlib.pyplot as plt
import math

from scipy.stats import norm

def align_yaxis(ax1, v1, ax2, v2):
    #adjust ax2 ylimit so that v2 in ax2 is aligned to v1 in ax1
    _, y1 = ax1.transData.transform((0, v1))
    _, y2 = ax2.transData.transform((0, v2))
    inv = ax2.transData.inverted()
    _, dy = inv.transform((0, 0)) - inv.transform((0, y1-y2))
    miny, maxy = ax2.get_ylim()
    ax2.set_ylim(miny+dy, maxy+dy)
    
    

def drawSingle(myax, mydf , title):
    
    num_bins = 200
    xs = mydf["gap"]
    x = np.linspace(-1,1,1000)
 
    mu =np.mean(xs) 
    sigma =np.std(xs)
    
    n, bins, patches = myax.hist(xs, num_bins, alpha=0.8, facecolor='blue', density=False)
    

    myax.set_ylabel('Frequency', color="black", fontsize=12, weight="bold")
    myax.set_xlabel(title, fontsize=12, weight="bold", horizontalalignment='center')

    normalization_factor = len(xs) * (bins[1] - bins[0])
    y_normcurve = norm.pdf(x, mu, sigma) * normalization_factor
    myax.plot(x, y_normcurve, 'r--')

    myax.vlines(mu, 0, y_normcurve.max(), 'y', '--', color='lime', label='example')
    
    plt.xlim(-0.8,0.8) 
    my_x_ticks = np.arange(-0.8, 0.8,0.1)
    plt.xticks(my_x_ticks)

    return normalization_factor, mu, sigma
    
    
def drawSubplots(mydf1,mydf2,mydf3,mydf4, title):
    plt.rcParams['figure.figsize'] = (18,15 )
    
    norm_factors = []
    mus = []
    sigmas = []
    
    my_x_ticks = np.arange(-0.8, 0.8,0.1)
    
    
    rows, cols = 2, 2
    fig, ax = plt.subplots(nrows=rows, ncols=cols, sharex=True, sharey=True)
    

    dfs = [mydf1, mydf2, mydf3, mydf4]
    #norm_factors = [drawSingle(ax_i, df, title)
                    #for ax_i, df, title in zip(ax.ravel(), dfs, ["Subplot1", "Subplot2", "Subplot3", "Subplot4"])]
    
    
    for ax_i, df, title in zip(ax.ravel(), dfs, ["Subplot1", "Subplot2", "Subplot3", "Subplot4"]):
        norm_factor, mu, sigma = drawSingle(ax_i, df, title)
        norm_factors.append(norm_factor)
        mus.append(mu)
        sigmas.append(sigma)
    
    
    for ax_i, norm_factor, mu, sigma in zip(ax.ravel(), norm_factors, mus, sigmas ):
        ax_twin = ax_i.twinx()
        
        xmax = ax_i.get_xlim()[1]
        ax_twin.set_ylim(0, xmax / norm_factor)
        ax_twin.set_ylabel("probablility dense",color="black",fontsize=12, weight = "bold")
        
        
        CI_95_lower = mu - (1.96*sigma)
        CI_95_upper = mu + (1.96*sigma)

        px_shaded = np.arange(CI_95_lower,CI_95_upper,0.1)
        ax_twin.fill_between(px_shaded,norm.pdf(px_shaded,loc=mu,scale=sigma) * norm_factor,alpha=0.75, color='pink')
        area_shaded_95_CI = norm.cdf(x=CI_95_upper, loc=mu, scale=sigma)-norm.cdf(x=CI_95_lower, loc=mu, scale=sigma)
        ax_twin.text(-0.06,0.01,str(round(area_shaded_95_CI*100,1))+"%", fontsize=20)
       
        ax_twin.annotate(s=f'lower bound= {CI_95_lower:.3f}',xy=(CI_95_lower,norm.pdf(CI_95_lower,loc=mu,scale=sigma)),xytext=(-0.75,0.01),weight='bold',color='blue',\
                 arrowprops=dict(arrowstyle='-|>',connectionstyle='arc3',color='green'),\
                 fontsize=12
                )

        ax_twin.annotate(s=f'upper bound= {CI_95_upper:.3f}',xy=(CI_95_upper,norm.pdf(CI_95_upper,loc=mu,scale=sigma)),xytext=(0.28,0.01),weight='bold',color='blue',\
                 arrowprops=dict(arrowstyle='-|>',connectionstyle='arc3',color='green'),\
                  fontsize=12
                )

        ax_twin.text(0.05, 0.03, r"$\mu=" + f'{mu:.6f}' + ", \sigma=" + f'{sigma:.6f}' + "$" + ", confidence interval=95%" ,
            horizontalalignment='center', fontsize=15)
        
        
    
    plt.suptitle(title, fontsize=18)
    plt.tight_layout()
    plt.show()


df1, df2, df3, df4 = [pd.DataFrame({"gap": np.random.normal(0, 0.2, n)}) for n in [6000, 4000, 1800, 1200]]

drawSubplots(df1, df2, df3, df4, "Title")

请注意,使用
sharey=True
,行数较少的数据帧的直方图将更小。如果希望这些具有相似高度,则需要使用
hist(…,density=True)
(将其面积缩放为1)对高度进行规格化。需要
density=True
以正确拟合法线曲线。或者,用
y\u normcrove
乘以
len(xs)
,再乘以binwidth(
y\u normcrove*len(xs)*(bins[1]-bins[0)
),使它们的面积相等。最好省去
twinx()
并在同一
ax
上绘制所有内容。此外,
mu=np.mean(xs)
mu=np.mean(x)更合适
。最后,使用
y\u normcrove=norm.pdf(x,mu,sigma)
将曲线绘制为
myax.plot(x,…)
Hi JohanC,非常感谢您的帮助!。我按照您的建议,在drawSingle函数中修改了代码。(设置密度=真)现在,正态分布曲线看起来比直方图更适合,但是每个图中的左Y轴不是频率。我还能有两个Y轴吗(左一个表示频率,右一个表示概率密度)?再次感谢!另外,我不太理解你的意思(Y_正态曲线*len(xs)*(bin[1]-bin[0))请您告诉我如何在代码中执行此操作?再次感谢!使用
density=True
,y轴将是“概率分布函数”的高度。请注意“频率”只有当您有一个明确定义的箱子宽度时,才是有用的度量。如果您使用
bins=200
,箱子宽度将
(xs.max()-xs.min())/200
,这在4个图中是不同的。乘以
(y_normcurve*len(xs)*(箱子[1]-箱子[0])
仅当您使用
density=False
时才需要。嗨,JohanC,我找到了它。正确的方法是将我添加的所有代码移动到drawSingle函数中,在该函数中我使用myax绘制阴影区域并添加注释。非常感谢您的帮助!非常感谢!嗨,JohanC,关于正确的Y轴刻度还有一个困惑。我运行了Y我们的代码并指出,右Y轴刻度不反映概率密度的正确值。请您建议如何调整代码,使其准确显示概率密度的值。具体而言,所有高斯数的顶部约为Y=0.02,但请使用“norm.pdf(mu,loc=mu,scale=sigm