Is there a way to have a mark on a plot axis for any data points off the current plot in Matlab? It is great if the method works with zoom and pan, but I can live without it.
I have several data sets that I am plotting using subplots. Each plot contains 20 data points, similar to marking where darts landed on a dart board. For most data sets, these plots are within a standard range, ie the size of the dart board, so I am using this standard for the axis range of each plot to make the subplots easily visually comparable. However, a few data sets have an outlier that is outside this standard range (typically way outside). I do not want to change the axis to show the outlier, but I want the user to be aware that there is 1 or more data points off the plot, and preferably in which direction they are off.
I thought a bit about trying to use the axis markers (set(gca, 'Ytick', xLowerLimit: markSpacing: xUpperLimit)) and adding an extra marker in a different color to indicate the location of an outlier, but I couldn't see how to do this without disrupting the regular markers and in a automated way that could allow for multiple outlier marks.
One approach is to see if there is any data that exceed your Y Axis limit and then place some text somewhere to notify the user. Options include text (puts text on axis) or uicontrol (puts text somewhere in figure window).
Here is an example using uicontrol:
% Set up figure window and axes
h.f = figure;
h.ax = axes('Units', 'Normalized', 'Position', [0.13 0.2 0.75 0.75]);
% Plot some sample data and window our axes to replicate the question
x = 1:10;
y = [1:9 20];
xmin = 0; xmax = 10;
ymin = 0; ymax = 10;
plot(h.ax, x, y);
axis(h.ax, [xmin xmax ymin ymax]);
% Generate logical matrix to find if any y data is beyond our axis limit
ymask = y > ymax;
if any(ymask) % Loop triggers if any y value is greater than ymax
str = sprintf('There are %u data points greater than current y axis limit', sum(ymask));
uicontrol('Parent', h.f, ...
'Style', 'text', ...
'Units', 'Normalized', ...
'Position', [0.01 0.01 0.9 0.05], ...
'String', str ...
);
end
Which generates the following (annotations added manually):

This can be extended to other directions with some simple copy + paste and tweaks.
Edit: see bottom for improved method using callbacks.
You can find data points exceeding the axes limits and draw them on the limits, using different symbols:
A = randn(20, 2);
x_range = [-1.5, 1.5];
y_range = [-1.5, 1.5];
figure(1);
clf;
ah = axes;
plot(ah, A(:,1), A(:,2), 'o')
hold on
set(ah, 'XLim', x_range, 'YLim', y_range)
x_lt = A(:,1) < x_range(1);
x_gt = A(:,1) > x_range(2);
y_lt = A(:,2) < y_range(1);
y_gt = A(:,2) > y_range(2);
A_out = A;
A_out(x_lt, 1) = x_range(1);
A_out(x_gt, 1) = x_range(2);
A_out(y_lt, 2) = y_range(1);
A_out(y_gt, 2) = y_range(2);
A_out = A_out(x_lt | x_gt | y_lt | y_gt, :);
plot(ah, A_out(:,1), A_out(:,2), 'rx')
This produces a plot like this:

Edit: add callbacks
If you really want to go fancy, you can add callbacks so that outliers are automatically drawn (and removed) when the figure is zoomed. Adding the same functionality for panning is left as an exercise to the reader ;)
mark_outliers.m:
function mark_outliers(fig, ax)
ah = ax.Axes;
lobj = findobj(ah, 'Type', 'Line');
x_range = ah.XLim;
y_range = ah.YLim;
ah.NextPlot = 'add';
for i_l = 1:numel(lobj)
xd = lobj(i_l).XData;
yd = lobj(i_l).YData;
x_lt = xd < x_range(1);
x_gt = xd > x_range(2);
y_lt = yd < y_range(1);
y_gt = yd > y_range(2);
outliers = x_lt | x_gt | y_lt | y_gt;
if any(outliers)
xd_out = xd;
xd_out(x_lt) = x_range(1);
xd_out(x_gt) = x_range(2);
yd_out = yd;
yd_out(y_lt) = y_range(1);
yd_out(y_gt) = y_range(2);
xd_out = xd_out(outliers);
yd_out = yd_out(outliers);
plot(ah, xd_out, yd_out, 'xr', 'Tag', 'Outliers')
end
end
delete_outliers.m:
function delete_outliers(fig, ah)
ah = ah.Axes;
delete(findobj(ah, 'Tag', 'Outliers'));
example.m:
A = randn(20, 2);
fh = figure(1);
clf;
ah = axes;
plot(ah, A(:,1), A(:,2), 'o')
h = zoom(fh);
h.ActionPreCallback=@delete_outliers;
h.ActionPostCallback=@mark_outliers;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With