Plots.jl with inset

I’m trying to use Plots.jl with an inset. For simplicity, consider the plot

x=range(0,3pi/2,length=100)
plot(x,[sin.(x),sinc.(x)], label=["sin" "sinc"])

Suppose I want to insert an inset in the lower left corner of the plot with details of the same plot around the first crossing of the curves, i.e., ca. (x,y) ca = (0.6,0.5). So I want to replicate the same plot, say, with xlim changed to ca. xlim=(0.5,0.7) and ylim=(0.4,0.6) for the inset, and put it in the lower left corner.

How can I do that?

inset_subplots here: Layouts · Plots

Thanks, I figured it out…

It could have been simpler, but it works.

Nice! Feel free to post any suggestions, recent frustration is a valuable resource :slight_smile:

2 Likes

Sorry for (week-end) delay. Here is more or less what I did (I don’t include all the code, just the essential parts).

Here is what I did

Note: at the outset, I have already defined 3 functions: uₒ, uₒ_c, and uₒ_ which depend on temperature in a temperature span T_span.

  • I plot the “main plot”:
plot(T_span.-273.15,uₒ.(T_span),...)
plot!(T_span.-273.15,uₒ_c.(T_span),...
plot!(T_span.-273.15,uₒ_.(T_span),...)
  • Next, I plot the inset:
plot!(
    T_span.-273.15, [uₒ.(T_span), uₒ_c.(T_span),uₒ_.(T_span)], ...
    xlim=(0,100),ylim=(1.165,1.192),
    frame=:box,
    yticks=false,
    inset=bbox(0.143,0.47,0.35,0.35),
    subplot=2
)

Some “recent frustrations” and (unrealistic?) suggestions…

Things in documentation that confused me

  1. At first, I didn’t know where to find the information in the documentation for Plots.jl.

If I search Plots.jl and open the
image

page, attempt to find the inset information by:

  • typing in inset in the search box – now luck

  • do Ctrl+f in the browser, and searched for inset. Again, now luck, because the browser search function only search in the active window, i.e., in the Tutorial up until (but not including) Series Types.

  • your suggstion of inset_subplots helped somewhat, but what really helped is the link to Layouts . Plots.

Still, inset_subplots does not occur in the plot command… it is possible that inset_subplots really means "the range of commands from inset to subplot?? That is, however, not obvious to me.

NOTE: this problem I have with search is not unique for Plots.jl.

  1. Somewhat confusing argument list?
# The call is `bbox(x, y, width, height, origin...)`, where numbers are treated as
# "percent of parent"

This is somewhat confusing… the numbers (x, y, width, height) are not “percent of parent” (a number from 0-100), but fraction of parent (range: 0-1).

So my understanding of these numbers are:

  • x, y gives the coordinates of the inset relative to the upper left corner of the parent figure, which is position (0,0), with coordinate axes going downwards (x) and to the right ( y), and where the lower right corner of the parent plot is in position (1,1).

  • width, height specify the width and height of the inset in fraction of the parent plot window.

  • Is it somewhat confusing the the first coordinate of x, y relates to the vertical position, while the first of width, height relates to horizontal size? More logical with x, y, height, width? Perhaps.

  • the origin argument is not really explained. But from examples, I assume that it is possible with two arguments, and that the purpose is to define how the inset is positioned relative to the inset origin specified by x, y. Where the default is that the inset origin is the upper left corner of the inset, so possible origin commands are :bottom, :right, and similar.

  1. What does the subplot command do? Do I have to set the subplot = 2 if I have a single parent plot, i.e., without subplots?

Could the inset work in a simpler way?

Perhaps. The plot command may get somewhat convoluted for complex plots with insets.

Would it be possible to do my plot above in the following way?

plot(T_span.-273.15,uₒ.(T_span),...)
plot!(T_span.-273.15,uₒ_c.(T_span),...
fig_parent = plot!(T_span.-273.15,uₒ_.(T_span),...)

followed by:

fig_sub = plot(T_span.-273.15, [uₒ.(T_span), uₒ_c.(T_span),uₒ_.(T_span)], ...
    xlim=(0,100),ylim=(1.165,1.192),
    frame=:box,
    yticks=false)

followed by:

plot!(fig_parent,
 fig_sub,
 inset=bbox(0.143,0.47,0.35,0.35),
 subplot=2

That way, the plot command holding the inset command itself becomes relatively simple, while it is still possible to created complex plots for the parent plot and the inset.

OK – I have no idea whether this is possible from the architecture of Plots

Yes, this is unfortunate. It’s really difficult to make everything that’s possible also easily searchable, but this could definitely be improved in this case. We should consider having a header for “Inset plots”, because it’s not obvious that the fifth (or whatever) item in that list, “Layouts” is the correct one.

Yes, that’s a mistake. I think your understanding of the arguments to bbox are nearly correct, but x goes from left to right and y goes from top to bottom, so they are in the same order as the width/height arguments. origin is poorly explained, I think a good solution would be to expand the docstring of bbox (which right now is pretty usesless).

Kind of, yes. This makes it so none of your keyword arguments affect the parent subplot. Consider

plot([sin, cos])
plot!([sin, cos]; xlim=(-0.5,0.5), inset=bbox(0.2,0.2,0.3,0.3,:right))

and

plot([sin, cos])
plot!([sin, cos];xlim=(-0.5,0.5), inset=bbox(0.2,0.2,0.3,0.3,:right), subplot=2)

These two will be very similar, but in the former, the xlim kwarg also updates the xlims of the parent subplot, so both of them go from -0.5 to 0.5, while the latter has the parent going from -4.5 to 4.5 and the inset going from -0.5 to 0.5. This is not at all transparent, but possibly necessary for this to be a consistent interface (What if I want to create an insert and update the xlims in a single call, this is how I would expect to do so). Perhaps the solution here is to create an inset!(parent, ...) alias, where the parent is not affected by any of the kwargs. Worth considering.

I agree with your suggestion, to be able to place a Plot object in an inset. In fact, when searching to see if anyone else had a similar suggestion in the Plots github repo, I found that I had raised just such an issue a year ago. I thought this sounded familiar. It should not be impossible to make one of these work.

Thank you for your comments! I hope the status quo is acceptable for now, I’ll try to make some time to make a PR at least fixing the documentation.

1 Like

Status quo is fine for now :-).

but x goes from left to right and y goes from top to bottom

Argh. Of course.

Could you please share the code for making this plot? I have two plots defined as p1 and p2. I am trying to have a similar plot to the one shown here.

Assuming two plots, p1 and p2 – for illustration

x = range(0,2pi,length=100)
p1 = plot(x,sin)
p2 = plot(x,cos)

What I would have liked is the following to work (but it doesn’t):

plot(p1)
plot!(p2,
    inset=bbox(0.15,0.45,0.35, 0.4),
    subplot=2
)

… the result is:

It seems like it is necessary to create p2 inside of the plot statement which has the inset statement:

plot(p1)
plot!(x, cos,
    inset=bbox(0.15,0.45,0.35, 0.4),
    subplot=2
)

which gives the desired result:

This lack of support for writing plot!(p2, inset...) is not a problem for a simple inset, but rather limiting for more complex insets.

I hope this answers your question.

@rafael.guerra – did you delete your response?

Anyways, I got an e-mail copy of your suggestions, and it works! Nice! Here is how it can be done:

x = range(0,2pi,length=100)
p1 = plot(x,sin)

produces:

Then do:

plot!(p1,
inset=bbox(0.15,0.45,0.35, 0.4),
    subplot=2)

leading to:

and finally:

plot!(p1[2],x,cos)
plot!(p1[2],x,sinc)

leads to:

Yes, the syntax is closer to what you wanted, but just using subplot=2 seems cleaner for this purpose.