Mercurial > octave-dspies
comparison scripts/set/unique.m @ 19003:d00f6b09258f @
Overhaul functions in scripts/set directory.
* set.txi: Rewrite documentation for set functions.
* intersect.m: Rewrite docstring. Use by_rows variable for code clarity.
Return output orientation which is compatible with Matlab. Add %!tests for
output orientation and N-dimensional inputs.
* setdiff.m: Rewrite docstring. Use by_rows variable for code clarity.
Rename output i to ia to clarify it is an index into the set a.
Return output orientation which is compatible with Matlab. Add %!tests for
N-dimensional inputs.
* setxor.m: Rewrite docstring. Use by_rows variable for code clarity.
Return output orientation which is compatible with Matlab. Add %!tests for
output orientation and N-dimensional inputs.
* union.m: Rewrite docstring. Use by_rows variable for code clarity.
Return output orientation which is compatible with Matlab. Add %!tests for
output orientation and N-dimensional inputs. Add %!tests for validsetargs
which are common to all set functions.
* unique.m: Rewrite docstring. Verify that input is numeric or cell array
of strings. Avoid computing idx for optional i,j outputs unless required.
Add %!error tests for input validation.
* ismember.m: Rewrite docstring. Use input variable 'a' instead of 'A' for
conformance with rest of set functions. Rename output index variable to
s_idx for clarity that it is an index into the set s.
* powerset.m: Rewrite doctring. Add input validation on nargin. Add %!error
input validation tests.
* module.mk: Include validsetargs.m in build system.
* validsetargs.m: Function renamed from validargs which was too general.
* validargs.m: Function renamed to validsetargs.
author | Rik <rik@octave.org> |
---|---|
date | Mon, 11 Aug 2014 09:39:45 -0700 |
parents | 7bbe3658c5ef |
children |
comparison
equal
deleted
inserted
replaced
19001:391e080ae810 | 19003:d00f6b09258f |
---|---|
18 ## <http://www.gnu.org/licenses/>. | 18 ## <http://www.gnu.org/licenses/>. |
19 | 19 |
20 ## -*- texinfo -*- | 20 ## -*- texinfo -*- |
21 ## @deftypefn {Function File} {} unique (@var{x}) | 21 ## @deftypefn {Function File} {} unique (@var{x}) |
22 ## @deftypefnx {Function File} {} unique (@var{x}, "rows") | 22 ## @deftypefnx {Function File} {} unique (@var{x}, "rows") |
23 ## @deftypefnx {Function File} {} unique (@dots{}, "first") | |
24 ## @deftypefnx {Function File} {} unique (@dots{}, "last") | |
25 ## @deftypefnx {Function File} {[@var{y}, @var{i}, @var{j}] =} unique (@dots{}) | 23 ## @deftypefnx {Function File} {[@var{y}, @var{i}, @var{j}] =} unique (@dots{}) |
26 ## Return the unique elements of @var{x}, sorted in ascending order. | 24 ## @deftypefnx {Function File} {[@var{y}, @var{i}, @var{j}] =} unique (@dots{}, "first") |
27 ## If the input @var{x} is a vector then the output is also a vector with the | 25 ## @deftypefnx {Function File} {[@var{y}, @var{i}, @var{j}] =} unique (@dots{}, "last") |
28 ## same orientation (row or column) as the input. For a matrix input the | 26 ## Return the unique elements of @var{x} sorted in ascending order. |
29 ## output is always a column vector. @var{x} may also be a cell array of | 27 ## |
30 ## strings. | 28 ## If the input @var{x} is a column vector then return a column vector; |
31 ## | 29 ## Otherwise, return a row vector. @var{x} may also be a cell array of strings. |
32 ## If the optional argument @qcode{"rows"} is supplied, return the unique | 30 ## |
33 ## rows of @var{x}, sorted in ascending order. | 31 ## If the optional argument @qcode{"rows"} is given then return the unique |
32 ## rows of @var{x} sorted in ascending order. The input must be a 2-D matrix | |
33 ## to use this option. | |
34 ## | 34 ## |
35 ## If requested, return index vectors @var{i} and @var{j} such that | 35 ## If requested, return index vectors @var{i} and @var{j} such that |
36 ## @code{x(i)==y} and @code{y(j)==x}. | 36 ## @code{@var{y} = @var{x}(@var{i})} and @code{@var{x} = @var{y}(@var{j})}. |
37 ## | 37 ## |
38 ## Additionally, if @var{i} is a requested output then one of @qcode{"first"} or | 38 ## Additionally, if @var{i} is a requested output then one of @qcode{"first"} or |
39 ## @qcode{"last"} may be given as an input. If @qcode{"last"} is specified, | 39 ## @qcode{"last"} may be given as an input. If @qcode{"last"} is specified, |
40 ## return the highest possible indices in @var{i}, otherwise, if @qcode{"first"} | 40 ## return the highest possible indices in @var{i}, otherwise, if @qcode{"first"} |
41 ## is specified, return the lowest. The default is @qcode{"last"}. | 41 ## is specified, return the lowest. The default is @qcode{"last"}. |
44 | 44 |
45 function [y, i, j] = unique (x, varargin) | 45 function [y, i, j] = unique (x, varargin) |
46 | 46 |
47 if (nargin < 1) | 47 if (nargin < 1) |
48 print_usage (); | 48 print_usage (); |
49 elseif (! (ismatrix (x) || iscellstr (x))) | |
50 error ("unique: X must be a matrix or cell array of strings"); | |
49 endif | 51 endif |
50 | 52 |
51 if (nargin > 1) | 53 if (nargin > 1) |
52 ## parse options | 54 ## parse options |
53 if (iscellstr (varargin)) | 55 if (! iscellstr (varargin)) |
54 optfirst = strcmp ("first", varargin); | |
55 optlast = strcmp ("last", varargin); | |
56 optrows = strcmp ("rows", varargin); | |
57 if (! all (optfirst | optlast | optrows)) | |
58 error ("unique: invalid option"); | |
59 endif | |
60 optfirst = any (optfirst); | |
61 optlast = any (optlast); | |
62 optrows = any (optrows); | |
63 if (optfirst && optlast) | |
64 error ('unique: cannot specify both "last" and "first"'); | |
65 endif | |
66 else | |
67 error ("unique: options must be strings"); | 56 error ("unique: options must be strings"); |
68 endif | 57 endif |
69 | 58 |
70 if (optrows && iscell (x)) | 59 optrows = any (strcmp ("rows", varargin)); |
60 optfirst = any (strcmp ("first", varargin)); | |
61 optlast = any (strcmp ("last", varargin)); | |
62 if (optfirst && optlast) | |
63 error ('unique: cannot specify both "first" and "last"'); | |
64 elseif (optfirst + optlast + optrows != nargin-1) | |
65 error ("unique: invalid option"); | |
66 endif | |
67 | |
68 if (optrows && iscellstr (x)) | |
71 warning ('unique: "rows" is ignored for cell arrays'); | 69 warning ('unique: "rows" is ignored for cell arrays'); |
72 optrows = false; | 70 optrows = false; |
73 endif | 71 endif |
74 else | 72 else |
73 optrows = false; | |
75 optfirst = false; | 74 optfirst = false; |
76 optrows = false; | |
77 endif | 75 endif |
78 | 76 |
79 ## FIXME: The operations | 77 ## FIXME: The operations |
80 ## | 78 ## |
81 ## match = (y(1:n-1) == y(2:n)); | 79 ## match = (y(1:n-1) == y(2:n)); |
85 ## fast as for full matrices, operate on the nonzero elements of the | 83 ## fast as for full matrices, operate on the nonzero elements of the |
86 ## sparse array as long as we are not operating on rows. | 84 ## sparse array as long as we are not operating on rows. |
87 | 85 |
88 if (issparse (x) && ! optrows && nargout <= 1) | 86 if (issparse (x) && ! optrows && nargout <= 1) |
89 if (nnz (x) < numel (x)) | 87 if (nnz (x) < numel (x)) |
90 y = unique ([0; (full (nonzeros (x)))], varargin{:}); | 88 y = unique ([0; nonzeros(x)], varargin{:}); |
91 else | 89 else |
92 ## Corner case where sparse matrix is actually full | 90 ## Corner case where sparse matrix is actually full |
93 y = unique (full (x), varargin{:}); | 91 y = unique (full (x), varargin{:}); |
94 endif | 92 endif |
95 return; | 93 return; |
105 | 103 |
106 y = x; | 104 y = x; |
107 ## Special cases 0 and 1 | 105 ## Special cases 0 and 1 |
108 if (n == 0) | 106 if (n == 0) |
109 if (! optrows && isempty (x) && any (size (x))) | 107 if (! optrows && isempty (x) && any (size (x))) |
110 if (iscell (y)) | 108 if (iscellstr (y)) |
111 y = cell (0, 1); | 109 y = cell (0, 1); |
112 else | 110 else |
113 y = zeros (0, 1, class (y)); | 111 y = zeros (0, 1, class (y)); |
114 endif | 112 endif |
115 endif | 113 endif |
125 [y, i] = sortrows (y); | 123 [y, i] = sortrows (y); |
126 else | 124 else |
127 y = sortrows (y); | 125 y = sortrows (y); |
128 endif | 126 endif |
129 match = all (y(1:n-1,:) == y(2:n,:), 2); | 127 match = all (y(1:n-1,:) == y(2:n,:), 2); |
130 idx = find (match); | 128 y(match,:) = []; |
131 y(idx,:) = []; | |
132 else | 129 else |
133 if (! isvector (y)) | 130 if (! isvector (y)) |
134 y = y(:); | 131 y = y(:); |
135 endif | 132 endif |
136 if (nargout > 1) | 133 if (nargout > 1) |
137 [y, i] = sort (y); | 134 [y, i] = sort (y); |
138 else | 135 else |
139 y = sort (y); | 136 y = sort (y); |
140 endif | 137 endif |
141 if (iscell (y)) | 138 if (iscellstr (y)) |
142 match = strcmp (y(1:n-1), y(2:n)); | 139 match = strcmp (y(1:n-1), y(2:n)); |
143 else | 140 else |
144 match = (y(1:n-1) == y(2:n)); | 141 match = (y(1:n-1) == y(2:n)); |
145 endif | 142 endif |
146 idx = find (match); | 143 y(match) = []; |
147 y(idx) = []; | |
148 endif | 144 endif |
149 | 145 |
150 if (isargout (3)) | 146 if (isargout (3)) |
151 j = i; | 147 j = i; |
152 if (dim == 1) | 148 if (dim == 1) |
155 j(i) = cumsum ([1, !match]); | 151 j(i) = cumsum ([1, !match]); |
156 endif | 152 endif |
157 endif | 153 endif |
158 | 154 |
159 if (isargout (2)) | 155 if (isargout (2)) |
156 idx = find (match); | |
160 if (optfirst) | 157 if (optfirst) |
161 i(idx+1) = []; | 158 idx += 1; # in-place is faster than other forms of increment |
162 else | 159 endif |
163 i(idx) = []; | 160 i(idx) = []; |
164 endif | |
165 endif | 161 endif |
166 | 162 |
167 endfunction | 163 endfunction |
168 | 164 |
169 | 165 |
170 %!assert (unique ([1 1 2; 1 2 1; 1 1 2]),[1;2]) | 166 %!assert (unique ([1 1 2; 1 2 1; 1 1 2]), [1;2]) |
171 %!assert (unique ([1 1 2; 1 0 1; 1 1 2],"rows"),[1 0 1; 1 1 2]) | 167 %!assert (unique ([1 1 2; 1 0 1; 1 1 2],"rows"), [1 0 1; 1 1 2]) |
172 %!assert (unique ([]),[]) | 168 %!assert (unique ([]), []) |
173 %!assert (unique ([1]),[1]) | 169 %!assert (unique ([1]), [1]) |
174 %!assert (unique ([1 2]),[1 2]) | 170 %!assert (unique ([1 2]), [1 2]) |
175 %!assert (unique ([1;2]),[1;2]) | 171 %!assert (unique ([1;2]), [1;2]) |
176 %!assert (unique ([1,NaN,Inf,NaN,Inf]),[1,Inf,NaN,NaN]) | 172 %!assert (unique ([1,NaN,Inf,NaN,Inf]), [1,Inf,NaN,NaN]) |
177 %!assert (unique ({"Foo","Bar","Foo"}),{"Bar","Foo"}) | 173 %!assert (unique ({"Foo","Bar","Foo"}), {"Bar","Foo"}) |
178 %!assert (unique ({"Foo","Bar","FooBar"}'),{"Bar","Foo","FooBar"}') | 174 %!assert (unique ({"Foo","Bar","FooBar"}'), {"Bar","Foo","FooBar"}') |
179 %!assert (unique (zeros (1,0)), zeros (0,1)) | 175 %!assert (unique (zeros (1,0)), zeros (0,1)) |
180 %!assert (unique (zeros (1,0), "rows"), zeros (1,0)) | 176 %!assert (unique (zeros (1,0), "rows"), zeros (1,0)) |
181 %!assert (unique (cell (1,0)), cell (0,1)) | 177 %!assert (unique (cell (1,0)), cell (0,1)) |
182 %!assert (unique ({}), {}) | 178 %!assert (unique ({}), {}) |
183 %!assert (unique ([1,2,2,3,2,4], "rows"), [1,2,2,3,2,4]) | 179 %!assert (unique ([1,2,2,3,2,4], "rows"), [1,2,2,3,2,4]) |
190 %!assert (unique (single ([1,2,2,3,2,4])), single ([1,2,3,4])) | 186 %!assert (unique (single ([1,2,2,3,2,4])), single ([1,2,3,4])) |
191 %!assert (unique (single ([1,2,2,3,2,4]'), "rows"), single ([1,2,3,4]')) | 187 %!assert (unique (single ([1,2,2,3,2,4]'), "rows"), single ([1,2,3,4]')) |
192 %!assert (unique (uint8 ([1,2,2,3,2,4]), "rows"), uint8 ([1,2,2,3,2,4])) | 188 %!assert (unique (uint8 ([1,2,2,3,2,4]), "rows"), uint8 ([1,2,2,3,2,4])) |
193 %!assert (unique (uint8 ([1,2,2,3,2,4])), uint8 ([1,2,3,4])) | 189 %!assert (unique (uint8 ([1,2,2,3,2,4])), uint8 ([1,2,3,4])) |
194 %!assert (unique (uint8 ([1,2,2,3,2,4]'), "rows"), uint8 ([1,2,3,4]')) | 190 %!assert (unique (uint8 ([1,2,2,3,2,4]'), "rows"), uint8 ([1,2,3,4]')) |
191 | |
195 %!test | 192 %!test |
196 %! [a,i,j] = unique ([1,1,2,3,3,3,4]); | 193 %! [a,i,j] = unique ([1,1,2,3,3,3,4]); |
197 %! assert (a, [1,2,3,4]); | 194 %! assert (a, [1,2,3,4]); |
198 %! assert (i, [2,3,6,7]); | 195 %! assert (i, [2,3,6,7]); |
199 %! assert (j, [1,1,2,3,3,3,4]); | 196 %! assert (j, [1,1,2,3,3,3,4]); |
215 %! [a,i,j] = unique (A, "rows"); | 212 %! [a,i,j] = unique (A, "rows"); |
216 %! assert (a, [1,2,3]); | 213 %! assert (a, [1,2,3]); |
217 %! assert (A(i,:), a); | 214 %! assert (A(i,:), a); |
218 %! assert (a(j,:), A); | 215 %! assert (a(j,:), A); |
219 | 216 |
220 %!error unique({"a", "b", "c"}, "UnknownOption") | 217 %% Test input validation |
221 %!error unique({"a", "b", "c"}, "UnknownOption1", "UnknownOption2") | 218 %!error unique () |
222 %!error unique({"a", "b", "c"}, "rows", "UnknownOption2") | 219 %!error <X must be a matrix or cell array of strings> unique ({1}) |
223 %!error unique({"a", "b", "c"}, "UnknownOption1", "last") | 220 %!error <options must be strings> unique (1, 2) |
224 | 221 %!error <cannot specify both "first" and "last"> unique (1, "first", "last") |
222 %!error <invalid option> unique (1, "middle") | |
223 %!error <invalid option> unique ({"a", "b", "c"}, "UnknownOption") | |
224 %!error <invalid option> unique ({"a", "b", "c"}, "UnknownOption1", "UnknownOption2") | |
225 %!error <invalid option> unique ({"a", "b", "c"}, "rows", "UnknownOption2") | |
226 %!error <invalid option> unique ({"a", "b", "c"}, "UnknownOption1", "last") | |
227 %!warning <"rows" is ignored for cell arrays> unique ({"1"}, "rows"); | |
228 |