创建使用百分比而不是计数的 matplotlib 或 seaborn 直方图?

Posted

技术标签:

【中文标题】创建使用百分比而不是计数的 matplotlib 或 seaborn 直方图?【英文标题】:Creating a matplotlib or seaborn histogram which uses percent rather than count? 【发布时间】:2017-02-26 18:51:47 【问题描述】:

具体来说,我正在处理 Kaggle Titanic 数据集。我绘制了一个堆叠直方图,显示了在泰坦尼克号上幸存和死亡的年龄。代码如下。

figure = plt.figure(figsize=(15,8))
plt.hist([data[data['Survived']==1]['Age'], data[data['Survived']==0]['Age']], stacked=True, bins=30, label=['Survived','Dead'])
plt.xlabel('Age')
plt.ylabel('Number of passengers')
plt.legend()

我想更改图表以显示该年龄组中幸存百分比的每个箱子的单个图表。例如。如果一个垃圾箱包含 10 到 20 岁之间的年龄,并且该年龄段泰坦尼克号上 60% 的人幸存下来,那么高度将沿 y 轴排列 60%。

编辑:我可能对我正在寻找的东西给出了一个糟糕的解释。我希望根据幸存的百分比来更改条形的实际形状,而不是更改 y 轴值。

图表上的第一个分类显示该年龄组中大约有 65% 的人幸存下来。我希望这个 bin 在 65% 处与 y 轴对齐。以下 bin 看起来分别为 90%、50%、10%,依此类推。

图表最终会看起来像这样:

【问题讨论】:

Dexplot 库能够创建百分比堆积条形图。见my answer below。 【参考方案1】:

也许以下会有所帮助...

    根据 'Survived' 拆分数据框

    df_survived=df[df['Survived']==1]
    df_not_survive=df[df['Survived']==0]
    

    创建垃圾箱

    age_bins=np.linspace(0,80,21)
    

    使用np.histogram生成直方图数据

    survived_hist=np.histogram(df_survived['Age'],bins=age_bins,range=(0,80))
    not_survive_hist=np.histogram(df_not_survive['Age'],bins=age_bins,range=(0,80))
    

    计算每个箱子的存活率

    surv_rates=survived_hist[0]/(survived_hist[0]+not_survive_hist[0])
    

    剧情

    plt.bar(age_bins[:-1],surv_rates,width=age_bins[1]-age_bins[0])
    plt.xlabel('Age')
    plt.ylabel('Survival Rate')
    

【讨论】:

避免所有浮点值四舍五入为0。使用以下from __future__ import division【参考方案2】:

pd.Series.hist 在下面使用np.histogram

让我们探索一下

np.random.seed([3,1415])
s = pd.Series(np.random.randn(100))
d = np.histogram(s, normed=True)
print('\nthese are the normalized counts\n')
print(d[0])
print('\nthese are the bin values, or average of the bin edges\n')
print(d[1])

these are the normalized counts

[ 0.11552497  0.18483996  0.06931498  0.32346993  0.39278491  0.36967992
  0.32346993  0.25415494  0.25415494  0.02310499]

these are the bin edges

[-2.25905503 -1.82624818 -1.39344133 -0.96063448 -0.52782764 -0.09502079
  0.33778606  0.77059291  1.20339976  1.6362066   2.06901345]

我们可以在计算平均 bin 边缘时绘制这些图

pd.Series(d[0], pd.Series(d[1]).rolling(2).mean().dropna().round(2).values).plot.bar()

实际答案

我们可以简单地将normed=True 传递给pd.Series.hist 方法。将其传递给np.histogram

s.hist(normed=True)

【讨论】:

很棒的帖子。虽然不是我想要的。我已经更新了我的帖子。【参考方案3】:

对于Seaborn,使用参数stat,它可以有多个值,see documentation。

seaborn.histplot(
    data=data,
    x='variable',
    discrete=True,
    stat='count'
)

stat 更改为probability 后的结果。

seaborn.histplot(
    data=data,
    x='variable',
    discrete=True,
    stat='probability'
)

根据documentation,目前支持的stat参数值为:

count 显示观察次数 frequency 显示观察数除以 bin 宽度 density 标准化计数,使直方图的面积为 1 probability 标准化计数,使条形高度的总和为 1

【讨论】:

【参考方案4】:

library Dexplot 能够返回组的相对频率。目前,您需要使用cut 函数将pandas 中的age 变量装箱。然后,您可以使用 Dexplot。

titanic['age2'] = pd.cut(titanic['age'], range(0, 110, 10))

将您要计算的变量 (age2) 传递给 count 函数。使用split 参数细分计数并通过age2 标准化。此外,这可能是堆积条形图的好时机

dxp.count('age2', data=titanic, split='survived', stacked=True, normalize='age2')

【讨论】:

【参考方案5】:

首先,如果您创建一个将数据分成年龄组的函数会更好

# This function splits our data frame in predifined age groups
def cutDF(df):
    return pd.cut(
        df,[0, 10, 20, 30, 40, 50, 60, 70, 80], 
        labels=['0-10', '11-20', '21-30', '31-40', '41-50', '51-60', '61-70', '71-80'])


data['AgeGroup'] = data[['Age']].apply(cutDF)

然后您可以按如下方式绘制图表:

survival_per_age_group = data.groupby('AgeGroup')['Survived'].mean()

# Creating the plot that will show survival % per age group and gender
ax = survival_per_age_group.plot(kind='bar', color='green')
ax.set_title("Survivors by Age Group", fontsize=14, fontweight='bold')
ax.set_xlabel("Age Groups")
ax.set_ylabel("Percentage")
ax.tick_params(axis='x', top='off')
ax.tick_params(axis='y', right='off')
plt.xticks(rotation='horizontal')             

# Importing the relevant fuction to format the y axis 
from matplotlib.ticker import FuncFormatter

ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: ':.0%'.format(y)))
plt.show()

【讨论】:

以上是关于创建使用百分比而不是计数的 matplotlib 或 seaborn 直方图?的主要内容,如果未能解决你的问题,请参考以下文章

如何将计数总结为百分比而不是绝对值?

matplotlib/pandas 中是不是有参数将直方图的 Y 轴作为百分比?

如何绘制直方图的密度而非计数? (Matplotlib)

Matplotlib / Seaborn:热图的控制线/行高

如何使用与计数不同语句相关的百分比创建附加列

使用 COUNT 函数停止 SAS 对空白单元格进行计数