【问题标题】:Add more descriptive labelling to x-axis of Matplotlib histogram in Python在 Python 中为 Matplotlib 直方图的 x 轴添加更多描述性标签
【发布时间】:2020-11-23 13:29:58
【问题描述】:

我在 Jupyter 笔记本中创建了一个直方图,以显示 100 次网络访问的页面时间分布(以秒为单位)。

代码如下:

ax = df.hist(column='time_on_page', bins=25, grid=False, figsize=(12,8), color='#86bf91', zorder=2, rwidth=0.9)

ax = ax[0]
for x in ax:

    # Despine
    x.spines['right'].set_visible(False)
    x.spines['top'].set_visible(False)
    x.spines['left'].set_visible(False)

    # Switch off ticks
    x.tick_params(axis="both", which="both", bottom="off", top="off", labelbottom="on", left="off", right="off", labelleft="on")

    
    # Draw horizontal axis lines
    vals = x.get_yticks()
    for tick in vals:
        x.axhline(y=tick, linestyle='dashed', alpha=0.4, color='#eeeeee', zorder=1)

    # Set title
    x.set_title("Time on Page Histogram", fontsize=20, weight='bold', size=12)

    # Set x-axis label
    x.set_xlabel("Time on Page Duration (Seconds)", labelpad=20, weight='bold', size=12)

    # Set y-axis label
    x.set_ylabel("Page Views", labelpad=20, weight='bold', size=12)

    # Format y-axis label
    x.yaxis.set_major_formatter(StrMethodFormatter('{x:,g}'))

这会产生以下可视化效果:

我通常对外观感到满意,但我希望轴更具描述性,可能会显示每个 bin 的 bin 范围以及每个 bin 占总数的百分比。

已在 Matplotlib 文档中查找此内容,但似乎找不到任何可以让我实现最终目标的内容。

非常感谢任何帮助。

【问题讨论】:

    标签: python matplotlib jupyter-notebook histogram distribution


    【解决方案1】:

    当您设置bins=25 时,会在遇到的最低值和最高值之间设置 25 个等间距的 bin。如果您使用这些范围来标记垃圾箱,那么由于任意值,事情可能会令人困惑。将这些 bin 边界四舍五入(例如 20 的倍数)似乎更合适。然后,这些值可以用作 x 轴上的刻度线,很好地位于 bin 之间。

    可以通过循环条形(矩形补丁)来添加百分比。它们的高度表示属于 bin 的行数,因此除以总行数并乘以 100 得到一个百分比。条高、x、半宽可以定位文字。

    from matplotlib import pyplot as plt
    import numpy as np
    import pandas as pd
    
    df = pd.DataFrame({'time_on_page': np.random.lognormal(4, 1.1, 100)})
    max_x = df['time_on_page'].max()
    bin_width = max(20, np.round(max_x / 25 / 20) * 20) # round to multiple of 20, use max(20, ...) to avoid rounding to zero
    bins = np.arange(0, max_x + bin_width, bin_width)
    axes = df.hist(column='time_on_page', bins=bins, grid=False, figsize=(12, 8), color='#86bf91', rwidth=0.9)
    ax = axes[0, 0]
    total = len(df)
    ax.set_xticks(bins)
    for p in ax.patches:
        h = p.get_height()
        if h > 0:
            ax.text(p.get_x() + p.get_width() / 2, h, f'{h / total * 100.0  :.0f} %\n', ha='center', va='center')
    ax.grid(True, axis='y', ls=':', alpha=0.4)
    ax.set_axisbelow(True)
    for dir in ['left', 'right', 'top']:
        ax.spines[dir].set_visible(False)
    ax.tick_params(axis="y", length=0)  # Switch off y ticks
    ax.margins(x=0.02) # tighter x margins
    plt.show()
    

    【讨论】:

    • 这是您要找的吗?
    • 是的!这太棒了,约翰。感谢您抽出宝贵时间回答并提供指导。非常感谢。
    • bin_width = max(10, np.round(max_x / 25 / 10) * 10) 将舍入为 10 的倍数。max(10, ... 中的 10 只是设置了一个最小值(以避免零宽度)。除以 25 使bin_width 约为总宽度的 1/25。除以 10、舍入并再次乘以相同的值会导致舍入到 10 的倍数。请注意,对于 10 的幂,这与 bin_width = max(10, np.round(max_x / 25, -1)) 相同,其中 -1 表示舍入到 -1 之后的数字小数点。
    • 只需总结所有百分比。如果一切正常,总和应该是 100(可能存在一些舍入误差,但如果正好有 100 行,则不会进行任何舍入)。此外,在我的示例代码中,bin 的宽度为 20,但您实际上可以选择任何数字。如果唯一的目标是查看百分比,您甚至不需要四舍五入 binwidths(虽然刻度会更难阅读)。
    • 对于整数值,您必须非常小心边界上的值。 pandas hist 的实现似乎将值 90 到 99 放在 bin 90-100 中,将 100 放在 bin 100-110 中。您可以更改此行为,例如将垃圾箱创建为 bins = np.arange(0.00001, ...) 会将 90 放入垃圾箱 80-90 并将 100 放入垃圾箱 90-100(但也会将 0 放入垃圾箱 -10,0 中,但未显示)。
    猜你喜欢
    • 2021-04-18
    • 2014-08-31
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 1970-01-01
    • 2013-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多