Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib ticks in the wrong location on additional axis

I would like to have an upper X axis with ticks at identical positions (on the axis) as the original x axis ticks (the tick labels can be different though). It seems easy enough to do, but I am not sure why the code below doesn't work:

import numpy as np
import matplotlib.pyplot as plt

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

X = np.linspace(11,80,1000)
Y = 2*np.sin(X)*np.exp(-X/20.)

ax1.plot(X,Y)
ax2 = ax1.twiny()

old_ticks = ax1.get_xticks()
ax2.set_xticks(old_ticks)

plt.show()

The output is shown below: clearly, the ticks on the top axis are not in the same location on the axis as the ticks below (namely, on the top axis there are 7 ticks versus only 6 ticks on the bottom).

Why is this so?

Offset ticks on the second x axis

EDIT: Setting xlim (as suggested below) works only on initial plot, but not when one zooms in on different regions. I added a callback function to, upon zooming in/out, add the ticks on ax2 in the same location as they are on ax1, but this doesn't seem to work.

Also, the reason I'm using twiny is because eventually the shown tick values for ax2 will depend on the ax1 tick values in a non-linear way. I just want the ticks to be in the same position on the axis. import numpy as np import matplotlib.pyplot as plt

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

X = np.linspace(11,80,1000)
Y = 2*np.sin(X)*np.exp(-X/20.)

ax1.plot(X,Y)
ax2 = ax1.twiny()

ax2.set_xlim(ax1.get_xlim())
ax2.set_xticks(ax1.get_xticks())

def on_xlim_changed(ax1):
    ax2.set_xlim(ax1.get_xlim())
    ax2.set_xticks(ax1.get_xticks())

ax1.callbacks.connect('xlim_changed',on_xlim_changed)

plt.show()
like image 363
Ron OGara Avatar asked Feb 24 '14 02:02

Ron OGara


2 Answers

try:

ax2.set_xlim(ax1.get_xlim())

also, if you just need ticks to be shown on the top, you do not need a twiny axis, and you may simply do

ax1.xaxis.set_ticks_position('both')
like image 143
behzad.nouri Avatar answered Sep 21 '22 06:09

behzad.nouri


The trick here is to disable zooming on the secondary axis with ax2.set_navigate(False). Adjusting the limits in the callback only has an effect if zooming does not affect the limits of the axis itself:

import numpy as np
import matplotlib.pyplot as plt

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

x = np.linspace(11, 80, 1000)
y = 2 * np.sin(x) * np.exp(-x / 20.)

ax1.plot(x, y)
ax2 = ax1.twiny()
ax2.set_navigate(False)  # Important!

old_ticks = ax1.get_xticks()
ax2.set_xticks(old_ticks)
ax1.grid(linewidth=1, ls='--') # Added to be able to see the (mis-)alignment better.

ax2.set_xlim(ax1.get_xlim())

def on_xlim_changed(ax_):
    ax2.set_xticks(ax_.get_xticks())
    ax2.set_xlim(ax_.get_xlim())

ax1.callbacks.connect('xlim_changed', on_xlim_changed)

plt.show()

Example of zooming without set_navigate(False)

Zooming w/o <code>set_navigate(False)</code>

Zooming with the code as posted in the answer

Zooming with the code as posted in the answer

like image 39
Jan Joswig Avatar answered Sep 22 '22 06:09

Jan Joswig