/
nearest.m
185 lines (163 loc) · 6.09 KB
/
nearest.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
function [indx] = nearest(array, val, insideflag, toleranceflag)
% NEAREST return the index of an array nearest to a scalar
%
% Use as
% [indx] = nearest(array, val, insideflag, toleranceflag)
%
% The second input val can be a scalar, or a [minval maxval] vector for
% limits selection.
%
% If not specified or if left empty, the insideflag and the toleranceflag
% will default to false.
%
% The boolean insideflag can be used to specify whether the value should be
% within the array or not. For example nearest(1:10, -inf) will return 1,
% but nearest(1:10, -inf, true) will return an error because -inf is not
% within the array.
%
% The boolean toleranceflag is used when insideflag is true. It can be used
% to specify whether some tolerance should be allowed for values that are
% just outside the array. For example nearest(1:10, 0.99, true, false) will
% return an error, but nearest(1:10, 0.99, true, true) will return 1. The
% tolerance that is allowed is half the distance between the subsequent
% values in the array.
%
% See also FIND
% Copyright (C) 2002-2012, Robert Oostenveld
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
% FieldTrip is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% FieldTrip is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$
mbreal(array);
mbreal(val);
mbvector(array);
assert(all(~isnan(val)), 'incorrect value (NaN)');
if numel(val)==2
% interpret this as a range specification like [minval maxval]
% see also http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=1431
intervaltol = eps;
sel = find(array>=val(1) & array<=val(2));
if isempty(sel)
ft_error('The limits you selected are outside the range available in the data');
end
indx = sel([1 end]);
if indx(1)>1 && abs(array(indx(1)-1)-val(1))<=intervaltol
indx(1) = indx(1)-1;
end
if indx(2)<length(array) && abs(array(indx(2)+1)-val(2))<=intervaltol
indx(2) = indx(2)+1;
end
return
end
mbscalar(val);
if nargin<3 || isempty(insideflag)
insideflag = false;
end
if nargin<4 || isempty(toleranceflag)
toleranceflag = false;
end
% ensure that it is a column vector
array = array(:);
% determine the most extreme values in the array
minarray = min(array);
maxarray = max(array);
% do some strict checks whether the value lies within the min-max range
if insideflag
if ~toleranceflag
if val<minarray || val>maxarray
if numel(array)==1
ft_warning('the selected value %g should be within the range of the array from %g to %g', val, minarray, maxarray);
else
ft_error('the selected value %g should be within the range of the array from %g to %g', val, minarray, maxarray);
end
end
else
if ~isequal(array, sort(array))
ft_error('the input array should be sorted from small to large');
end
if numel(array)<2
ft_error('the input array must have multiple elements to compute the tolerance');
end
mintolerance = (array(2)-array(1))/2;
maxtolerance = (array(end)-array(end-1))/2;
if val<(minarray-mintolerance) || val>(maxarray+maxtolerance)
ft_error('the value %g should be within the range of the array from %g to %g with a tolerance of %g and %g on both sides', val, minarray, maxarray, mintolerance, maxtolerance);
end
end % toleragceflag
end % insideflag
% FIXME it would be possible to do some soft checks and potentially give a
% warning in case the user did not explicitly specify the inside and
% tolerance flags
% note that [dum, indx] = min([1 1 2]) will return indx=1
% and that [dum, indx] = max([1 2 2]) will return indx=2
% whereas it is desired to have consistently the match that is most towards the side of the array
if val>maxarray
% return the last occurrence of the largest number
[dum, indx] = max(flipud(array));
indx = numel(array) + 1 - indx;
elseif val<minarray
% return the first occurrence of the smallest number
[dum, indx] = min(array);
else
% implements a threshold to correct for errors due to numerical precision
% see http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=498 and http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=1943
% if maxarray==minarray
% precision = 1;
% else
% precision = (maxarray-minarray) / 10^6;
% end
%
% % return the first occurrence of the nearest number
% [dum, indx] = min(round((abs(array(:) - val)./precision)).*precision);
% use find instead, see http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=1943
wassorted = true;
if ~issorted(array)
wassorted = false;
[array, xidx] = sort(array);
end
indx2 = find(array<=val, 1, 'last');
indx3 = find(array>=val, 1, 'first');
if abs(array(indx2)-val) <= abs(array(indx3)-val)
indx = indx2;
else
indx = indx3;
end
if ~wassorted
indx = xidx(indx);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function mbreal(a)
if ~isreal(a)
ft_error('Argument to mbreal must be real');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function mbscalar(a)
if ~all(size(a)==1)
ft_error('Argument to mbscalar must be scalar');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function mbvector(a)
if ndims(a) > 2 || (size(a, 1) > 1 && size(a, 2) > 1)
ft_error('Argument to mbvector must be a vector');
end