【问题标题】:Matplotlib: Automatic labeling in side by side bar chartMatplotlib:并排条形图中的自动标记
【发布时间】:2020-07-25 00:34:23
【问题描述】:

基于 matplotlib 中的以下示例,我制作了一个函数,将两个每周时间序列绘制为并排条形图。 https://matplotlib.org/3.1.1/gallery/lines_bars_and_markers/barchart.html#sphx-glr-gallery-lines-bars-and-markers-barchart-py

我的问题是我明确设置了 xtick,这会创建混乱的 xtick-labels。有没有办法让 matplotlib 在这样的图中明确选择 xticks(位置和标签)?

我必须说,我发现使用 (x - width/2) 指定条形位置的整个操作对于并排条形来说非常不雅 - 还有其他选项(除了 matplotlib 或其他包之外的其他包matplotlib 中的规范)以避免编写此类显式代码?

下面是代码和结果。我正在寻找一种解决方案,可以选择 xticks 和 xticklabels 的数量和位置,使其可读:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np


labels = ['W1-2020', 'W2-2020', 'W3-2020', 'W4-2020', 'W5-2020','W6-2020','W7-2020','W8-2020','W9-2020','W10-2020','W11-2020','W12-2020','W13-2020','W14-2020','W15-2020']
men_means = [20, 34, 30, 35, 27,18,23,29,27,29,38,28,17,28,23]
women_means = [25, 32, 34, 20, 25,27,18,23,29,27,29,38,19,20, 34]

x = np.arange(len(labels))  # the label locations
width = 0.35  # the width of the bars

fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width, label='Men')
rects2 = ax.bar(x + width/2, women_means, width, label='Women')

# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()


def autolabel(rects):
    """Attach a text label above each bar in *rects*, displaying its height."""
    for rect in rects:
        height = rect.get_height()
        ax.annotate('{}'.format(height),
                    xy=(rect.get_x() + rect.get_width() / 2, height),
                    xytext=(0, 3),  # 3 points vertical offset
                    textcoords="offset points",
                    ha='center', va='bottom')


autolabel(rects1)
autolabel(rects2)

fig.tight_layout()

plt.show()

【问题讨论】:

    标签: python matplotlib


    【解决方案1】:

    解决方案 1:使用 Pandas

    您可以先创建一个 pandas DataFrame,然后直接绘制多个条形图。 x轴上标签的格式更整洁

    df = pd.DataFrame(
        {'labels': labels,
         'men_means': men_means,
         'women_means': women_means
        })
    
    df.plot(x="labels", y=["men_means", "women_means"], kind="bar")
    

    解决方案 2:使用 Seaborn(改编自 this 答案)

    import seaborn as sns
    
    fig, ax = plt.subplots(figsize=(6, 4))
    tidy = df.melt(id_vars='labels').rename(columns=str.title)
    sns.barplot(x='Labels', y='Value', hue='Variable', data=tidy, ax=ax)
    sns.despine(fig)
    ax.tick_params(axis='x', labelrotation=90)
    

    要仅隐藏每第 n 个刻度,您可以执行以下改编自 this 答案的操作

    n = 2
    for label in ax.xaxis.get_ticklabels()[::n]:
        label.set_visible(False)
    

    要显示每个第 n 个标签,您可以使用以下技巧

    fig.canvas.draw()
    
    n = 4
    labels = [item.get_text() if i%n == 0 else "" for i, item in enumerate(ax.get_xticklabels())]
    
    ax.set_xticklabels(labels);
    

    【讨论】:

    • 谢谢!使用 pandas 的解决方案 1 很简洁。您是否还知道是否可以不绘制所有 xlabels?有了 15 周,这仍然很容易阅读,但是当我绘制 52 周时,如果只打印大约每 5 个标签会更好..
    • 基本问题:符号 [::n] 是什么意思?我试过这个,并设法隐藏每 5 个刻度,而我想显示每 5 个刻度。我尝试将 label.set_visible 从 False 更改为 True,希望能反转结果,但随后每个标签都显示了。
    • @Gro : [::n] 表示列表的每个第 n 个元素
    • MultipleLocator 的解决方案很奇怪,因为它会使星期错位。如果您改回具有可见属性的解决方案,我会赞成。当 n = 2 时,它给出了足够整齐的图。
    • @Gro :给你,我也加了。
    猜你喜欢
    • 2011-01-11
    • 2019-05-07
    • 2020-11-28
    • 1970-01-01
    • 2012-05-08
    • 2021-09-28
    • 2019-06-23
    • 2012-07-10
    • 2020-04-10
    相关资源
    最近更新 更多