-
Notifications
You must be signed in to change notification settings - Fork 70
/
shadedErrorBar.m
275 lines (223 loc) · 7.41 KB
/
shadedErrorBar.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
function varargout=shadedErrorBar(x,y,errBar,varargin)
% generate continuous error bar area around a line plot
%
% function H=shadedErrorBar(x,y,errBar, ...)
%
% Purpose
% Makes a 2-d line plot with a pretty shaded error bar made
% using patch. Error bar color is chosen automatically.
%
%
% Inputs (required)
% x - vector of x values [optional, can be left empty]
% y - vector of y values or a matrix of n observations by m cases
% where m has length(x);
% errBar - if a vector we draw symmetric errorbars. If it has a size
% of [2,length(x)] then we draw asymmetric error bars with
% row 1 being the upper bar and row 2 being the lower bar
% (with respect to y -- see demo). ** alternatively **
% errBar can be a cellArray of two function handles. The
% first defines statistic the line should be and the second
% defines the error bar.
%
% Inputs (optional, param/value pairs)
% 'lineProps' - ['-k' by default] defines the properties of
% the data line. e.g.:
% 'or-', or {'-or','markerfacecolor',[1,0.2,0.2]}
% 'transparent' - [true by default] if true, the shaded error
% bar is made transparent. However, for a transparent
% vector image you will need to save as PDF, not EPS,
% and set the figure renderer to "painters". An EPS
% will only be transparent if you set the renderer
% to OpenGL, however this makes a raster image.
% 'patchSaturation'- [0.2 by default] The saturation of the patch color.
% 'plotAxes' - handle of axis to plot into. By default this is gca
%
%
%
% Outputs
% H - a structure of handles to the generated plot objects.
%
%
% Examples:
% y=randn(30,80);
% x=1:size(y,2);
%
%
% 1)
% shadedErrorBar(x,mean(y,1),std(y),'lineProps','g');
%
%
% 2)
% shadedErrorBar(x,y,{@median,@std},'lineProps',{'r-o','markerfacecolor','r'});
%
%
% 3)
% shadedErrorBar([],y,{@median,@(x) std(x)*1.96},'lineProps',{'r-o','markerfacecolor','k'});
%
%
% 4)
% Overlay two transparent lines:
% clf
% y=randn(30,80)*10;
% x=(1:size(y,2))-40;
% shadedErrorBar(x,y,{@mean,@std},'lineProps','-r','transparent',1);
% hold on
% y=ones(30,1)*x; y=y+0.06*y.^2+randn(size(y))*10;
% shadedErrorBar(x,y,{@mean,@std},'lineProps','-b','transparent',1);
% hold off
%
%
% 5) Target to a specific axis
% clf
% a = subplot(1,2,1);
% subplot(1,2,2);
% shadedErrorBar(x,mean(y,1),std(y),'lineProps','g','plotAxes',a);
%
%
% Rob Campbell - November 2009
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Parse input arguments
narginchk(3,inf)
params = inputParser;
params.CaseSensitive = false;
params.addParameter('lineProps', '-k', @(x) ischar(x) | iscell(x));
if (sum( size(ver('MATLAB'))) > 0 )
params.addParameter('transparent', true, @(x) islogical(x) || x==0 || x==1);
elseif (sum( size(ver('Octave'))) > 0 )
params.addParameter('transparent', false, @(x) islogical(x) || x==0 || x==1);
end
params.addParameter('patchSaturation', 0.2, @(x) isnumeric(x) && x>=0 && x<=1);
params.addParameter('plotAxes', gca);
params.parse(varargin{:});
%Extract values from the inputParser
lineProps = params.Results.lineProps;
transparent = params.Results.transparent;
patchSaturation = params.Results.patchSaturation;
plotAxes = params.Results.plotAxes;
if ~iscell(lineProps), lineProps={lineProps}; end
%Process y using function handles if needed to make the error bar dynamically
if iscell(errBar)
fun1=errBar{1};
fun2=errBar{2};
errBar=fun2(y);
y=fun1(y);
else
y=y(:).';
end
if isempty(x)
x=1:length(y);
elseif sum( size(ver('MATLAB'))) > 0
x=x(:).';
end
%Make upper and lower error bars if only one was specified
if length(errBar)==length(errBar(:))
errBar=repmat(errBar(:)',2,1);
else
s=size(errBar);
f=find(s==2);
if isempty(f), error('errBar has the wrong size'), end
if f==2, errBar=errBar'; end
end
% Check for correct x, errbar formats
x_size = size(x);
if (length(x) ~= length(errBar) && sum( size(ver('MATLAB'))) > 0 )
error('length(x) must equal length(errBar)')
elseif( ( length(x) ~= length(errBar) && checkOctave_datestr(x) == false ) ...
&& sum( size(ver('Octave'))) > 0 )
error('length(x) must equal length(errBar) or x must have valid datestr')
end
%Log the hold status so we don't change
initialHoldStatus = ishold(plotAxes);
if ~initialHoldStatus
cla(plotAxes,'reset') %Wipe the axes if we are not holding what was there before
hold(plotAxes,'on')
end
H = makePlot(x,y,errBar,lineProps,transparent,patchSaturation,plotAxes);
if ~initialHoldStatus
hold(plotAxes,'off')
end
if nargout==1
varargout{1}=H;
end
function H = makePlot(x,y,errBar,lineProps,transparent,patchSaturation,plotAxes)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Determine host application
if sum( size(ver('MATLAB')) ) > 0
hostName = 'MATLAB';
elseif sum( size(ver('Octave')) ) > 0
hostName = 'Octave';
end % if
% Plot to get the parameters of the line
if hostName == 'MATLAB'
H.mainLine=plot(plotAxes, x, y, lineProps{:});
elseif hostName == 'Octave'
boolxDatestr = checkOctave_datestr(x);
if boolxDatestr
x = datenum(x);
x = x(:).';
H.mainLine=plot(plotAxes, x, y, lineProps{:});
datetick(plotAxes);
else
H.mainLine=plot(plotAxes, x, y, lineProps{:});
end
end
% Tag the line so we can easily access it
H.mainLine.Tag = 'shadedErrorBar_mainLine';
% Work out the color of the shaded region and associated lines.
% Here we have the option of choosing alpha or a de-saturated
% solid colour for the patch surface.
mainLineColor=get(H.mainLine,'color');
edgeColor=mainLineColor+(1-mainLineColor)*0.55;
if transparent
faceAlpha = patchSaturation;
patchColor = mainLineColor;
else
faceAlpha = 1;
patchColor = mainLineColor + (1-mainLineColor) * (1-patchSaturation);
end
%Calculate the error bars
uE = y + errBar(1,:);
lE = y - errBar(2,:);
%Make the patch (the shaded error bar)
yP = [lE,fliplr(uE)];
xP = [x,fliplr(x)];
%remove nans otherwise patch won't work
xP(isnan(yP))=[];
yP(isnan(yP))=[];
if isdatetime(x) && strcmp(hostName,'MATLAB')
H.patch = patch(datenum(xP),yP,1,'parent',plotAxes);
else
H.patch = patch(xP,yP,1,'parent',plotAxes);
end
set(H.patch, ...
'facecolor', patchColor, ...
'edgecolor', 'none', ...
'facealpha', faceAlpha, ...
'HandleVisibility', 'off', ...
'Tag', 'shadedErrorBar_patch')
%Make pretty edges around the patch.
H.edge(1) = plot(plotAxes, x, lE, '-');
H.edge(2) = plot(plotAxes, x, uE, '-');
set([H.edge], 'color',edgeColor, ...
'HandleVisibility','off', ...
'Tag', 'shadedErrorBar_edge')
% Ensure the main line of the plot is above the other plot elements
if hostName == 'MATLAB'
if strcmp(get(plotAxes,'YAxisLocation'),'left') %Because re-ordering plot elements with yy plot is a disaster
uistack(H.mainLine,'top')
end
elseif hostName == 'Octave'
% create the struct from scratch by temp.
H = struct('mainLine', H.mainLine, ...
'patch', H.patch, ...
'edge', H.edge);
end
function boolDate = checkOctave_datestr(x)
%% Simple try/catch for casting datenums, requireing valid datestr
boolDate = true;
try
datenum(x)
catch
boolDate = false;
end