1

Based on the following example from matplotlib, I have made a function that plots two weekly time series as a side-by-side bar chart. https://matplotlib.org/3.1.1/gallery/lines_bars_and_markers/barchart.html#sphx-glr-gallery-lines-bars-and-markers-barchart-py

My problem is that I set the xticks explicitly, and that creates messy xtick-labels. Is there a way to get matplotlib to choose xticks (position and labels) explicitly in such a plot?

I must say that I find the whole operation with specifycing the position of the bar using (x - width/2) quite inelegant to get side-by-side-bars - are there other options (other packages than matplotlib or other specifications in matplotlib) to avoid writing such explicit code?

Below is code and result. I'm seeking a solution that selects the number and placements of xticks and xticklabels that leaves it readable:

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

enter image description here

Gro
  • 23
  • 5

1 Answers1

1

Solution 1 : Using Pandas

You can first create a pandas DataFrame and then plot a multiple bar chart directly. The formatting of labels on the x-axis is much neater

df = pd.DataFrame(
    {'labels': labels,
     'men_means': men_means,
     'women_means': women_means
    })

df.plot(x="labels", y=["men_means", "women_means"], kind="bar")

enter image description here

Solution 2: Using Seaborn (adapted from this answer)

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)

enter image description here

To hide only every n-th tick, you can do the following as adapted from this answer

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

To show every n-th label, you can use the following trick

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

enter image description here

Sheldore
  • 37,862
  • 7
  • 57
  • 71
  • Thanks! Solution 1 using pandas is neat. Do you also know if it is possible to not plot all xlabels? With 15 weeks this is still quite readable, but when I am plotting 52 weeks, it would have been better if only every 5th label or so was printed.. – Gro Apr 12 '20 at 13:11
  • Basic question: What does the notation [::n] mean? I tried this, and managed to hide every 5th tick, whereas I would like to show every 5th tick. I tried changing label.set_visible from False to True, hoping to reverse the result, but then every label showed. – Gro Apr 12 '20 at 13:34
  • 1
    @Gro : [::n] means every nth element of the list – Sheldore Apr 12 '20 at 13:37
  • The solution with the MultipleLocator was weird, because it dislocates the weeks. If you change back to the solution with the visible-attribute, I will upvote that. With n = 2, it gives neat enough plots. – Gro Apr 12 '20 at 13:48
  • Done! Thanks for you time and effort. If anyone else can enlighten me on how to only show every nth label instead of hiding every nth label, I'd be greatful. – Gro Apr 12 '20 at 13:56
  • 1
    @Gro : Here you go, I added that too. – Sheldore Apr 12 '20 at 14:00