-
Notifications
You must be signed in to change notification settings - Fork 1
/
ploterr.m
406 lines (379 loc) · 14.4 KB
/
ploterr.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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
function hh = ploterr(x, y, xerr, yerr, varargin)
%PLOTERR General error bar plot.
%
%
% Usage for the impatient
% =======================
%
% X, Y: x and y axis values
% xerr, yerr: if matrix relative error
% if cell array lower and upper bound
% LineSpec as in plot. If passed it must be between yerr and properties.
% Properties: 'logx', 'logy', 'logxy':
% toggles for logarithmic scaling, no value needed
% 'hhx', 'hhy', 'hhxy':
% relative handle sizes (Handle Height)
% 'abshhx', 'abshhy', 'abshhxy':
% absolute handle sizes. Total height for linear scale,
% fraction of power of 10 for log scale.
%
% X, Y, xerr and yerr must be of the same size as long as dimensions are
% not equal to 1, otherwise singletons are expanded. You can pass every-
% thing you like, e.g. xerr={0, 20:29} and x=[(1:10)' (2:11)'] to
% indicate a lower bound of 0 for all values and an upper bound of 20:29
% for both columns of x, which will show up as two separate lines.
%
%
% USAGE
% =====
%
% PLOTERR(X,Y,EX,EY) plots X vs. Y with x error bars [X-XE X+XE] and y
% error bars [Y-YE Y+YE].
%
% PLOTERR(X,Y,{LX,UX},{LY,UY}) plots the graph of vector X vs. vector Y
% with error bars specified by LX and UX in horizontal direction and
% with error bars specified by LY and UY in vertical direction.
% L and U contain the lower and upper error ranges for each point
% in X resp. Y (L = lower, U = upper). Each error bar ranges from L(i)
% to U(i). X, LX and UX sizes may vary in singleton dimensions only, the
% same accounts for Y, LY and UY. If any of X,Y,LX,UX,LY,UY is a matrix
% then each column produces a separate line.
%
% PLOTERR(X,Y,[],EY) plots no x error bars
% PLOTERR(X,Y,EX,[]) plots no y error bars
% PLOTERR(X,Y,EX,{LY,UY}) plots x errors [X-XE X+XE] and y errors [LY UY]
% ... etc ...
%
% PLOTERR(X,Y,EX,EY,LineSpec) uses the color and linestyle specified by
% the string LineSpec. See PLOT for possibilities. Defaults to a solid
% line connecting the points (X,Y).
%
% PLOTERR(X,Y,EX,EY,LineSpec,'Property1',Value1,'Property2',Value2,...)
% Property Value pairs can be passed after LineSpec, however, LineSpec
% does not need to be passed.
% The Following properties are available:
% - 'logx', 'logy', 'logxy': Logarithmic scaling of x axis, y axis or
% both. A value is not required. If you still specify a value, 0 turns
% log scale off, 1 turns it on. E.g. PLOTERR(X,Y,EX,EY,'logx') plots on
% a logarithmically scaled x axis.
% - 'hhx', 'hhy', 'hhxy': relative size of bar handles compared to the
% average distance of the datapoints. The default for hhx and hhy is
% 2/3, indicating a total width of the bar handles of 2/3 of the mean
% distance of datapoints in y. For logarithmic plots that is the mean
% distance on a logarithmic scale.
% E.g. PLOTERR(X,Y,EX,EY,'logy','hhy',0.1) plots normal x error bars and
% tiny y errorbars on a logarithmic y scale and a linear x scale.
% - 'abshhx', 'abshhy', 'abshhxy': absolute size of bar handles for
% linear scale, absolute size in units power of 10 for logscale, i.e.
% the command PLOTERR(...,'logy','abshhx',1.0) produces x handlebars
% spanning each one decade on the y axis.
%
% H = PLOTERR(...) returns a vector of line handles in the order:
% H(1) = handle to datapoints
% H(2) = handle to errorbar y OR errorbar x if error y not specified
% H(3) = handle to errorbar x if error y specified
% If more than one line is plotted, the ordering is the following:
% H(1:n) = handle to lines with datapoints
% H(n+1:2*n) = handle to y error bars
% H(2*n+1:3*n) = handle to x error bars
%
% NOTES:
% ------
% - If both abshh* and hh* are set, the latter of both in the argument
% list is used. The same is true for any of the other properties
% passed twice.
% - Properties ending on 'xy' may also end on 'yx' or you can leave the
% ending away: logxy = logyx = log
% - You cannot pass PLOT properties like 'LineWidth'. To use those
% plot properties, call set(h(i),'Property',Value) with i according
% to the specification for H = PLOTERR(...) above.
%
%
% Examples
% ========
%
% Basic example:
% -------------
%
% x = 2:11;
% y = sin(x);
% e = std(y)*ones(size(x));
% ploterr(x,y,e)
%
% Draws symmetric error bars of unit standard deviation along a sine wave.
%
%
% Extended example:
% -----------------
%
% x = 0:15;
% y=exp(-x).*(rand(1,16)*0.9+0.1);
% h=ploterr(x,y,0.3,{exp(-x)*0.1 exp(-x)},'r.','logy','hhxy',0.5)
% set(h(2),'Color','b'), set(h(3),'Color','b')
% legend(h,{'data' 'error x' 'error y'})
%
% Draws samples of a noisy exponential function on a logarithmic y scale
% with constant relative errors in x and variable absolute errors in y with
% slim error bar handles. The lineseries objects are used to set the color
% of the error bars and to display a legend.
%
%
% Acknowledgements
% ================
%
% technique for plotting error bars
% ---------------------------------
% L. Shure 5-17-88, 10-1-91 B.A. Jones 4-5-93
% Copyright 1984-2002 The MathWorks, Inc.
% $Revision: 5.19 $ $Date: 2002/06/05 20:05:14 $
%
% modified for plotting horizontal error bars
% -------------------------------------------
% Goetz Huesken
% e-mail: goetz.huesken(at)gmx.de
% Date: 10/23/2006
%
%
% Version History
% ===============
%
% v1.0, October 2008: modification of errorbar_x by Goetz Huesken for
% plotting horizontal and/or vertical errorbars and to support
% logarithmic scaling.
% v1.1, December 2008: changed the user interface. Handle sizes and
% logarithmic scaling can now be set via properties. LineSpec is
% not compulsory anymore when setting logscale or handle sizes.
% v1.1.1, December 2008: bugfix of v1.1
% v1.1.2, February 2009: added abshh properties to set handle sizes as
% absolute values
%
% Felix Zörgiebel
% email: felix_z -> web.de
% Date: 12/03/2008
%% read inputs, set default values
if nargin<1, error('Not enough input arguments.'), end
if nargin<2, y=x; x=[]; end
if nargin<3, xerr=[]; end
if nargin<4, yerr=[]; end
[symbol,logx,logy,hhx,hhy]=getproperties(varargin);
%% analyse and prepare inputs (this section is a little ugly, you are invited to improve it!)
if ndims(x)~=2 || ndims(y)~=2
error('x and y must not have more than two dimensions!')
end
if isempty(x), x = (1:size(y,1))'; end
try [x,y]=expandarrays(x,y); catch error('x and y are not consistent in size.'), end
% check if xerror is relative or absolute
relxerr=false;
if ~iscell(xerr)
if ~isempty(xerr)
relxerr=true;
xerr={-xerr, +xerr};
else
xerr={[],[]};
end
elseif length(xerr)~=2
error('xerr must have two entries (low and upper bounds) if it is a cell array,')
end
% make xerr and x and y values same size
try [xl,xh]=expandarrays(xerr{1},xerr{2}); catch error('xl and xh are not consistent in size.'), end
try [y,xl,ty,txl]=expandarrays(y,xl); catch error('xl and y are not consistent in size.'), end
if ty, y=y'; xl=xl'; txl=~txl; end % make sure x and y still match
if txl, xh=xh'; end % make sure xl and xh still match
try [y,xh]=expandarrays(y,xh); catch error('xh and y are not consistent in size.'), end
if relxerr
try [xl,x,txl]=expandarrays(xl,x); catch error('xl and x are not consistent in size.'), end
if txl, xh=xh'; end
try [x,xh]=expandarrays(x,xh); catch error('xh and x are not consistent in size.'), end
xl=x+xl; xh=x+xh;
end
% check if yerror is relative or absolute
relyerr=false;
if ~iscell(yerr)
if ~isempty(yerr)
relyerr=true;
yerr={-yerr, +yerr};
else
yerr={[],[]};
end
elseif length(yerr)~=2
error('yerr must have two entries (low and upper bounds) if it is a cell array,')
end
% make yerr and x and y values same size
try [yl,yh]=expandarrays(yerr{1},yerr{2}); catch error('yl and yh are not consistent in size.'), end
try [x,yl,tx,tyl]=expandarrays(x,yl); catch error('yl and x are not consistent in size.'), end
if tx, x=x'; yl=yl'; tyl=~tyl; end % make sure x and y still match
if tyl, yh=yh'; end % make sure yl and yh still match
try [x,yh]=expandarrays(x,yh); catch error('yh and x are not consistent in size.'), end
if relyerr
try [y,yl]=expandarrays(y,yl); catch error('yl and y are not consistent in size.'), end
try [y,yh]=expandarrays(y,yh); catch error('yh and y are not consistent in size.'), end
yl=y+yl; yh=y+yh;
end
% choose the appropriate function for the plot
if logx && logy, plotfct=@loglog;
elseif logx && ~logy, plotfct=@semilogx;
elseif ~logx && logy, plotfct=@semilogy;
else plotfct=@plot;
end
% LineSpec setup
[ls,col,mark,msg] = colstyle(symbol); if ~isempty(msg), error(msg); end
symbol = [ls mark col]; % Use marker only on data part
esymbol = ['-' col]; % Make sure bars are solid
%% do the plotting
hold_state = ishold;
h=[];
% plot specified data
if ~isempty(xl) % x errorbars
[bary,barx]=barline(y,xl,xh,logy,hhx);
h = plotfct(barx,bary,esymbol); hold on
end
if ~isempty(yl) % y errorbars
[barx,bary]=barline(x,yl,yh,logx,hhy);
h = [plotfct(barx,bary,esymbol);h]; hold on
end
if ~isempty(y) % function values
h = [plotfct(x,y,symbol);h];
end
if ~hold_state, hold off; end
if nargout>0, hh = h; end
end
%% helper functions
function [perp,para] = barline(v,l,h,uselog,handleheight)
% v: value "perpendicular"
% l: lower bound "parallel"
% h: upper bound "parallel"
[npt,n]=size(l);
% calculate height of errorbar delimiters
% set basic operations for linear spacing
dist=@minus;
invdist=@plus;
scale=@times;
if uselog
% overwrite basic operations for logarithmic spacing
dist=@rdivide;
invdist=@times;
scale=@power;
end
if handleheight>0 % means handleheight was passed as a relative value
% set width of ends of bars to handleheight times mean distance of the bars.
% If number of points is under 15, space as if 15 points were there.
if dist(max(v(:)),min(v(:)))==0
dv = scale(abs(v),1/40) + (abs(v)==0);
else
dv = scale(dist(max(v(:)),min(v(:))),1/max(15,npt-1)*handleheight/2);
end
else % handleheight<=0 means handleheight was passed as an absolute value
dv=handleheight/2;
if uselog, dv=10^dv; end
end
vh = invdist(v,dv);
vl = dist(v,dv);
% build up nan-separated vector for bars
para = zeros(npt*9,n);
para(1:9:end,:) = h;
para(2:9:end,:) = l;
para(3:9:end,:) = NaN;
para(4:9:end,:) = h;
para(5:9:end,:) = h;
para(6:9:end,:) = NaN;
para(7:9:end,:) = l;
para(8:9:end,:) = l;
para(9:9:end,:) = NaN;
perp = zeros(npt*9,n);
perp(1:9:end,:) = v;
perp(2:9:end,:) = v;
perp(3:9:end,:) = NaN;
perp(4:9:end,:) = vh;
perp(5:9:end,:) = vl;
perp(6:9:end,:) = NaN;
perp(7:9:end,:) = vh;
perp(8:9:end,:) = vl;
perp(9:9:end,:) = NaN;
end
function [A,B,tA,tB] = expandarrays(A,B)
% A, B: Matrices to be expanded by repmat to have same size after being processed
% tA, tB: indicate wether A or B have been transposed
sizA=size(A); tA=false;
sizB=size(B); tB=false;
% do not process empty arrays
if isempty(A) || isempty(B), return, end
% make vectors column vectors
if sizA(1)==1, A=A(:); tA=~tA; sizA=sizA([2 1]); end
if sizB(1)==1, B=B(:); tB=~tB; sizB=sizB([2 1]); end
% transpose to fit column, if necessary
if sizA(2)==1 && sizB(2)~=1 && sizB(2)==sizA(1) && sizB(1)~=sizA(1), B=B'; tB=~tB; sizB=sizB([2 1]); end
if sizB(2)==1 && sizA(2)~=1 && sizA(2)==sizB(1) && sizB(1)~=sizB(1), A=A'; tA=~tA; sizA=sizA([2 1]); end
% if only singletons need to be expanded, do it
if all(sizA==sizB | sizA==1 | sizB==1)
singletonsA=find(sizA==1 & sizB~=1);
repA=ones(1,2);
repA(singletonsA)=sizB(singletonsA);
A=repmat(A,repA);
singletonsB=find(sizB==1 & sizA~=1);
repB=ones(1,2);
repB(singletonsB)=sizA(singletonsB);
B=repmat(B,repB);
else % otherwise return error
error('Arrays A and B must have equal size for all dimensions that are not singleton!')
end
end
function [sym,lx,ly,hx,hy] = getproperties(A)
lx=0; ly=0; hx=2/3; hy=2/3; sym='-'; % presets
if isempty(A), return, end
[k,k,k,errmsg]=colstyle(A{1});
if isempty(errmsg)
sym = A{1}; % get symbol from first entry if it is a style
A=A(2:end); % skip symbol for properties
end
n=length(A);
A=[A '!"§$%&()=?']; % append some stupid string for the case that the last property comes without a value
idx=1;
while idx <= n
prop=A{idx};
val=A{idx+1};
if all(prop(end-1:end)=='yx') || all(prop(end-1:end)=='xy'), prop=prop(1:end-2); end
switch prop
case 'logx'
if isnumeric(val), lx=val;
else lx=1; idx=idx-1;
end
case 'logy'
if isnumeric(val), ly=val;
else ly=1; idx=idx-1;
end
case 'log'
if isnumeric(val), ly=val; lx=val;
else ly=1; lx=1; idx=idx-1;
end
case 'hhx'
if isnumeric(val), hx=abs(val);
else error('Property hhx must be followed by a numerical value.');
end
case 'hhy'
if isnumeric(val), hy=abs(val);
else error('Property hhy must be followed by a numerical value.');
end
case 'hh'
if isnumeric(val), hy=abs(val); hx=abs(val);
else error('Property hh must be followed by a numerical value.');
end
case 'abshhx'
if isnumeric(val), hx=-abs(val);
else error('Property abshhx must be followed by a numerical value.');
end
case 'abshhy'
if isnumeric(val), hy=-abs(val);
else error('Property abshhy must be followed by a numerical value.');
end
case 'abshh'
if isnumeric(val), hy=-abs(val); hx=-abs(val);
else error('Property abshh must be followed by a numerical value.');
end
otherwise
if ischar(prop), error(['Unknown property: ' prop])
else error('Parsed a property that is not a string.')
end
end
idx=idx+2;
end
end