Geo
Geo

Reputation: 175

Multiple label positions for same axis in Matplotlib

I have a long bar chart with lots of bars and I wanna improve its reability from axis to the bars.

Suppose I have the following graph:

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

y = np.linspace(1,-1,20)
x = np.arange(0,20)
labels = [f'Test {i}' for i in x]

fig, ax = plt.subplots(figsize=(12,8))
sns.barplot(y = y, x = x, ax=ax )
ax.set_xticklabels(labels, rotation=90)

which provides me the following: what I have

All I know is how to change the label position globally across the chart. How can I change the axis layout to be cantered in the middle and change its label position based on a condition (in this case, being higher or lower than 0)? What I want to achieve is: goal

Thanks in advance =)

Upvotes: 0

Views: 759

Answers (1)

JohanC
JohanC

Reputation: 80299

You could remove the existing x-ticks and place texts manually:

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

y = np.linspace(1,-1,20)
x = np.arange(0,20)
labels = [f'Test {i}' for i in x]

fig, ax = plt.subplots(figsize=(12,8))
sns.barplot(y = y, x = x, ax=ax )
ax.set_xticks([]) # remove existing ticks
for i, (label, height) in enumerate(zip(labels, y)):
    ax.text(i, 0, '  '+ label+' ', rotation=90, ha='center', va='top' if height>0 else 'bottom' )
ax.axhline(0, color='black') # draw a new x-axis
for spine in ['top', 'right', 'bottom']:
    ax.spines[spine].set_visible(False) # optionally hide spines
plt.show()

label bars around y=0

Here is another approach, I'm not sure whether it is "more pythonic".

  • move the existing xaxis to y=0
  • set the tick marks in both directions
  • put the ticks behind the bars
  • prepend some spaces to the labels to move them away from the axis
  • realign the tick labels depending on the bar value
fig, ax = plt.subplots(figsize=(12, 8))
sns.barplot(y=y, x=x, ax=ax)
ax.spines['bottom'].set_position('zero')

for spine in ['top', 'right']:
    ax.spines[spine].set_visible(False)
ax.set_xticklabels(['    ' + label for label in labels], rotation=90)
for tick, height in zip(ax.get_xticklabels(), y):
    tick.set_va('top' if height > 0 else 'bottom')
ax.tick_params(axis='x', direction='inout')
ax.set_axisbelow(True)  # ticks behind the bars
plt.show()

moving the existing xaxis

Upvotes: 1

Related Questions