2

I'm trying to add a second x-axis to my inset plot that I created with InsetPosition from mpl_toolkits.axes_grid1.inset_locator (following e.g. https://scipython.com/blog/inset-plots-in-matplotlib/), but the second x-axis doesn't seem to show up and I can't figure out why.

This is the code I'm using:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca()

from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
zoom_ax = fig.add_axes([0,0,1,1])
zoom_ax.set_axes_locator(InsetPosition(ax, [0.6, 0.6, 0.3, 0.3]))

def expansion(z):
    return 1.0 / (1.0 + z)

def redshift(a):
    return 1.0 / a - 1.0

def tick_function(a):
    return ["%.1f" % z for z in redshift(a)]

z_ticks = np.array([0.0, 0.5, 1.0, 2.0, 5.0, 100.0])
a_ticks = expansion(z_ticks)

twin_ax = zoom_ax.twiny()
twin_ax.set_xticks(a_ticks)
twin_ax.set_xticklabels(tick_function(a_ticks))
twin_ax.set_xlim(zoom_ax.get_xlim())

xmin, xmax = 0.0, 1.0
x = np.linspace(xmin, xmax)
zoom_ax.plot(x, np.sin(x))
zoom_ax.set_xlim(xmin, xmax)

plt.show()

This produces the following plot - without any twiny() axis:

result of the above code

2 Answers 2

2

Apparently twiny() has problems with the axes_locator used by zoom_ax (don't know if that is a bug or not). If you repeat the set_axes_locator() command for twin_ax, the resulting plot looks like what I would expect (I left out the axes ticks commands to make my example plot more comprehensible):

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca()

from mpl_toolkits.axes_grid1.inset_locator import InsetPosition
zoom_ax = fig.add_axes([0,0,1,1])
zoom_ax.set_axes_locator(InsetPosition(ax, [0.6, 0.6, 0.3, 0.3]))

def expansion(z):
    return 1.0 / (1.0 + z)

def redshift(a):
    return 1.0 / a - 1.0

def tick_function(a):
    return ["%.1f" % z for z in redshift(a)]

z_ticks = np.array([0.0, 0.5, 1.0, 2.0, 5.0, 100.0])
a_ticks = expansion(z_ticks)

twin_ax = zoom_ax.twiny()
##twin_ax.set_xticks(a_ticks)
##twin_ax.set_xticklabels(tick_function(a_ticks))
twin_ax.set_xlim(zoom_ax.get_xlim())

xmin, xmax = 0.0, 1.0
x = np.linspace(xmin, xmax)
zoom_ax.plot(x, np.sin(x))
zoom_ax.set_xlim(xmin, xmax)

##the extra lines
twin_ax.set_axes_locator(InsetPosition(ax, [0.6, 0.6, 0.3, 0.3]))
x2 = np.linspace(xmin, 2*xmax)
twin_ax.plot(x2,np.cos(x2),'r')
twin_ax.set_xlim(xmin, 2*xmax)

plt.show()

This produces the following plot:

result of the above code

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

Comments

2

Maybe you want to use the usual mpl_toolkits.axes_grid1.inset_locator.inset_axes, which works fine even with twinning.

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca()

from mpl_toolkits.axes_grid1.inset_locator import inset_axes
zoom_ax = inset_axes(ax, "100%", "100%", bbox_to_anchor=[0.6, 0.6, 0.3, 0.3], 
                     bbox_transform=ax.transAxes)

def expansion(z):
    return 1.0 / (1.0 + z)

def redshift(a):
    return 1.0 / a - 1.0

def tick_function(a):
    return ["%.1f" % z for z in redshift(a)]

z_ticks = np.array([0.0, 0.5, 1.0, 2.0, 5.0, 100.0])
a_ticks = expansion(z_ticks)

twin_ax = zoom_ax.twiny()
twin_ax.set_xticks(a_ticks)
twin_ax.set_xticklabels(tick_function(a_ticks))
twin_ax.set_xlim(zoom_ax.get_xlim())

xmin, xmax = 0.0, 1.0
x = np.linspace(xmin, xmax)
zoom_ax.plot(x, np.sin(x))
zoom_ax.set_xlim(xmin, xmax)

plt.show()

From matplotlib 3.0 on you may simplify this even further, using Axes.inset_axes:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.gca()

zoom_ax = ax.inset_axes([0.6, 0.6, 0.3, 0.3])

def expansion(z):
    return 1.0 / (1.0 + z)

def redshift(a):
    return 1.0 / a - 1.0

def tick_function(a):
    return ["%.1f" % z for z in redshift(a)]

z_ticks = np.array([0.0, 0.5, 1.0, 2.0, 5.0, 100.0])
a_ticks = expansion(z_ticks)

twin_ax = zoom_ax.twiny()
twin_ax.set_xticks(a_ticks)
twin_ax.set_xticklabels(tick_function(a_ticks))
twin_ax.set_xlim(zoom_ax.get_xlim())

xmin, xmax = 0.0, 1.0
x = np.linspace(xmin, xmax)
zoom_ax.plot(x, np.sin(x))
zoom_ax.set_xlim(xmin, xmax)

plt.show()

The result is the same visually:

enter image description here

2 Comments

I originally avoided using inset_axes, as I thought the use of the bbox was a bit of a workaround - but this definitely solves the problem, thanks!
Well, I can tell you that bbox_to_anchoris definitely meant to be used like this. Many use cases are shown in the example I redacted not too long ago.

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.