# Plotting a graph with legends that don't overlap the graph?

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.

• Look up PlotLegends in the docs, specifically Scope -> Legend Placement -> last example. Commented Jun 17, 2021 at 11:57
• 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.
– Arji
Commented Jun 17, 2021 at 11:57
• @Szabolcs I want them in the plot, not outside.
– Leo
Commented Jun 17, 2021 at 12:01
• The example I refer to shows how to put them inside as well. Commented Jun 17, 2021 at 12:28

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}]
]


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.

• 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 :)
– Leo
Commented Jun 21, 2021 at 11:33
• @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. Commented Jun 21, 2021 at 11:37
• 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. Commented Jun 21, 2021 at 11:39
• Agreed. is there a way to reduce the legend size then since they are rather oversized?
– Leo
Commented Jun 21, 2021 at 11:41
• @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). Commented Jun 21, 2021 at 11:43

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:

• 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?
– Leo
Commented Jun 17, 2021 at 12:40
• 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.
– Leo
Commented Jun 17, 2021 at 12:47
• 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. Commented Jun 17, 2021 at 13:09
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"}]


• 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$?
– Leo
Commented Jun 21, 2021 at 11:19
• 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`. Commented Jun 21, 2021 at 13:26