Blame view
dsplot.m
9.25 KB
b197c3fdf first commit |
1 2 3 4 5 6 7 8 |
function hL = dsplot(x, y, numPoints) %DSPLOT Create down sampled plot. % This function creates a down sampled plot to improve the speed of % exploration (zoom, pan). % % DSPLOT(X, Y) plots Y versus X by downsampling if there are large number % of elements. X and Y needs to obey the following: |
1a0e88f0c replace 4-spaces ... |
9 10 11 |
% 1. X must be a monotonically increasing vector. % 2. If Y is a vector, it must be the same size as X. % 3. If Y is a matrix, one of the dimensions must line up with X. |
b197c3fdf first commit |
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 |
% % DSPLOT(Y) plots the columns of Y versus their index. % % hLine = DSPLOT(X, Y) returns the handles of the line. Note that the % lines may be downsampled, so they may not represent the full data set. % % DSPLOT(X, Y, NUMPOINTS) or DSPLOT(Y, [], NUMPOINTS) specifies the % number of points (roughly) to display on the screen. The default is % 50000 points (~390 kB doubles). NUMPOINTS can be a number greater than % 500. % % It is very likely that more points will be displayed than specified by % NUMPOINTS, because it will try to plot any outlier points in the range. % If the signal is stochastic or has a lot of sharp changes, there will % be more points on plotted on the screen. % % The figure title (name) will indicate whether the plot shown is % downsampled or is the true representation. % % The figure can be saved as a .fig file, which will include the actual % data. The figure can be reloaded and the actual data can be exported to % the base workspace via a menu. % % Run the following examples and zoom/pan to see the performance. % % Example 1: (with small details) % x = linspace(0, 2*pi, 1000000); % y1 = sin(x)+.02*cos(200*x)+0.001*sin(2000*x)+0.0001*cos(20000*x); % dsplot(x,y1);title('Down Sampled'); % % compare with % figure;plot(x,y1);title('Normal Plot'); % % Example 2: (with outlier points) % x = linspace(0, 2*pi, 1000000); % y1 = sin(x) + .01*cos(200*x) + 0.001*sin(2000*x); % y2 = sin(x) + 0.3*cos(3*x) + 0.001*randn(size(x)); % y1([300000, 700000, 700001, 900000]) = [0, 1, -2, 0.5]; % y2(300000:500000) = y2(300000:500000) + 1; % y2(500001:600000) = y2(500001:600000) - 1; % y2(800000) = 0; % dsplot(x, [y1;y2]);title('Down Sampled'); % % compare with % figure;plot(x, [y1;y2]);title('Normal Plot'); % % See also PLOT. % Version: % v1.0 - first version (Aug 1, 2007) % v1.1 - added CreateFcn for the figure so that when the figure is saved |
1a0e88f0c replace 4-spaces ... |
61 62 63 |
% and re-loaded, the zooming and panning works. Also added a menu % item for saving out the original data back to the base % workspace. (Aug 10, 2007) |
b197c3fdf first commit |
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 |
% % Jiro Doke % August 1, 2007 debugMode = false; %-------------------------------------------------------------------------- % Error checking error(nargchk(1, 3, nargin, 'struct')); if nargin < 3 % Number of points to show on the screen. It's quite possible that more % points will be displayed if there are outlier points numPoints = 50000; % ~390 kB for doubles end if nargin == 1 || isempty(y) noXVar = true; y = x; x = []; else noXVar = false; end myErrorCheck; %-------------------------------------------------------------------------- if size(x, 2) > 1 % it's a row vector -> transpose x = x'; y = y'; varTranspose = true; else varTranspose = false; end % Number of lines numSignals = size(y, 2); % If the number of lines is greater than the number of data points per % line, it's possible that the user may have mistaken the matrix % orientation. if numSignals > size(y, 1) s = input(sprintf('Are you sure you want to plot %d lines? (y/n) ', ... |
1a0e88f0c replace 4-spaces ... |
104 |
numSignals), 's'); |
b197c3fdf first commit |
105 |
if ~strcmpi(s, 'y') |
1a0e88f0c replace 4-spaces ... |
106 107 108 109 110 |
disp('Canceled. You may want to transpose the matrix.'); if nargout == 1 hL = []; end return; |
b197c3fdf first commit |
111 112 113 114 115 116 117 118 119 120 121 122 |
end end % Attempt to find outliers. Use a running average technique filterWidth = ceil(min([50, length(x)/10])); % max window size of 50 a = y - filter(ones(filterWidth,1)/filterWidth, 1, y); [iOutliers, jOutliers] = find(abs(a - repmat(mean(a), size(a, 1), 1)) > ... repmat(4 * std(a), size(a, 1), 1)); clear a; % Always create new figure because it messes around with zoom, pan, % datacursors. |
1a0e88f0c replace 4-spaces ... |
123 |
hFig = figure; |
b197c3fdf first commit |
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 |
figName = ''; % Create template plot using NaNs hLine = plot(NaN(2, numSignals), NaN(2, numSignals)); set(hLine, 'tag', 'dsplot_lines'); % Define CreateFcn for the figure set(hFig, 'CreateFcn', @mycreatefcn); mycreatefcn(); % Create menu for exporting data hMenu = uimenu(hFig, 'Label', 'Data'); uimenu(hMenu, ... 'Label' , 'Export data to workspace.', ... 'Callback', @myExportFcn); % Update lines updateLines([min(x), max(x)]); % Deal with output argument if nargout == 1 hL = hLine; end %-------------------------------------------------------------------------- function myExportFcn(varargin) |
1a0e88f0c replace 4-spaces ... |
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 |
% This callback allows for extracting the actual data from the figure. % This means that if you save this figure and load it back later, you % can get back the data. % Determine the variable name allVarNames = evalin('base', 'who'); newVarName = genvarname('dsplotData', allVarNames); % X if ~noXVar if varTranspose dat.x = x'; else dat.x = x; end end % Y if varTranspose dat.y = y'; else dat.y = y; end assignin('base', newVarName, dat); msgbox(sprintf('Data saved to the base workspace as ''%s''.', ... newVarName), 'Saved', 'modal'); |
b197c3fdf first commit |
179 180 181 182 |
end %-------------------------------------------------------------------------- function mycreatefcn(varargin) |
1a0e88f0c replace 4-spaces ... |
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
% This callback defines the custom zoom/pan functions. It is defined as % the CreateFcn of the figure, so it allows for saving and reloading of % the figure. if nargin > 0 hFig = varargin{1}; end hLine = findobj(hFig, 'type', 'axes'); hLine(strmatch('legend', get(hLine, 'tag'))) = []; hLine = get(hLine, 'Children'); % Create Zoom, Pan, Datacursor objects hZoom = zoom(hFig); hPan = pan(hFig); hDc = datacursormode(hFig); set(hZoom, 'ActionPostCallback', @mypostcallback); set(hPan , 'ActionPostCallback', @mypostcallback); set(hDc , 'UpdateFcn' , @myDCupdatefcn); |
b197c3fdf first commit |
201 202 203 204 205 |
end %-------------------------------------------------------------------------- function mypostcallback(obj, evd) %#ok |
1a0e88f0c replace 4-spaces ... |
206 207 |
% This callback that gets called when the mouse is released after % zooming or panning. |
b197c3fdf first commit |
208 |
|
1a0e88f0c replace 4-spaces ... |
209 210 211 212 |
% single or double-click switch get(hFig, 'SelectionType') case {'normal', 'alt'} updateLines(xlim(evd.Axes)); |
b197c3fdf first commit |
213 |
|
1a0e88f0c replace 4-spaces ... |
214 215 |
case 'open' updateLines([min(x), max(x)]); |
b197c3fdf first commit |
216 |
|
1a0e88f0c replace 4-spaces ... |
217 |
end |
b197c3fdf first commit |
218 219 220 221 222 |
end %-------------------------------------------------------------------------- function updateLines(rng) |
1a0e88f0c replace 4-spaces ... |
223 224 |
% This helper function is for determining the points to plot on the % screen based on which portion is visible in the current limits. |
b197c3fdf first commit |
225 |
|
1a0e88f0c replace 4-spaces ... |
226 227 |
% find indeces inside the range id = find(x >= rng(1) & x <= rng(2)); |
b197c3fdf first commit |
228 |
|
1a0e88f0c replace 4-spaces ... |
229 230 |
% if there are more points than we want if length(id) > numPoints / numSignals |
b197c3fdf first commit |
231 |
|
1a0e88f0c replace 4-spaces ... |
232 233 |
% see how many outlier points are in this range blah = iOutliers > id(1) & iOutliers < id(end); |
b197c3fdf first commit |
234 |
|
1a0e88f0c replace 4-spaces ... |
235 236 |
% determine indeces of points to plot. idid = round(linspace(id(1), id(end), round(numPoints/numSignals)))'; |
b197c3fdf first commit |
237 |
|
1a0e88f0c replace 4-spaces ... |
238 239 240 241 242 243 244 245 |
x2 = cell(numSignals, 1); y2 = x2; for iSignals = 1:numSignals % add outlier points ididid = unique([idid; iOutliers(blah & jOutliers == iSignals)]); x2{iSignals} = x(ididid); y2{iSignals} = y(ididid, iSignals); end |
b197c3fdf first commit |
246 |
|
1a0e88f0c replace 4-spaces ... |
247 248 249 250 251 |
if debugMode figName = ['downsampled - ', sprintf('%d, ', cellfun('length', y2))]; else figName = 'downsampled'; end |
b197c3fdf first commit |
252 |
|
1a0e88f0c replace 4-spaces ... |
253 254 |
else % no need to down sample figName = 'true'; |
b197c3fdf first commit |
255 |
|
1a0e88f0c replace 4-spaces ... |
256 257 |
x2 = repmat({x(id)}, numSignals, 1); y2 = mat2cell(y(id, :), length(id), ones(1, numSignals))'; |
b197c3fdf first commit |
258 |
|
1a0e88f0c replace 4-spaces ... |
259 |
end |
b197c3fdf first commit |
260 |
|
1a0e88f0c replace 4-spaces ... |
261 262 263 |
% Update plot set(hLine, {'xdata', 'ydata'} , [x2, y2]); set(hFig, 'Name', figName); |
b197c3fdf first commit |
264 265 266 267 268 |
end %-------------------------------------------------------------------------- function txt = myDCupdatefcn(empt, event_obj) %#ok |
1a0e88f0c replace 4-spaces ... |
269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
% This function displays appropriate data cursor message based on the % display type pos = get(event_obj,'Position'); switch figName case 'true' txt = {['X: ',num2str(pos(1))],... ['Y: ',num2str(pos(2))]}; otherwise txt = {['X: ',num2str(pos(1))],... ['Y: ',num2str(pos(2))], ... 'Warning: Downsampled', ... 'May not be accurate'}; end |
b197c3fdf first commit |
283 284 285 286 |
end %-------------------------------------------------------------------------- function myErrorCheck |
1a0e88f0c replace 4-spaces ... |
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 |
% Do some error checking on the input arguments. if ~isa(numPoints, 'double') || numel(numPoints) > 1 || numPoints < 500 error('Third argument must be a scalar greater than 500'); end if ~isnumeric(x) || ~isnumeric(y) error('Arguments must be numeric'); end if length(size(x)) > 2 || length(size(y)) > 2 error('Only 2-D data accepted'); end % If only one input, create index vector X if isempty(x) if ismember(1, size(y)) x = reshape(1:numel(y), size(y)); else x = (1:size(y, 1))'; end end if ~ismember(1, size(x)) error('First argument has to be a vector'); end if ~isequal(size(x, 1), size(y, 1)) && ~isequal(size(x, 2), size(y, 2)) error('One of the dimensions of the two arguments must match'); end if any(diff(x) <= 0) error('The first argument has to be a monotonically increasing vector'); end |
b197c3fdf first commit |
317 318 319 |
end end |