【问题标题】:How to customise pandas box and whisker plot with additional percentiles?如何使用额外的百分位数自定义熊猫盒和胡须图?
【发布时间】:2023-04-07 03:19:01
【问题描述】:

我正在尝试使用 pandas 制作以下在 excel 中制作的情节。

工作中的许多绘图都是使用 excel 完成的,将数据转换为所需的格式既繁琐又繁琐。我想使用 pandas,但我的老板希望看到制作完全相同(或非常接近)的情节。

我通常使用 seaborn 绘制箱线图,发现它非常方便,但我需要显示更多的百分位数(第 5、第 10、第 25、第 50、第 75、第 90 和第 95),如图图例所示。

我知道 seaborn/matplotlib 允许我使用 whis=[10,90] 更改胡须的范围,我可以使用 showmean=True,但这会留下其他标记(第 95 和第 5 个百分位数)添加到每个图中。如何覆盖这些?

我根据需要对数据进行了分组,我可以使用 .describe() 提取百分位数,如下所示 pcntls=assay.groupby(['LocalSTRAT']).describe(percentiles=[0.1,0.05,0.25,0.5,0.75,0.9,0.95])

和 transform 这给了我这个:

LocalSTRAT  AB  CBC     CLB     LAB     LBB     LBL     MS  TB  TBL     UAB     UBB
count   982.000000  234.000000  159.000000  530.000000  1136.000000     72.000000   267.000000  1741.000000     16.000000   1641.000000     2099.000000
mean    0.687658    1.410962    0.118302    0.211321    0.110251    0.077917    0.766124    0.262648    0.191875    0.119174    1.320357
std     0.814027    0.855342    0.148397    0.286574    0.146550    0.088921    0.647259    0.309134    0.125497    0.207197    1.393613
min     0.005000    0.005000    0.020000    0.005000    0.005000    0.010000    0.005000    0.005000    0.060000    0.005000    0.005000
5%  0.030000    0.196500    0.030000    0.020000    0.020000    0.020000    0.060000    0.020000    0.067500    0.005000    0.170000
10%     0.050000    0.363000    0.038000    0.020000    0.020000    0.021000    0.096000    0.030000    0.070000    0.020000    0.230000
25%     0.130000    0.825000    0.045000    0.050000    0.030000    0.030000    0.225000    0.050000    0.077500    0.030000    0.450000
50%     0.400000    1.260000    0.070000    0.120000    0.050000    0.050000    0.610000    0.150000    0.175000    0.060000    0.940000
75%     0.950000    1.947500    0.140000    0.250000    0.120000    0.072500    1.120000    0.350000    0.257500    0.130000    1.570000
90%     1.720000    2.411000    0.262000    0.520000    0.265000    0.149000    1.624000    0.640000    0.340000    0.250000    2.770000
95%     2.370000    2.967500    0.322000    0.685500    0.390000    0.237000    2.037000    0.880000    0.390000    0.410000    4.322000
max     7.040000    5.070000    1.510000    2.620000    1.450000    0.580000    3.530000    2.390000    0.480000    4.190000    11.600000

我不知道如何使用这个输出从头开始构建箱线图。

我认为以正常方式构建一些箱线图更容易,然后在顶部添加额外的几个数据点(第 5 个和第 95 个百分位标记),但不知道如何做到这一点。

(对于制作如图所示的图例或如何将其图像文件插入到我的绘图中,并获得日志样式的网格线,并在 x 轴中包含计数的方法的奖励积分!)

【问题讨论】:

    标签: pandas matplotlib seaborn boxplot


    【解决方案1】:

    只需使用从 .describe() 输出中提取的百分位数覆盖散点图,记住对两者进行排序以确保顺序不会混淆。 图例在外部制作为图像并单独插入。

    使用 plt.text() 计算和添加计数。

    使用plt.grid(True, which='both') 应用对数网格线并将轴设置为对数。

    代码和结果如下。

    import pandas as pd
    import seaborn as sns
    import matplotlib
    import matplotlib.pyplot as plt
    
    
    pathx = r"C:\boxplots2.xlsx"
    
    pathx =  pathx.replace( "\\", "/")#avoid escape character issues
    #print pathx
    #pathx = pathx[1:len(pathx)-1]
    df=pd.read_excel(pathx)
    
    #this line removes missing data rows (where the strat is not specified)
    df=df[df["STRAT"]!=0]
    
    
    
    assay=df
    
    factor_to_plot='Total %S'
    f=factor_to_plot
    
    x_axis_factor='STRAT'
    g=x_axis_factor
    
    pcntls=assay.groupby([g]).describe(percentiles=[0.05,0.1,0.25,0.5,0.75,0.9,0.95])
    sumry= pcntls[f].T
    #print sumry
    ordered=sorted(assay[g].dropna().unique())
    
    #set figure size and scale text
    plt.rcParams['figure.figsize']=(15,10)
    text_scaling=1.9
    sns.set(style="whitegrid")
    sns.set_context("paper", font_scale=text_scaling) 
    
    #plot boxplot
    ax=sns.boxplot(x=assay[g],y=assay[f],width=0.5,order=ordered, whis=[10,90],data=assay, showfliers=False,color='lightblue', 
                showmeans=True,meanprops={"marker":"x","markersize":12,"markerfacecolor":"white", "markeredgecolor":"black"})
    
    plt.axhline(0.3, color='green',linestyle='dashed', label="S%=0.3")
    
    #this line sets the scale to logarithmic
    ax.set_yscale('log')
    
    leg= plt.legend(markerscale=1.5,bbox_to_anchor=(1.0, 0.5) )#,bbox_to_anchor=(1.0, 0.5)
    #plt.title("Assay data")
    plt.grid(True, which='both')
    ax.scatter(x=sorted(list(sumry.columns.values)),y=sumry.loc['5%'],s=120,color='white',edgecolor='black') 
    ax.scatter(x=sorted(list(sumry.columns.values)),y=sumry.loc['95%'],s=120,color='white',edgecolor='black')
    
    
    #add legend image
    img = plt.imread("legend.jpg")
    plt.figimage(img, 1900,900, zorder=1, alpha=1)
    
    
    #next line is important, select a column that has no blanks or nans as the total items are counted. 
    assay['value']=assay['From']
    
    vals=assay.groupby([g])['value'].count()
    j=vals
    
    ymin, ymax = ax.get_ylim()
    xmin, xmax = ax.get_xlim()
    #print ymax
    
    #put n= values at top of plot    
    x=0
    for i in range(len(j)):
    
        plt.text(x = x , y = ymax+0.2, s = "N=\n" +str(int(j[i])),horizontalalignment='center')
        #plt.text(x = x , y = 102.75, s = "n=",horizontalalignment='center')
        x+=1
    
    
    
    
    
    #use the section below to adjust the y axis lable format to avoid default of 10^0 etc for log scale plots.
    ylabels = ['{:.1f}'.format(y) for y in ax.get_yticks()]
    ax.set_yticklabels(ylabels)
    

    这给出了:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-28
      • 2022-01-16
      • 2018-02-23
      • 2017-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多