Using 'find' for finding whole numbers
30 visualizaciones (últimos 30 días)
Mostrar comentarios más antiguos
Hello,
I have a large matrix containing latitude gird edges (lat_grid) and I wish to find lat values from a vector (lat) using the find function. I use this command:
R = find(lat_grid <= lat,1,'first');
This works fine for floating numbers, but does work for whole numbers, which are numerous in the grid matrix (lat_grid).
How can I either convert the whole numbers to floating points with two decimal places?
OR how do I fix the find command to include finding the nearest whole numbers?
4 comentarios
per isakson
el 16 de Oct. de 2017
Cleve's piece is dated 1996 and I guess "flint" always been a part of Matlab. I think Cleve's piece is good background reading and it is relevant to comparison of floating point numbers. (I replace "called" by "they call" in my comment above.)
dpb
el 16 de Oct. de 2017
There's the key...and could be OPs issue if he were looking for a specific integer value -- but his test is for
R = find(lat_grid <= lat,1,'first');
which is an inequality, not an exact match so the FP rounding issue in comparisons internal to FIND won't be a factor unless his lat value were to be within eps of the value in question that was trying to be located or less than, in this case, any integer in the array being searched.
As Cleve says, integers in floating point are exact representations as long as their precision is within the length of the (53) bits available in the double mantissa but OP isn't searching for exact match anyway.
There's something else going on we don't have sufficient data to discern the problem as yet...
Respuestas (2)
dpb
el 16 de Oct. de 2017
Editada: dpb
el 17 de Oct. de 2017
find has no difficulty in returning integer-valued floating-point numbers (which is the default storage in Matlab and in an array all values are of the same type). Consequently, your issue is likely that the "integers" actually aren't quite actually integer-valued but were computed from floating point values and therefore have some rounding and are only displayed to precision such that they appear to be whole numbers.
There is a function specifically designed for such purpose ismembertol will find values within a tolerance of given value(s); that's probably what you want.
If this doesn't answer the ?, post a (small) dataset that illustrates the problem.
ADDENDUM
If you're going to traverse the route S Lord has shown with later revision than I have here, I'll withdraw from the playing field, but I'll note a few things from your additional input you've provided as general comments...
The sequence you wrote:
>> lonW = 0; %easterly limit
lonE = 360; %westerly limit
res = 0.25;
n_lon = (lonE - lonW)/res; %no. longitude boxes
for n = (1:n_lon)
lon_grid(n) = lonW + (n-1)*res;
end
>>
is quite inefficient...use something like
>> long=linspace(0,360-res,n_lon); % use builtin function rather than loop
>> all(long==lon_grid) % get same answer??? (Yes-->1)
ans =
1
>>
NB: The last entry in the lon_grid array is NOT 360 if that's the intent; it's one element short; hence the "-res" in the argument list.
Alternatively, there's colon that may be a little more handy given the delta instead of length...
>> long=[0:res:360-res]; % colon
>> all(long==lon_grid) % also builds identical vector
ans =
1
>>
Now the question is, do we, or do we not have integer-valued degrees or was there roundoff in generating the values???
>> sum(lon_grid==fix(lon_grid)) % how many are integer-valued?
ans =
360
>>
So indeed, there are 360 integers in the vector as we hoped, 0:359 (360 isn't in the array, remember? :) )
>> lon_grid(end) % last element is one short of 360...intended?????
ans =
359.7500
>>
Now back to your original question--
>> lat=89.15;
>> find(lon_grid<=lat,1,'last')
ans =
357
>> lon_grid(ans)
ans =
89
>>
So the logic DOES work if the inputs are as expected -- hence, the supposition earlier that there's something else going on in the actual case that isn't represented in the question and supporting data.
More than likely, lat isn't what you think it is or you've made some other coding error so the values just aren't what you really think they are at the time you see the failure -- as noted earlier, set a breakpoint to see what's going on in the actual application because the symptom isn't owing to the cause you think; the function works as expected if the data are what are here.
Now, this isn't to say that the newer functions might not be a better implementation of the problem; just that what problem you're having at present is not owing to find and integer/non-integer values; it's that you really have some other values than you think at the moment of the failure.
7 comentarios
Steven Lord
el 17 de Oct. de 2017
discretize can't handle a case like this:
discretize(5, [1 2 3 6 4 4.5])
The fourth bin would be [6, 4) but that doesn't make sense as a bin. Give me a number that is greater than or equal to 6 and less than 4 simultaneously.
You can make your edges non-decreasing and ask discretize to return the non-decreasing edges.
[n, edges] = discretize(5, sort([1 2 3 6 4 4.5]))
dpb
el 17 de Oct. de 2017
One last comment...the find solution works as you wrote it iff the searched-for value is greater than that wanted in absolute value -- this works for the vector 0:360 as all are positive values but it will NOT work reliably for the case of the latitude vector of -90:90 because the sign change switches the needed direction of the search.
Before the new-fangled discretize, one way to do this would have been histc returning the optional bin, another way that works irrespective of the sign is
>> interp1(lon_grid,1:length(lon_grid),fix(lon),'previous')
ans =
885
893
893
893
893
901
901
905
925
993
997
997
997
1001
1001
1001
1001
1001
1017
1033
>> lon_grid(ans)
ans =
Columns 1 through 17
221 223 223 223 223 225 225 226 231 248 249 249 249 250 250 250 250
Columns 18 through 20
250 254 258
>>
for the example vector you give above. The above is just an inverse lookup for the position in the array and is equivalent to
find(lon_grid==fix(lon))
excepting is "vectorizable" if that's a need; find isn't directly.
Guillaume
el 16 de Oct. de 2017
I use this command R = find(lat_grid <= lat,1,'first'). This works fine for floating numbers
No, it doesn't work. If it has worked so far, you've been lucky. It wouldn't be hard to find an example where this would break, i.e. where the true value stored in memory is just slightly above (or below) your search value.
The proper way of finding any value in an array of numbers which can be floating point is to do a comparison of the difference to a tolerance (or use ismembertol as has been suggested):
tol = 1e-6; %whatever is appropriate for the magnitude of your numbers
R = find(abs(lat_grid - lat) <= tol);
Your method will break. It may not have failed yet but it will.
1 comentario
dpb
el 17 de Oct. de 2017
Again, that's true if he were searching for an exact match on a FP, non-integer value. But, as demonstrated above, his data do contain the integer degree values (unless he's somehow inadvertently modified them which could be a problem, granted) so the test will work as intended.
Ver también
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!