-
Notifications
You must be signed in to change notification settings - Fork 7
/
SplitVec.m
256 lines (244 loc) · 8.56 KB
/
SplitVec.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
function varargout = SplitVec(v, fun, varargin)
% [out1, out2, ...] = SplitVec(V, FUN, type1, type2, ...)
% [out1, out2, ...] = SplitVec(V, COL, type1, type2, ...)
%
% Purpose: Partition an input vector V into smaller series of subvectors
% of consecutive elements based on split points
%
% EXAMPLE:
% > s = SplitVec([1 1 1 2 2 3])
% > s returned is: {[1 1 1] [2 2] [3]}
%
% INPUT:
% V: main argument, array of the length n.
% It can be also matrix, in this case SplitVec works along the first
% dimension
%
% FUN: Typical value 'equal' 'consecutive', 'reverse'
% It can be a customized function that - when apply on V - returns
% a logical array of length n (or n-1), contains *true* at the same
% location where the LAST element of a series (i.e., right before the
% break occurs). Example:
% V: [5 5 5 2 2 3 3 3 4]
% FUN(V): [0 0 1 0 1 0 0 1 1]
% - By default FUN is 'equal': @(v) any(diff(v,1,dim),2)~=0
% Group by series of equal elements. DIM is 2 for row vector
% 1 otherwise.
% - Set FUN to 'consecutive': diff(V(:,1)-(1:n)')~=0 to split
% into series of consecutive integers (n denotes size(V,1))
% - Set FUN to 'reverse': diff(V(:,1)-(n:-1:1)')~=0 to split
% in series of the reverse ordered integers
% COL: Alternatively if the second parameter is an integer indexes,
% the 'EQUAL' grouping will be carried out based only on the
% specified column-indexes COL
%
% TYPE<k> can take one of the string values:
% - 'split': Split the vector V in cell. It is the DEFAULT output
% - 'first' 'begining': index of the first element of a series,
% - 'last' 'end': index of the last element of a series,
% - 'firstval' 'firstelem': value of the first element,
% - 'lastval' 'lastelem': value of the first element,
% - 'length': the length of the series,
% - 'bracket': the braket of the form [first last] of the series,
% - 'loc' 'location' 'index', 'indice': the row indices (first:last)
% of all series.
% - 'block' 'blockid': Cell contains block ID of all series,
% ID numerotations are {1,2,3,...}, each cell contains a row vector
% of the same length P as the corresponding block, each vector has
% constant ID repeated P times.
% - 'subsetorder': a cell contains order within a series,
% each cell contains a row vector (1:P) where P is
% the of the length of the corresponding block.
% User could provide a customized function handle for TYPE<k>.
% The function takes as input a subarray of V (one splitted series)
% and return a result.
% Example:
% SplitVec([1 2 3 1 2],'cons',@(x) mean(x.^2))
% - Additional input arguments are passed by the cell function
% handle form
% Example:
% SplitVec([1 2 3 1 2],'cons',{@(x,p) mean(x.^p) 4})
% - Use SplitVec(..., typefun, 'UniformOutput', false) to return
% composite output result in cell array.
%
% OUTPUT:
% See Type, by default V is splitted in series, stored in cell array.
%
% See also: sortrows, ismember, unique
%
% Author: Bruno Luong
% History:
% 17-May-2009 original
% 28-May-2009: multiple-column grouping
% 21-June-2009: correct bug when function handle is passed in second
% input.
% 07-Jul-2009: correct bug for empty array, group function
% Add output types: 'firstval' 'lastval'
% 02-Feb-2010: correct bug when 'loc' is required when working
% down the columns of matrix (NUMEL(V) -> SIZE(V,1))
% 26-Aug-2011: New builtin outputs: blockID and subsetorder
isrow = false;
% Reshape a row vector in column vector
if isvector(v) && size(v,1)==1
isrow = true;
v = v(:);
end
n = size(v,1);
groupfun = @(v) any(diff(v,1,1)~=0,2);
% Determine which function used for spliting
if nargin>=2 && ischar(fun)
switch lower(fun)
case {'group' 'same' 'eq' 'equal'}
fun = groupfun;
case {'cons' 'consecutive'}
fun = @(v) diff(v(:,1)-(1:n)')~=0;
case {'reverse'}
fun = @(v) diff(v(:,1)-(n:-1:1)')~=0;
otherwise
fun = groupfun;
end
elseif nargin>=2 && ...
~isa(fun,'function_handle') % case SplitVec(v, [1 3],...)
if ~isempty(fun)
col = fun;
fun = @(v) any(diff(v(:,col),1,1)~=0,2);
else
fun = groupfun;
end
elseif nargin<2 || isempty(fun)
fun = groupfun;
end
% Check if additional arguments are provides (cell function handle)
if iscell(fun)
arg = fun(2:end);
fun = fun{1};
else
arg = {};
end
b = feval(fun,v,arg{:});
if length(b)==size(v,1)-1
b = [true; b(:); true];
elseif length(b)==size(v,1) % numel(b)==numel(v)
b = [true; b(:)];
b(end) = true;
else
error('FUN returns incorrect output vector (wrong length)')
end
first = find(b);
lgt = diff(first);
first(end) = [];
if ~isempty(first)
last = first + lgt -1;
else
last = [];
end
% Assigned Output
if isempty(varargin)
outtype = {'split'};
else
outtype = varargin;
end
nout = length(outtype);
out = cell(1,nout);
getsplit('clean');
for k=1:nout
% Empty is set if function handles followed by 'UniformOutput'
if isempty(outtype{k})
continue
end
% Check if additional arguments are provides (cell function handle)
if iscell(outtype{k})
funk = outtype{k}{1};
argk = outtype{k}(2:end);
else % no
funk = outtype{k};
argk = {};
end
% Call user function handles
if isa(funk,'function_handle')
split = getsplit(isrow, v, lgt);
if k<=nout-2 && ischar(outtype{k+1}) && ...
~isempty(strmatch(outtype{k+1},'UniformOutput'))
unif = outtype{k+2};
outtype(k+1:k+2) = {[]};
else
unif = false;
end
out{k} = cellfun(@(obj) funk(obj, argk{:}), split, ...
'UniformOutput', unif);
elseif ischar(funk)
switch lower(funk)
case {'split'}
split = getsplit(isrow, v, lgt);
out{k} = split;
case {'first' 'begining'}
out{k} = first;
case {'last' 'end'}
out{k} = last;
case {'length'}
out{k} = lgt;
case {'firstval' 'firstelem'}
out{k} = v(first,:);
if isrow
out{k} = out{k}.';
end
case {'lastval' 'lastelem'}
out{k} = v(last,:);
if isrow
out{k} = out{k}.';
end
case {'bracket'}
out{k} = [first last];
case {'loc' 'location' 'index', 'indice'}
out{k} = mat2cell(1:size(v,1),1,lgt);
case {'block' 'blockid'}
% [ 1 1 1 2 2 3 ...] each block has a length in LGT
i = cumsum([0; lgt(1:end)])+1;
block = zeros(1,i(end)-1);
block(i(1:end-1)) = 1;
block = cumsum(block);
out{k} = mat2cell(block,1,lgt);
case {'subsetorder'}
% [ 1 1 1 2 2 3 ...] each block has a length in LGT
i = cumsum([0; lgt(1:end)])+1;
suborder = ones(1,i(end)-1);
suborder(i(2:end-1)) = 1-lgt(1:end-1);
suborder = cumsum(suborder);
out{k} = mat2cell(suborder,1,lgt);
otherwise
try % Try to evaluate as it is a function name
split = getsplit(isrow, v, lgt);
if k<=nout-2 && ischar(outtype{k+1}) && ...
~isempty(strmatch(outtype{k+1},'UniformOutput'))
unif = outtype{k+2};
outtype(k+1:k+2) = {[]};
else
unif = false;
end
out{k} = cellfun(@(obj) feval(funk, obj, argk{:}), ...
split, 'UniformOutput', unif);
catch %#ok
error('unknown outtype %s', out{k})
end
end % switch
end % ischar
end % for-loop on outputs
varargout=out;
getsplit('clean');
end % SplitVec
% Get a split arrays, do it once only, nested function
function s = getsplit(isrow, v, lgt)
persistent SPLIT
if ischar(isrow) && strcmpi(isrow,'clean')
SPLIT = [];
elseif ~iscell(SPLIT)
if ~isrow
SPLIT = mat2cell(v,lgt,size(v,2));
else
SPLIT = mat2cell(reshape(v,1,[]),1,lgt);
end
s = SPLIT;
else
s = SPLIT;
end
end % getsplit