1

I am trying to plot some data on which I would like to make an inset to show some details. It is quite easy to create the inset and set the characteristics of the plot inside the inset.

What I have found cumbersome is how to set the properties of the inset itself, in particular the color and thickness of the inset frame, of the small box indicating where the plot is sampled and the lines to connect the small box to the inset. What I would need is to change them singularly. I know it is possible to change the properties of the mark_inset as a single piece, but not as separated parts.

Let's say, as example, the inset frame should be thinner and dashed, the sampling box thicker and red and the connecting lines dotted and of the same color as in the MWE.

Here is the MWE:

import scipy as sc
import matplotlib.pyplot as plt
from cycler import cycler
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
plt.close('all')

color = plt.get_cmap('viridis')(sc.linspace(0, 1, 4)[1:3])
params_IEEEtran = {'legend.fontsize': 10,
                   'axes.labelsize': 10,
                   'axes.titlesize': 10,
                   'xtick.labelsize': 9,
                   'ytick.labelsize': 9,
                   'mathtext.fontset': 'stix',
                   'font.family': 'Times New Roman',
                   'mathtext.rm': 'serif',
                   'mathtext.bf': 'serif:bold',
                   'mathtext.it': 'serif:italic',
                   'mathtext.sf': 'sans\\-serif',
                   'grid.color': 'k',
                   'grid.linestyle': ':',
                   'grid.linewidth': 0.5,
                   'axes.xmargin': 0,
                   'axes.axisbelow': False,
                   'lines.linewidth': 1.0,
                   'legend.frameon': False,
                   'axes.prop_cycle': cycler('color', plt.get_cmap('viridis')(
                           sc.linspace(0, 1, 4))),
                   'figure.figsize': [3, 2.4],
                   }
plt.rcParams.update(params_IEEEtran)

x = sc.linspace(0.1, 100)
y1 = sc.array(sc.sqrt(x)).T
y2 = sc.array(1/sc.sqrt(x)).T

fig = plt.figure('FIG')
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y1)

ax_twinx = ax.twinx()
ax_twinx.plot(x, y2)

ax.set_xlim(0, 100)
ax.set_ylim(0, 10)
ax_twinx.set_ylim(0, 1)

inset = fig.add_axes([0.6, 0.3, 0.12, 0.35], frameon=False)
inset.axes.get_xaxis().set_visible(False)
inset.axes.get_yaxis().set_visible(False)

inset.plot(x, y1)

inset_twinx = inset.twinx()
inset_twinx.axes.get_yaxis().set_visible(False)

inset_twinx.plot(x, y2)

inset.set_xlim(5, 15)
inset.set_ylim(2, 4)
inset_twinx.set_ylim(0.2, 0.4)

mark_inset(ax, inset, loc1=4, loc2=2, lw=0.3,
           fc="none", ec=color[0], zorder=200)
fig.savefig('./example.png', bbox_inches='tight', dpi=300)

And here result:

enter image description here

1 Answer 1

3

Changing the properties of the individual objects is usually not a big problem.

  • the inset frame should be thinner and dashed,

    plt.setp(inset_twinx.spines.values(), linewidth=0.5, linestyle="--") #python 2
    

    or

    plt.setp(list(inset_twinx.spines.values()), linewidth=0.5, linestyle="--") #python3
    

    or

    for d in ["left", "right", "top", "bottom"]:
        inset_twinx.spines[d].set_linewidth(0.5)
        inset_twinx.spines[d].set_linestyle("--")
    

To change the inset connector and box, we need a handle to those objects first,

box, c1, c2 = mark_inset(ax, inset, loc1=4, loc2=2, lw=0.3,
           fc="none", ec=color[0], zorder=200)
  • the sampling box thicker and red

    plt.setp(box, linewidth=3, color="red")
    

    or

    box.set_linewidth(3)
    box.set_color("red")
    
  • the connecting lines dotted and of the same color as in the MWE.

    plt.setp([c1,c2], linestyle=":")
    

    or

    for c in [c1,c2]:
        c.set_linestyle(":") 
    

Result:
enter image description here

7
  • I get an exception when setting setp: TypeError: 'odict_values' object does not support indexing. Commented Jun 23, 2017 at 8:56
  • Perfect, very helpful! Just two curiosities: Why did you set the properties on inset_twinx and not on inset? Must they be forced on the last overlay? Do you think is possible to change the inset frame using an handle and not the static function plt.setp() (eg. ax.setp()) or something similar?) Commented Jun 23, 2017 at 9:04
  • I added the different options using plt.setp and handles for each of the attributes. Which one to use is just a matter of taste, I'd say. Concerning inset_twinx: Since you set the frame for inset off (, frameon=False) the frame shown is the one from the twin axes. Thus we need to change that one and not the invisible other. Commented Jun 23, 2017 at 9:14
  • I found more than one way: [x.set_linestyle('--') for x in inset_twinx.spines.values()] ; [x.set_linewidth(0.5) for x in inset_twinx.spines.values()]. This is not well documented: [x.set(linewidth=0.5, linestyle="--") for x in inset_twinx.spines.values()] Commented Jun 23, 2017 at 9:15
  • 1
    I guess there are always reasons to do something or not. As said, in this case, I would consider the options as equivalent. Personnally, I also cannot agree with the "only one right way to do things" mentality; others would see this differently. But this is getting off-topic now. Commented Jun 23, 2017 at 9:36

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.