# Creating Marker (Tags) on Top of Plots

I am wondering and tried to plot a graph having labeling specified on top of the graphic. I kind of know a brute force method to accomplish it, but the results will not even look close to clean looking. Is there a more programmatic way or smoother way to accomplish getting a plot with labeling on it as such: Instead though, I will like to have the $\mu = \frac{(2n+1)\pi}{2L}$ to be shifted higher as so in the plot below. I guess, could it be made to easily be able to adjust coordinates (placement's) or marker on top of the plot. The interesting question is: how do we get labels into a plot without having the frame and/or axis range of that plot automatically adjust to enclose the labels. After all, you want labels on top of the plot, which means they will have to be "hovering" above the maximum vertical height of the axes.

I think Overlay is really good for such tasks in principle. Let's say the y-axis is supposed to extend from $-5$ to $5$, then the first thing we have to do is create some empty space on top of that range. This can be done by setting PlotRegion. In the plot below, I want the extra height (in the coordinate system of the plot) to be up to yExtra = 8.5.

The problem with Overlay is that it aligns two objects using scaled coordinates that are "agnostic" of the plot coordinates we're interested in (e.g., the center of the plot is always {0, 0} for Overlay). But we are trying to put labels at specific horizontal positions in the original plot coordinates.

To make the overlays align properly, especially in the horizontal direction where the placement has to be most accurate, I put the annotation into an invisible copy of the original plot. That guarantees that the overlays will fit together nicely:

g = With[{yMax = 5, yExtra = 8.5},
p = Plot[
{-.1 x, Tan[x]}, {x, 0, 3 Pi},
PlotRange -> {-yMax, yMax},
AxesLabel -> {x, y},
ImageSize -> 400
];
Overlay[{
Show[p,
PlotRegion -> {{0, 1}, {0, 2 yMax/(yMax + yExtra)}}],
Show[p,
AspectRatio -> (AspectRatio /.
Options[p, AspectRatio]) (1 + yExtra/yMax)/2,
BaseStyle -> Opacity,
PlotRange -> {-yMax, yExtra},
Epilog -> {Opacity,
Table[{Arrow[{{(2 i + 1)/2 Pi, 6.5}, {(2 i + 1)/2 Pi,
5.5}}],
Text[Row[{x, " = ", (2 i + 1)/(2 L) Pi}], {(2 i + 1)/
2 Pi, 7.4}]}, {i, 0, 3}]}
]
}
]
] The invisible layer is created in Show[p, BaseStyle -> Opacity... and the annotation is added to that layer as Epilog -> {Opacity,... (the specific annotation with text and arrows is created in the Table).

For the original plot p, I use Show[p, PlotRegion ->...] to make sure it is shown with the empty space on top. That PlotRegion specification can't be added to the initial definition of p because then it would disappear when the plot is displayed inside Overlay.

The horizontal coordinates of the original plot and the overlay are identical, but the vertical coordinates are not. This is due to the axis label y that isn't contained in the spacing calculations.

However, you can now use the Epilog in the overlay to add annotations that overlap the plot any way you like - partly inside and outside of the coordinate frame. E.g., you could try changing the Arrow specification above to Arrow[{{(2 i + 1)/2 π, 6}, {(2 i + 1)/2 π, 4}}] to make the arrows reach deeper into the plot: Edit: make it prettier

A separate issue is to make the arrows nicer:

With[{yMax = 5, yExtra = 7.5},
p = Plot[{-.1 x, Tan[x]}, {x, 0, 3 Pi}, PlotRange -> {-yMax, yMax},
AxesLabel -> {x, y}, ImageSize -> 400];
Overlay[{Show[p,
PlotRegion -> {{0, 1}, {0, 2 yMax/(yMax + yExtra)}}],
Show[p, AspectRatio -> (AspectRatio /.
Options[p, AspectRatio]) (1 + yExtra/yMax)/2,
BaseStyle -> Opacity, PlotRange -> {-yMax, yExtra},
Epilog -> {Opacity,
Table[{Arrow@
BezierCurve[{{(2 i + 1)/2 Pi, 6.5}, {(2 i + 1)/2 Pi + 1,
5}, {(2 i + 1)/2 Pi, 3.5}}, SplineDegree -> 2],
Text[Row[{x,
" = ", (2 i + 1)/(2 L) Pi}], {(2 i + 1)/2 Pi - .5,
6.5}]}, {i, 0, 3}]}]}]] The arrows have been modified to have a curved appearance by using BezierCurve. All you have to do is replace Arrow by Arrow@BezierCurve and add one extra point in-between the start and end point of the original (straight) arrow. That point is the handle that pulls the curve to one side.

• Very nice solution. I can think of lots of places to use it in my work. thanks – Jagra May 13 '12 at 21:52
• @Jagra Thanks - yes, I think this could also be useful when the positioning of PlotLabel is too inflexible. – Jens May 13 '12 at 21:59
• @Jens: Thank You. This is a very useful way to think about the problem. I learned some new functionality of mathematica. Thanks – night owl Jun 10 '12 at 23:55

I suggest 3 strategies.

First, break the data into parts and then using Epilog and Prologue to label the different things you need to label. While not what you need, the following snippet of code illustrates the idea:

    Prolog -> {Text["Expectation", {mean + 3.5, .01}],
Text["r", {r + 1, PDFr - .003}],
Text["MRL", {mrl + 2, PDFmrl - .003}]
} I have the above in a Manipulate that includes several overlaid plots, so the labels become dynamic. Moving as one moves the control.

By breaking up the data you could use a Prolog and Epilog on each data set. This a bit cumbersome, but it might give you the most flexibility, with respect to placement of the labels.

Then you can use Show to display everything together.

A variation of this is you could use Show to overlay labels the you create in a Graphics object. I think you'd find this more tedious because one can't as easily locate the labels relative to your plots.

A third strategy, one could again use Show with a second plot using selected points or slight offsets of those points and then specifying specific labels as PlotMarkers.

Others will have additional ideas. Good luck.