0

I have been battling this for too long, to the point that I'm pretty sure I don't understand the implementation of twinx in matplotlib

ok I have some code

import matplotlib.pyplot as plt

data1 = [5, 6, 6, 7]
err1 = [1, 1, 1, 1]
data2 = [0.5, 0.6]
err2 = [0.01, 0.01]

label1 = ['var1', 'var2', 'var3', 'var4']
label2 = ['var5', 'var6']

If I just want to plot data 1 everything is fine.

fig, ax1 = plt.subplots()
ax1.bar(label1, data1, yerr=err1, color='red')
plt.show()

This gives me a pretty chart enter image description here

If I want to add data 2 onto a second axis I lose columns and the order is also odd.

ax1.bar(label1, data1, yerr=err1, color='red')
ax2 = ax1.twinx()
ax2.bar(label2, data2, yerr=err2, color='blue')
plt.show()

Does not give me six columns but only 4 with two of the data1 columns now missing?

enter image description here

Obviously Im 100% sure its my fault, but please someone put me out of this misery...

2 Answers 2

1

The problem is that your bars are overlapping and therefore you see two less bars. The solution is to use well-defined x-values for positioning your bars and then after you have plotted on twin axis, assign the x-tick labels.

import matplotlib.pyplot as plt

data1 = [5, 6, 6, 7]
err1 = [1, 1, 1, 1]
data2 = [0.5, 0.6]
err2 = [0.01, 0.01]

label1 = ['var1', 'var2', 'var3', 'var4']
label2 = ['var5', 'var6']

fig, ax1 = plt.subplots()
ax1.bar(range(len(label1)), data1, yerr=err1, color='red')

ax2 = ax1.twinx()
ax2.bar(range(4,6), data2, yerr=err2, color='blue')
ax2.set_xticks(range(6))
ax2.set_xticklabels(label1+label2)
plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Excellent - In the docs, I interpreted the `the x coordinates of the bars' as referring to the labels - which works with a single plot but clearly not with two axes.
@nick.sculthorpe: You don't need to pass range(4) etc. while plotting on single axis. There you can do as you did already. But for twin axis, you want to plot the blue bars successively at x=4 and 5. Therefore you need to use 0, 1, 2, 3 for the left y-axis
1

You can also do:

label = ['var1', 'var2', 'var3', 'var4', 'var5', 'var6']
data1 = [5, 6, 6, 7, np.NaN, np.NaN]
data2 = [np.NaN, np.NaN, np.NaN, np.NaN, 0.5, 0.6]

The issue here is that a matplotlib axes converts to integers: 'var1'->1, 'var2'->2 etc, and ax2 is a new axes so 'var4'->1, 'var5'->2 and you get the overlap. Making your categories the same between the twinned axes clears this confusion up.

2 Comments

Thanks for the explanation - @Bazingaa explained how to fix it, but I stil found it odd that Matplotlib would add a right hand axis but then add the columns to the left hand side. At least the underlying mechanics now make some kind of sense.
Those two axes don't really know much about each other, except the x-limits they share. Its possible that an improvement would be that they share "converters" (i.e. the thing that translates from "category" to integers), but that is a bit of complexity that hasn't been tried yet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.