有些时候,为了更加清晰地表示数据,我们需要对绘好的图像添加一些其他标注信息,以帮助读者更好地理解自己的意思,本文将向大家介绍几种常用的添加额外信息的方法。注:下述实现均参考自MathWorks,在此表示感谢,如有侵权请留言联系。
目录
1. 添加线段
常见的两个需求:
- 观察某个特定点的数据的值;
- 观察数据的趋势,判断数据收敛的上界或下界。
我们可以通过添加一些必要的线段来帮助读者理解上述信息。
具体的实现代码如下所示:
% 数据
step_data =[
0 0.0113
10.0000 0.0162
20.0000 0.1947
30.0000 0.5591
40.0000 0.7050
50.0000 0.7744
60.0000 0.9218
70.0000 0.9208
80.0000 0.9852
90.0000 0.9575
100.0000 1.0546
110.0000 0.9873
120.0000 1.0258
130.0000 0.9930
140.0000 1.0203
150.0000 1.0605
160.0000 0.9637
170.0000 1.0051
180.0000 0.9878
190.0000 0.9876
200.0000 1.0349];
figure
plot(step_data(:,1), step_data(:,2), 'r*-')
% 设置坐标轴显示范围
axis([0 200 0 1.2])
% 添加标题和标签
title('Closed loop response')
xlabel('Time')
ylabel('Tank temperature (normalized)')
% 画一条横线
line('XData', [0 200], 'YData', [1 1], 'LineStyle', '-', ...
'LineWidth', 2, 'Color','m')
% 在x=25处添加一条横线和一条竖线
x = 25;
y = (step_data(4,2) - step_data(3,2));
line('XData', [x x 0], 'YData', [0 y y], 'LineWidth', 2, ...
'LineStyle', '-.', 'Color', [0 0.6 0])
% 在x=50处添加一条横线和一条竖线
x = 50;
y = step_data(6,2);
line('XData', [x x 0], 'YData', [0 y y], 'LineWidth', 2, ...
'LineStyle', '-.', 'Color', [0.2 0.4 1.0])
2. 添加Latex公式
另一个常见的需求:
我们绘制了一条曲线,为了读者一眼可以认出这条曲线表达的含义,我们需要把公式放在图中;
具体实现代码如下所示:
%%
% *This is an example of how to add LaTeX equations to plots in MATLAB®* .
%
% You can open this example in the <https://www.mathworks.com/products/matlab/live-editor.html
% Live Editor> with MATLAB version 2016a or higher.
%
% Read about the <http://www.mathworks.com/help/matlab/ref/text.html |text|> function in the MATLAB documentation.
% For more examples, go to <http://www.mathworks.com/discovery/gallery.html MATLAB Plot Gallery>
%
% Copyright 2012-2018 The MathWorks, Inc.
% Calculate the Fibonacci numbers from 1 to 12
fib = zeros(1, 12);
for i = 1:12
fib(i) = (((1+sqrt(5))/2)^i - ((1-sqrt(5))/2)^i)/sqrt(5);
end
% Plot the first 12 Fibonacci numbers
figure
plot(1:12, fib, 'k^-')
% Add a title and axis labels
title('Fibonacci Numbers from 1-12')
xlabel('n')
ylabel('F_n')
% Build a string that contains the Latex expression
eqtext = '$$F_n={1 \over \sqrt{5}}';
eqtext = [eqtext '\left[\left({1+\sqrt{5}\over 2}\right)^n -'];
eqtext = [eqtext '\left({1-\sqrt{5}\over 2}\right)^n\right]$$'];
% Add the string containing the Latex expression to the plot
text(0.5, 125, eqtext, 'Interpreter', 'Latex', 'FontSize', 12, 'Color', 'k')
3. 数据放大镜
有时候,我们需要动态地观察整个周期的某个细节的数据,这个需求有点类似淘宝里面的放大镜功能。
上图中,左图为放大镜中看到的细节,右图为原始图像,通过上下左右键可以控制右图窗口的移动,实现放大,具体实现如下:
classdef Orienter < handle
%Zoom and pan plotted data (referencePlot) using keypresses (arrow keys, shift, and command).
%Keypresses adjust the limits of the axes (Ax2Adjust) where the data are plotted.
%A separate axes, TrackerAx, shows the user which portion of the referencePlot is visible in Ax2Adjust.
%TrackerAx contains copy of referencePlot (miniPlot) such that all its X and Y data are visible.
%A semi-opaque yellow square (TrackerPatch) highlights the portion of data currently visible in Ax2Adjust.
%See MATLAB documentation on Patch to can change the color etc. of TrackerPatch
%Orienter automatically responds to events.
%If referencePlot data or Ax2Adjust limits changes, listeners automatically
%adjust miniPlot and TrackerAx accordingly. These allows you link axes with Ax2Adjust and to
%edit referencePlot data without having to alter Orienter.
%User can adjust the speed/precision (aka moveSpeed) of zooming and panning.
%More specifically, user can adjust how much the axes limits changes with
%each key press by clicking the patch object.
%Orienter should function in standalone applications/GUIs.
%TrackerPatch appearance can be altered by accessing Patch properties
%(e.g. ?FaceColor? for color, ?FaceAlpha? for transparency).
%% limitations
% Only changes in referencePlot XData and YData properties will cause miniPlot to update.
% If properties of referencePlot change BESIDES X-Data and Y-Data (e.g. color)
% these changes may not transfer automically transfer to miniPlot.
% you can always ensure miniplot matches referencePlot by using
% update_miniPlot method. When referencePlot X/Y Data do change, ALL
% properties of referencePlot are transferred to miniPlot.
%Ellen Zakreski (2016), efzakreski@mac.com
%% KEY PRESS
% || Key || Description
% ||(+modifier)||
%----------------------------------------------------------------
%PANING-------------------------------------------------------------
% || > || pan right (moves both left and right sides of patch)
% || < || pan left
% || ^ || pan up (moves both top and bottom sides of patch)
% || v || pan down
%ZOOMING (horizontally)---------------------------------------------
% || > shift || zoom in horizontally (moves only left side of patch right)
% || < shift || zoom out horizontally (moves only left side left)
% || > command || zoom out horizontally (moves right side right)
% || < command || zoom in horizontally (moves right side left)
%ZOOMING (vertically)-----------------------------------------------
% || v shift || zoom in vertically (moves only top side of patch down)
% || ^ shift || zoom out vertically (moves top side of patch up)
% || v command || zoom out vertically (move bottom side of patch down)
% || ^ command || zoom in vertically (moves bottom side up)
properties
referencePlot %plot handle or any graphics object with XData and YData
Ax2Adjust %will become the axes whose limits will be adjusted (by default, is the first axes that is ancestor to the reference plot).
keypressCatcher %figure that when clicked will detect keypresses that will move the patch (by default, is the parent of the TrackerAx).
listeners = struct('plotXData',[],'plotYData',[],'axXLim',[],'axYLim',[]); %see addAllListeners method below
XmoveSpeed = 2; %percentage by which X-axis limits change with each keypress.
YmoveSpeed = 10; %percentage by which Y-axis limits change with each keypress.
%% TRACKER
miniPlot %copy of referencePlot (plotted in TrackerAx);
%properties below are initially structs but become objects
%TrackerFig = struct('MenuBar','none','ToolBar','none','Position',[100 100 600 200]);
TrackerFig = struct('Position',[100 100 600 200]);
TrackerAx = struct('NextPlot','add',...%TrackerAx (by default plotted on TrackerFig) shows the entire reference plot
'XLimMode','manual','XLim',[1 2],...
'YLimMode','manual','YLim',[1 2]);
TrackerPatch
end %end properties
properties (Dependent)
totalXLim % X limits of reference plot
totalYLim % Y limits of reference plot
end
methods
function obj = Orienter(referencePlot)
%referencePlot can be plot or any graphics object with XData and YData
if nargin ~=0
obj.referencePlot = referencePlot;
%get axes to adjust
p = referencePlot.Parent;
while ~strcmpi(p.Type,'axes')
p = p.Parent;
end
obj.Ax2Adjust = p; clear p
% set to default Ax2Adjust axes limits
defaultview(obj);
% Add key press function to tracker fig(so that key presses trigger patch movement when trackerfig is in focus.
obj.TrackerFig = figure(obj.TrackerFig);
% TrackerAx shows the entire reference plot
obj.TrackerAx = axes(obj.TrackerAx,'parent',obj.TrackerFig);
obj.TrackerPatch = patch('parent',obj.TrackerAx,...
'XData',[1 1 2 2 1],...%TrackerPatch is square showing the portion of the total data in view.
'YData',[1 2 2 1 1],...
'FaceColor',[1 1 0],...%yellow
'FaceAlpha',0.5,... %opacity = 50%.
'EdgeColor','none',...
'buttondownfcn',@(src,ev)adjust_moveSpeed(obj));
% make miniPlot if reference plot changes
obj.miniPlot = copyobj(obj.referencePlot,obj.TrackerAx);
addAllListeners(obj)
%make miniPlot reflect referencePlot
update_plotData(obj,'X'); update_plotData(obj,'Y');
%make patch reflect portion of data currently in view
update_axLim(obj,'X'); update_axLim(obj,'Y');
end
end
%default view
function defaultview(obj)
%by default show 20% of the total x data,
obj.Ax2Adjust.XLim = [obj.totalXLim(1), obj.totalXLim(1) + diff(obj.totalXLim)*0.20];
% and 80% of the total y data
obj.Ax2Adjust.YLim = [obj.totalYLim(1), obj.totalYLim(1) + diff(obj.totalYLim)*0.80];
end
%% listeners
function addAllListeners(obj)
structfun(@delete,obj.listeners);
% listen to changes in reference plot X/YData
obj.listeners.plotXData = addlistener(obj.referencePlot,'XData','PostSet',@(src,ev)update_plotData(obj,'X'));
obj.listeners.plotYData = addlistener(obj.referencePlot,'YData','PostSet',@(src,ev)update_plotData(obj,'Y'));
%listen to changes in Ax2Adjust
obj.listeners.axXLim = addlistener(obj.Ax2Adjust,'XLim','PostSet',@(src,ev)update_axLim(obj,'X'));
obj.listeners.axYLim = addlistener(obj.Ax2Adjust,'YLim','PostSet',@(src,ev)update_axLim(obj,'Y'));
end
% listern callbacks
function update_plotData(obj,xory)
%if referencePlot X/YData changes
xory=upper(xory);
%% update minplot only X/YData
%set(obj.miniPlot,[xory,'Data'],obj.referencePlot.([xory,'Data']));
%% just copy miniplot again.
update_miniPlot(obj);
%change the axes limits of TrackerAx
newlim =obj.(['total',xory,'Lim']);
if isempty(newlim) || any(isnan(newlim))
warning('Could not reset tracker axes limits because plot %s data are all empty or NaN''s.', xory)
else
set(obj.TrackerAx,[xory,'Lim'],newlim);
end
end
function update_axLim(obj,xory)
%if Ax2Adjust axes limits change update patch X/YData
%accordingly so it overlaps with the portion of referencePlot that is currently
%visible in Ax2Adjust.
xory=upper(xory);
% get indexes for converting axes limits to patch X/YData/
switch xory
case 'Y'
ind = [1 2 2 1 1]; %B T T B B (B=bottom, T=top)
case 'X'
ind = [1 1 2 2 1]; %L L R R L (L=left, R=right)
end
L = obj.Ax2Adjust.([xory,'Lim']);
%update patch position.
obj.TrackerPatch.([xory,'Data']) = L(ind);
end
%% Get
function l=get.totalXLim(obj)
l=feval(@(d)[nanmin(d),nanmax(d)],obj.referencePlot.XData);
end
function l=get.totalYLim(obj)
l=feval(@(d)[nanmin(d),nanmax(d)],obj.referencePlot.YData);
end
%% Set
function set.referencePlot(obj,newval)
%validate
if ~ishandle(newval) || ~isvalid(newval)
error('referencePlot must be valid handle')
end
assert(all(ismember({'XData','YData'},fieldnames(newval))),'referencePlot must have properties called XData and YData');
obj.referencePlot=newval;
end
function set.Ax2Adjust(obj,newval)
if isstruct(newval)
newval = axes(newval);
end
if ~isa(newval,'matlab.graphics.axis.Axes') || ~isvalid(newval)
error('Ax2Adjust must be valid axes handle');
end
set(newval,'NextPlot','add','XLimMode','manual','YLimMode','manual');
obj.Ax2Adjust = newval;
end
function set.TrackerFig(obj,newval)
if isstruct(newval)
newval = figure(newval);
end
if ~isa(newval,'matlab.ui.Figure') || ~isvalid(newval)
error('TrackerFig must be valid figure handle');
end
% Add key press function to tracker fig(so that key presses trigger patch movement when trackerfig is in focus.
newval.KeyPressFcn=@(src,ev)keypress(obj,ev);
obj.TrackerFig = newval;
end
function set.TrackerAx(obj,newval)
if isstruct(newval)
newval = axes(newval);
end
if ~isa(newval,'matlab.graphics.axis.Axes') || ~isvalid(newval)
error('TrackerAx must be valid axes handle');
end
set(newval,'NextPlot','add',...
'XLimMode','manual','XLim',[1 2],'YLimMode','manual','YLim',[1 2]);
obj.TrackerAx = newval;
end
function update_miniPlot(obj)
delete(obj.miniPlot);
obj.miniPlot = copyobj(obj.referencePlot,obj.TrackerAx);
uistack(obj.TrackerPatch,'top'); %put TrackerPatch on top of all other plots in TrackerAx.
end
%% moveSpeed
function adjust_moveSpeed(obj)
xyls = {'X','Y'};
prompt = cellfun(@(xy)sprintf('%% of %s-axis that changes per key press:',xy),xyls,'uniformoutput',0);
defval = cellfun(@(xy)num2str(obj.([xy,'moveSpeed'])),xyls,'uniformoutput',0);
answer = inputdlg(prompt,...
'Adjust pan/zoom speed/precision.',...%title
1,...%numlines
defval);
if isempty(answer); return; end
for n=1:numel(xyls)
val = str2double(answer{n});
xy = xyls{n};
if isnan(val)
uiwait(errordlg('%input for must be numeric. %s-axis was not changed.',xy));
continue
end
obj.([xy,'moveSpeed']) = val;
end
end
function keypress(obj,ev)
TL = ismember('shift',ev.Modifier); %shift moves top/left side only
BR = ismember('command',ev.Modifier); %command moves bottom/right side only
%This is because on the left side of the keyboard,
%shift is above and to the left of the command key.
ispan = ~xor(TL,BR); %ispan if either TL or BR, or not TL nor BR.
%panning moves both sides move together, if zoom, only one side moves.
if ispan; TL = true; BR = true; end;
coef = [0,0]; % determines how much and in what direction Ax2Adjust axes limits change by.
switch ev.Key
case 'uparrow'
xory = 'Y';
coef([BR,TL]) = 1; %since for Y-axis limits [ymin,ymax], ymin is Bottom, ymax is Top [B,T]
case 'downarrow'
xory = 'Y';
coef([BR,TL]) = -1; %since for Y-axis limits [ymin,ymax], ymin is Bottom, ymax is Top [B,T]
case 'leftarrow'
xory ='X';
coef([TL,BR]) = -1; %since for X-axis limits [xmin,xmax], xmin is Left, xmax is Right [L,R]
case 'rightarrow'
xory ='X';
coef([TL,BR]) = 1; %since for X-axis limits [xmin,xmax], xmin is Left, xmax is Right [L,R]
otherwise
return
end
movepatch(obj,xory,coef);
end
function movepatch(obj,xory,coef)
xory = upper(xory);
mspeed = obj.([xory,'moveSpeed']);
span = diff(obj.(sprintf('total%sLim',xory)));
moveby = span*coef*mspeed/100;
%coeff elements indicate which sides of axis limits (min, max or both) and whether to increase or decrease
%is -1 (decrease along axis) or 1 (increase along axis)
%set new axes position
obj.Ax2Adjust.([xory,'Lim']) = obj.Ax2Adjust.([xory,'Lim']) + moveby; %will trigger listener to update patch position
end
end
end
function OrienterObj = example
%example of Orienter.
XX= unique(randi(300,[100,1]),'sorted');
YY = rand(size(XX));
f=figure; a=axes('parent',f); refPlot = plot(XX,YY,'-r*','parent',a);
title(a,'Ax2Adjust: Pan using arrowkeys. Use shift/command to zoom in and out.');
xlabel(a,'Visible X-Data');
ylabel(a,'Visible Y-Data');
legend({'referencePlot'});
OrienterObj = Orienter(refPlot);
title(OrienterObj.TrackerAx,'TrackerAx: Click patch(yellow square) to adjust pan/zoom speed');
xlabel(OrienterObj.TrackerAx,'Total X-Data');
ylabel(OrienterObj.TrackerAx,'Total Y-Data');
end
4.添加图标志
有些时候,我们在生成图像时,由于图像的数据个数不一致,或者数据过多时,我们不能将每个图像中的数据点都显示为特定的标志,此时,就需要一种工具,能够生成图标在我们已经绘出的图像中。
实现代码如下:
function ho=addmarkers(hi,n)
% ADDMARKERS Add a pre-defined number of markers to a plot instead of
% one at each datapoint which is default in Matlab.
%
% Usage:
% ADDMARKERS(HI) adds 5 markers to the input handle HI, J=1..length(HI)
% ADDMARKERS(HI,N) adds N markers to the input handle HI, J=1..length(HI)
% HO=ADDMARKERS(...) returns the output handle HO for future usage such as
% changing colors, markers, etc.
%
% Hint: use the output handle when adding legends, e.g.
% LEGEND(HO,'legend 1',...,'legend N')
%
% The markers will be added in the following order:
%
% o Circle
% s Square
% d Diamond
% x Cross
% + Plus sign
% * Asterisk
% . Point
% ^ Upward-pointing triangle
% v Downward-pointing triangle
% > Right-pointing triangle
% < Left-pointing triangle
% p Five-pointed star (pentagram)
% h Six-pointed start (hexagram)
%
% By Matthijs Klomp at Saab Automobile AB, 2009-10-11
% E-mail: matthijs.klomp@gm.com
%
if nargin<2, n=5; end % default number of markers
if nargin<1, error('Supply an input handle as input argument.'), end
figure(get(get(hi(1),'parent'),'parent'))% plot in the figure of the handle
subplot(get(hi(1),'parent')) % plot in the subplot of the handle
hold on % do not overwrite the current plot
markers = {'o','s','d','^','v','x','+','*','.','>','<','p','h'};
ho = hi; % initialize output handle
for i = 1:length(hi)
x = get(hi(i),'xdata'); % get the independent variable
y = get(hi(i),'ydata'); % get the dependent variable data
s = linspace(1,n,length(x)); % sampling independent variable
sn = [1 (2:n-1)+randn(1,n-2)/n n]; % add some noise to avoid overlap
xrs = interp1(s,x,sn,'nearest'); % downsample to n datapoints
yrs = interp1(s,y,sn,'nearest'); % downsample to n datapoints
plot(xrs,yrs,... % Plot the markers
'Marker',markers{i},'LineStyle','None','Color',get(hi(i),'Color'));
ho(i) = plot([0 1],[1 1]*NaN,... % Create the output handle
'Marker',markers{i},...
'LineStyle',get(hi(i),'Linestyle'),...
'Color',get(hi(i),'Color'));
end
if nargout==0, clear ho, end
x=(linspace(1,10,100));
y=x+1;
plot(x,y)
h=plot(x,y);
h0=addmarkers(h,4);%返回h0可以改变格式
legend(h0,'legend 1');
实现效果如下: