1
$\begingroup$

Consider:

tmax = 100;
b = 0.1;
n[t_] := S[t] + P[t]
SP = NDSolveValue[{
    S'[t] == (-b/n[t])*S[t]*P[t],
    P'[t] == (b/n[t])*S[t]*P[t],
    S[0] == 0.99,
    P[0] == 0.01},
   {S, P},
   {t, 0, tmax}];
Plot[{Through[SP[t]], Total@Through[SP[t]]} // Evaluate, {t, 0, tmax},
  PlotStyle -> {Blue, Red, Dashed}, Frame -> True, 
 FrameLabel -> {Style[Time, Black, FontSize -> 15], 
   Style[Density, Black, FontSize -> 15]}, 
 PlotLegends -> Placed[{"A(t)", "B(t)"}, {Right, Top}]]

How do I plot the legends such that it doesn't overlap? for different inputs the graph changes so is there an automatic way for it not to overlap?

Side questions:

i) How do I put a box around the legends after they've been fixed?
ii) How to insert extra parameter in legends without it being a graph in the plot?
iii) How to put another dashed line at 0(and make the dashed lines both black in colour)?

EDIT(using Szabolcs's solution):

tmax = 100;
b = 0.2;
g = 0.2;
n[t_] := S[t] + P[t] + R[t]
SPR = NDSolveValue[{
    S'[t] == (-b/n[t])*S[t]*P[t],
    P'[t] == (b/n[t])*S[t]*P[t] - g*P[t],
    R'[t] == g*P[t],
    S[0] == 0.95,
    P[0] == 0.05,
    R[0] == 0},
   {S, P, R},
   {t, 0, tmax}];

{f1, f2, f3} = SPR;

st = Style[#, 15, Black] &;

Plot[{f1[t], f2[t], f3[t], f1[t] + f2[t] + f3[t], 0}, {t, 0, tmax}, 
 PlotStyle -> {Blue, Red, Green, Directive[Black, Dashed], 
   Directive[Black, Dashed]}, Frame -> True, 
 FrameLabel -> st /@ {"Time", "Density"}, 
 PlotLegends -> 
  Placed[LineLegend[{Blue, Red, Green, Black}, {"A(t)", "B(t)", 
     "C(t)"}, LegendFunction -> Framed], {0.85, 0.65}]]

We have overlapping; how can we place the legends automatically such that it doesn't overlap the graph?

EDIT 2

I manually shifted the legends and it worked fine.

$\endgroup$
4
  • $\begingroup$ Look up PlotLegends in the docs, specifically Scope -> Legend Placement -> last example. $\endgroup$
    – Szabolcs
    Jun 17 '21 at 11:57
  • $\begingroup$ Did you consider PlotLegends -> {"A(t)", "B(t)"} such that the legends are outside of the frame? That way they cant intercept with the plot. $\endgroup$
    – Arjihad
    Jun 17 '21 at 11:57
  • $\begingroup$ @Szabolcs I want them in the plot, not outside. $\endgroup$
    – Leo
    Jun 17 '21 at 12:01
  • $\begingroup$ The example I refer to shows how to put them inside as well. $\endgroup$
    – Szabolcs
    Jun 17 '21 at 12:28
3
$\begingroup$

All the legend-related questions can be answered by looking at examples in the PlotLegends doc page. Generally, it is a good idea to look through the examples in the relevant documentation pages.

You can do something like this:

{f1, f2} = SP;

st = Style[#, 15, Black] &;

Plot[{f1[t], f2[t], f1[t] + f2[t], 0},
 {t, 0, tmax}, 
 PlotStyle -> {Blue, Red, Directive[Black, Dashed], Directive[Black, Dashed]},  
 Frame -> True,
 FrameLabel -> st /@ {"Time", "Density"},
 PlotLegends -> 
  Placed[
   LineLegend[{Blue, Red, Black}, {"A(t)", "B(t)", "foo"}, LegendFunction -> Framed], 
   {0.85, 0.65}]
]

enter image description here

How do I plot the legends such that it doesn't overlap?

Use scaled coordinates for the legend position. See the PlotLegends doc page, Scope -> Legend Placement.

How do I put a box around the legends after they've been fixed?

See LegendFunction. There are also several examples in the doc page of PlotLegends under Scope -> Legend content. Check them to see how to use different frame styles.

How to insert extra parameter in legends without it being a graph in the plot?

Use an explicit legend specification. See e.g. LineLegend. There is an example with SwatchLegend in the PlotLegends doc page.

How to put another dashed line at 0(and make the dashed lines both black in colour)?

There are several ways:

As for making the existing dashed line in your plot black, use Directive to combine styles, as in my example.

$\endgroup$
7
  • $\begingroup$ your solution is nice, however for a slightly different inputs for a different model results in overlapping the graph. Is there a way to reduce the size of the legends? also to place it automatically? Please look at my edit :) $\endgroup$
    – Leo
    Jun 21 '21 at 11:33
  • $\begingroup$ @Maths No, there is no way to place it automatically in such a way that it will not overlap with the contents of the plot. The only way to ensure no overlap without manual intervention is to have the legend outside of the frame. $\endgroup$
    – Szabolcs
    Jun 21 '21 at 11:37
  • $\begingroup$ Personally, I did not find this to be a problem. If I am doing exploratory work, i.e. I make plots so that I can look at them myself, it is fine to have the legend outside. If I am making figures for a publication, I find it necessary to manually polish the figure anyway. A better positioning of the legend is just a small part of this. In my experience, making high quality publication figures always takes some amount of manual work. $\endgroup$
    – Szabolcs
    Jun 21 '21 at 11:39
  • $\begingroup$ Agreed. is there a way to reduce the legend size then since they are rather oversized? $\endgroup$
    – Leo
    Jun 21 '21 at 11:41
  • $\begingroup$ @Maths There are ways to tweak the spacing between legend entries, tweak the margin size, the font size, the legend marker size, so yes. I don't recall the syntax off-hand, I would have to check the docs, and I don't have time now. Please check the docs, and feel free to ask a separate question about the details you did not find (some of these tweaks are not that easy to make, as I recall). $\endgroup$
    – Szabolcs
    Jun 21 '21 at 11:43
1
$\begingroup$

I like to use Epilog to insert graphics primitives exactly as I like them. For example:

Plot[{Through[SP[t]], Total@Through[SP[t]]} // Evaluate, {t, 0, tmax},
  PlotStyle -> {Blue, Red, Dashed}, Frame -> True, 
 FrameLabel -> {Style[Time, Black, FontSize -> 15], 
   Style[Density, Black, FontSize -> 15]},
 Epilog -> {Text["A(t)", {65, .25}], Text["B(t)", {70, .8}], 
   Dashing[.02], Line[{{0, 0}, {100, 0}}]}]

Produces:

enter image description here

$\endgroup$
3
  • $\begingroup$ Nice answer, however can you make the top line in black too? Also suppose I wanted to state another parameter on the side, how would I do it? $\endgroup$
    – Leo
    Jun 17 '21 at 12:40
  • $\begingroup$ Also, since I have many different inputs for "b" in this example, so the labels aren't in the correct place, is there a way to do it automatically? For example, consider b=1. $\endgroup$
    – Leo
    Jun 17 '21 at 12:47
  • $\begingroup$ To add a dashing line instead of the one you plotted at the top, delete it and add in the Epilog sequence a Line[{{0,1},{100,1}}]. As for placing the "b" according to values, you just pass the values to the coordinates of the Text[] command after some adjustment, although it sounds like you want several "b" and I am not sure what you envision. You could add Point[] commands at the coordinates you want and put text near those. $\endgroup$
    – Nicholas G
    Jun 17 '21 at 13:09
1
$\begingroup$
Clear["Global`*"]

tmax = 100;
n[t_] := S[t] + P[t]

For "different inputs" use ParametricNDSolveValue

SP = ParametricNDSolveValue[{S'[t] == (-b/n[t])*S[t]*P[t], 
    P'[t] == (b/n[t])*S[t]*P[t], S[0] == 99/100, P[0] == 1/100}, 
    {S, P}, {t, 0, tmax}, {b}];

Use Manipulate to see the effects of varying the parameter

Manipulate[
 Plot[
  {Through[SP[b][t]], Total@Through[SP[b][t]], 0} //
   Evaluate, {t, 0, tmax},
  PlotStyle ->
   {Blue, Red, {Black, Dashed}, {Black, Dashed}},
  Frame -> True,
  FrameLabel ->
   (Style[#, Black, FontSize -> 15] & /@
     {Time, Density}),
  PlotLegends -> Placed[
    LineLegend[
     {"A(t)", "B(t)"},
     LegendLabel -> 
      StringForm["b\[ThinSpace]=\[ThickSpace]``", NumberForm[b, {3, 2}]]],
    {If[b >= 0.1, Right, Left], Center}]],
 {{b, 0.1}, 0.01, 1, 0.01,
  Appearance -> "Labeled"}]

enter image description here

$\endgroup$
2
  • $\begingroup$ Good answer. However, is there a way to remove the "$b$" parameter from the top of the graph? because I'll be using it in a document so it wont look 'good'. Also is the a way to name the $b$ as $b_0$? $\endgroup$
    – Leo
    Jun 21 '21 at 11:19
  • $\begingroup$ The slider control is needed for the Manipulate so you can change the values of b. If you want its label to read b0 change the control specification to {{b, 0.1, Subscript["b", 0]}, ...} to remove the label use {{b, 0.1, ""}, ...} (in either case, the Appearance -> "Labeled" is optional). Also change to LegendLabel -> StringForm["``\[ThinSpace]=\[ThickSpace]``", Subscript["b", 0], NumberForm[b, {3, 2}]]. To remove the panel add Paneled -> False to the Manipulate. $\endgroup$
    – Bob Hanlon
    Jun 21 '21 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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