How to Make Better Plots in MATLAB: Text

This is the second of a short series of posts on plotting in MATLAB. This series is focusing on suggestions and considerations to get your plot looking “just right” for your paper or presentation. By the end, you will have a plotting function that will do most of the tweaks automatically. In this post, I’m talking about text in figures. These include axis labels, legends, and other annotations.

As a reminder, this is the final result that we’re heading towards:

A target plot. This figure is easy to read and the lines are easy to distinguish.
Our target plot. This figure is easy to read and the lines are easy to distinguish.

The figure above is clean, clear, and would be as easy to read in black-and-white as it is in color. In the first post, I talked about ways to improve how curves look. We started with the default call to the plot function and worked on a script to modify the properties. Here we’re going to pick up where we left off. We had this script:


% Plotting take 4
numPlotPoints = 50;
x = 1e-2:1e-2:100;
% Smooth curves
y1 = exp(-x);
y2 = 1-exp(-x);
% Noisy curves
n = length(x);
z1 = y1.*(1+0.005*randn(1,n));
z2 = y2.*(1+0.005*randn(1,n));
plotInd = unique(floor(logspace(0, log10(n), numPlotPoints)));
hFig = figure;
hAxes = axes('Parent',hFig,…
'FontSize',12,'XGrid','on', 'YGrid','on', 'Box', 'on');
hold('on');
plot(hAxes, x(plotInd),y1(plotInd),'LineStyle', '-', 'Color', [0 0 0],…
'LineWidth', 1);
plot(hAxes, x(plotInd),y2(plotInd),'LineStyle', '-', 'Color', [0 0 1],…
'LineWidth', 1);
plot(hAxes, x(plotInd),z1(plotInd),'LineStyle', 'none', 'Color', [0 0.6 0],…
'LineWidth', 1, 'Marker', 'o');
plot(hAxes, x(plotInd),z2(plotInd),'LineStyle', 'none', 'Color', [1 0 0],…
'LineWidth', 1, 'Marker', 'o');
xlabel('Time [s]');
ylabel('Exponential Observation');
set(hAxes,'XScale','log');

The script generates the following figure (or almost the same, depending on the state of the random number generator):

matlab_plot_5
Last time on Rambling Academic …

Now we’re going to keep polishing the figure by modifying our script.

LaTeX Interpreter

One very quick fix improves the display of the numbers and labels on each axis. The default “interpreter” used by Matlab for displaying text is “tex”. We want to change it to “latex”. We’ll do this in 2 places. First, we will set “TickLabelInterpreter” when we create the axes. Second, we will set the “interpreter” in the calls to set the labels. The script now looks like this:


numPlotPoints = 50;
x = 1e-2:1e-2:100;
% Smooth curves
y1 = exp(-x);
y2 = 1-exp(-x);
% Noisy curves
n = length(x);
z1 = y1.*(1+0.005*randn(1,n));
z2 = y2.*(1+0.005*randn(1,n));
plotInd = unique(floor(logspace(0, log10(n), numPlotPoints)));
hFig = figure;
hAxes = axes('Parent',hFig,…
'FontSize',12,'XGrid','on', 'YGrid','on', 'Box', 'on',…
'TickLabelInterpreter', 'latex');
hold('on');
plot(hAxes, x(plotInd),y1(plotInd),'LineStyle', '-', 'Color', [0 0 0],…
'LineWidth', 1);
plot(hAxes, x(plotInd),y2(plotInd),'LineStyle', '-', 'Color', [0 0 1],…
'LineWidth', 1);
plot(hAxes, x(plotInd),z1(plotInd),'LineStyle', 'none', 'Color', [0 0.6 0],…
'LineWidth', 1, 'Marker', 'o');
plot(hAxes, x(plotInd),z2(plotInd),'LineStyle', 'none', 'Color', [1 0 0],…
'LineWidth', 1, 'Marker', 'o');
xlabel('Time [s]', 'interpreter', 'Latex');
ylabel('Exponential Observation', 'interpreter', 'Latex');
set(hAxes,'XScale','log');

And we get this figure:

Using the LaTeX interpreter for the axis scales and labels
Using the LaTeX interpreter for the axis scales and labels.

Using the LaTeX interpreter does more than make the text look like it belongs in your paper. You can also use regular LaTeX math text surrounded by double-$, e.g., $alpha_0$. So if you have variables in your paper then you can have them appear nicely in your figures without needing to edit them outside of MATLAB.

Ghosts and Legends

How about we add a legend? We could just click the “legend” button in the figure toolbar …

Using the default legend text and format.
Using the default legend text and format.

This doesn’t look great. Now, the natural reaction would be to edit the 4 text labels (1 for each curve) and call it a day. Let’s do that first:

Modifying the default legend.
Modifying the default legend.

This legend still doesn’t read well. There’s a lot of repetition, and if you took a quick look it would be hard to read. The figure is really plotting 2 phenomena (Growth and Decay). Each one has an “Analytical” curve and a “Simulation” curve. It would be best to only have 2 entries in the legend. Here are two ways to do this.

Hide Extra Legend Entries You Don’t Want

You can manually exclude specific curves from a legend. Open the figure editor and use the “Plot Browser” to select the curves whose legends you want to hide. Then, run this code:


% This script removes the selected lines from a figure's legend. The lines
% must be selected BEFORE calling this script
%
% Created June 6, 2014, by Adam Noel
%
function removeLegendLines()
a = findobj('Selected', 'on');
for i = 1:length(a)
hasbehavior(a(i), 'legend', false);
end

Now, disable and re-enable the legend. The entries that you wanted to hide will be gone.

This method works well when you’re manually editing a figure. But, we need to keep the colors of the curves that we didn’t remove. It would be better in this case if the legend entries had “generic” colors (i.e., black).

Create Ghost Curves

A good automated method is to create “Ghost curves” in our figure that have no data but have the properties that we want to display in the legend. We add these curves first in our script, and only define those entries when we call the “legend” function. To create a curve with no data, use NaN (Not a Number) for at least one dimension (more generally, NaN can be used to leave out individual points when plotting a line).

Let’s modify our script to use ghost curves, and we’ll also set the background to white:


numPlotPoints = 50;
x = 1e-2:1e-2:100;
% Smooth curves
y1 = exp(-x);
y2 = 1-exp(-x);
% Noisy curves
n = length(x);
z1 = y1.*(1+0.005*randn(1,n));
z2 = y2.*(1+0.005*randn(1,n));
plotInd = unique(floor(logspace(0, log10(n), numPlotPoints)));
hFig = figure;
hAxes = axes('Parent',hFig,…
'FontSize',12,'XGrid','on', 'YGrid','on', 'Box', 'on',…
'TickLabelInterpreter', 'latex');
set(hFig, 'Color', 'w')
hold('on');
plot(hAxes, NaN,NaN,'LineStyle', '-', 'Color', [0 0 0],…
'LineWidth', 1);
plot(hAxes, NaN,NaN,'LineStyle', 'none', 'Color', [0 0 0],…
'LineWidth', 1, 'Marker', 'o');
plot(hAxes, x(plotInd),y1(plotInd),'LineStyle', '-', 'Color', [0 0 0],…
'LineWidth', 1);
plot(hAxes, x(plotInd),y2(plotInd),'LineStyle', '-', 'Color', [0 0 1],…
'LineWidth', 1);
plot(hAxes, x(plotInd),z1(plotInd),'LineStyle', 'none', 'Color', [0 0.6 0],…
'LineWidth', 1, 'Marker', 'o');
plot(hAxes, x(plotInd),z2(plotInd),'LineStyle', 'none', 'Color', [1 0 0],…
'LineWidth', 1, 'Marker', 'o');
xlabel('Time [s]', 'interpreter', 'Latex');
ylabel('Exponential Observation', 'interpreter', 'Latex');
legend(hAxes, 'Analytical', 'Simulation')
set(hAxes,'XScale','log');

So we get the following:

Figure with legend based on ghost curves.
Figure with legend based on ghost curves.

This legend clearly states that any solid line is an analytical curve and the markers show simulations, even if the colors don’t match any specific curves.

Annotations

We’re now very close to our target plot. We want to add some annotations to distinguish between the 2 phenomena on the figure. Unfortunately, annotations are harder to do automatically (particularly in terms of where to place them), so I usually just do them by hand. We will draw a textbox and make the following changes:

  • Set “Background Color” to white (default is transparent). This makes the text easier to read.
  • Set “Line Style” to “none” to remove the bounding box.
  • Set “Interpreter” to “latex” (of course). I will also do this for the legend.

The text can now be set to “Growth” and we can duplicate the box to make one that says “Decay”. By placing these boxes near their corresponding curves we get our desired result:

A target plot. This figure is easy to read and the lines are easy to distinguish.
Done.

More on Annotations

If your figure has many sets of curves, then you might find it helpful to be more specific with your annotations. For example, you can group a set of curves with an ellipse and use an arrow to point from the label. I recommend using separate text boxes and arrows instead of a “text arrow”. Text arrows are much harder to tweak because the text location is determined automatically.

Here is our plot with some manually-placed arrows and ellipses:

Figure with arrows and ellipses.
Figure with arrows and ellipses.

In this case, the arrows and ellipses don’t add much for understanding the figure. But I’ve used them in crowded figures and they can really help to see things clearly.

What’s Next

We’ve gotten the original desired figure, so that stage is complete. The next post in this series will look at automating these steps with a wrapper function that you can use when making (practically) any figure.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.