Matplotlib twiny() axis does not have the padding the original axis has

Viewed 215

Consider the following toy code:

import matplotlib.pyplot as plt

fig = plt.figure()
plt.grid()
ax1 = fig.add_subplot(111)

X1 =[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
X2 = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
Y = [0.7, 0.6, 0.5, 0.4, 0.4, 0.3, 0.7, 0.8, 0.9, 0.2]

ax1.plot(X1,Y)
ax1.set_ylim([0, 1.0])


ax2 = ax1.twiny()
ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks(X2)

ax1.set_xlabel('X1 Label')
ax1.set_ylabel('Y Label')
ax2.set_xlabel('X2 Label')

plt.show()

Which gives the following plot:

enter image description here

As one can see, the X1 axis has a padding at the end (where the 10 is located). However, the second axis X2 stops at the end of the actual graph, which is not desired.

Basically, I want the 10000 value aligned with the 10, ending in the same place with that padding.

The answer in this similar question does not help me because I am already doing what it was suggested (adding the set_xlim for the twiny() axis).

Also (albeit not as important as the original question), I would like to know why Matplotlib decided to plot all the x ticks on the X2 axis instead of "jumping" a few ones just like on the X1 axis.

2 Answers

You can connect() the bottom and top axes by using the ax1.xlim_changed callback to trigger ax2.set_xlim().

Update: To restore padding on the axes limits, you can use something like this lim() function (default 5%):

fig, ax1 = plt.subplots()

X1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
X2 = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
Y = [0.7, 0.6, 0.5, 0.4, 0.4, 0.3, 0.7, 0.8, 0.9, 0.2]

ax1.plot(X1, Y)
ax1.set_ylim([0, 1.0])

# add axes margins by `pad` percent
def lim(x, pad=5):
    xmin, xmax = min(x), max(x)
    margin = (xmax - xmin) * pad/100
    return xmin - margin, xmax + margin

ax2 = ax1.twiny()
ax1.callbacks.connect('xlim_changed', lambda ax1: ax2.set_xlim(*lim(X2)))
ax1.set_xlim(*lim(X1)) # trigger the xlim_changed callback

connected ax1/ax2 with margins

The two axis are completely separate and X2 will not just automatically align with the X1 axis when you change the ticks on an empty plot on your secondary axis.

if you plot a second line on X2 instead they will align

import matplotlib.pyplot as plt

fig = plt.figure()

plt.grid()
ax1 = fig.add_subplot(111)

X1 =[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
X2 = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
Y = [0.7, 0.6, 0.5, 0.4, 0.4, 0.3, 0.7, 0.8, 0.9, 0.2]

ax1.plot(X1,Y)
ax1.set_ylim([0, 1.0])

ax2 = ax1.twiny()
#ax2.set_xlim(ax1.get_xlim())
#ax2.set_xticks(X2)
ax2.plot(X2,Y) # Plot a second line on top of the previous one

ax1.set_xlabel('X1 Label')
ax1.set_ylabel('Y Label')
ax2.set_xlabel('X2 Label')

plt.show()

enter image description here

You could also do the following by using the correct xlim for X2 and remove the margin from X1 to match X2. You can probably add some margin to X2 instead some how, but I'm not sure how to do it without plotting anything on X2.

import matplotlib.pyplot as plt

fig = plt.figure()

plt.grid()
ax1 = fig.add_subplot(111)

X1 =[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
X2 = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
Y = [0.7, 0.6, 0.5, 0.4, 0.4, 0.3, 0.7, 0.8, 0.9, 0.2]

ax1.plot(X1,Y)
ax1.set_ylim([0, 1.0])

ax2 = ax1.twiny()
ax2.set_xlim(X2[0], X2[-1]) # Correct xlim for secondary axis
ax2.set_xticks(X2)
ax1.margins(0) # Remove margin from first axis to match X2

ax1.set_xlabel('X1 Label')
ax1.set_ylabel('Y Label')
ax2.set_xlabel('X2 Label')

plt.show()

enter image description here

Related