在 Python 中为 Matplotlib 直方图的 x 轴添加更多描述性标签

Add more descriptive labelling to x-axis of Matplotlib histogram in Python

我在 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 文档中查找此内容,但似乎找不到任何能让我实现最终目标的内容。

非常感谢任何帮助。

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

可以通过遍历条形图(矩形块)来添加百分比。它们的高度表示属于 bin 的行数,因此除以总行数再乘以 100 得到一个百分比。 bar height, x 和 half width 可以定位文本。

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()