find 'last' function not working

2 views (last 30 days)
Hi,
I am using "find" function to identify the first and last row where no-NaN values can be found in an array, as below. The interesting fact is that while 'first' works fine and returns the first index, the 'last' result is a huge number which even exceeds the number of rows in my array. Any ideas on why this happens? Could it be a bug or am I doing a logical error?
Monotonic_first_index = find(~isnan(Monotonic),1,'first')
Monotonic_last_index = find(~isnan(Monotonic),1,'last');
  2 Comments
Vasileios Papavasileiou
Vasileios Papavasileiou on 11 Jul 2022
It worked with the accepted answer. Thanks :)

Sign in to comment.

Accepted Answer

Stephen23
Stephen23 on 11 Jul 2022
Edited: Stephen23 on 11 Jul 2022
"Any ideas on why this happens?"
Yes.
"Could it be a bug or am I doing a logical error? "
You are doing several logical errors.
"I am using "find" function to identify the first and last row ..."
Nope, you are not.
Your code actually FINDs the first/last linear index in your matrix:
linear_index = find(...);
%^^^^^^^^^^^ !!!! one output is a linear index !!!!
If you want FIND to return the row and column indices, then two outputs must be specified:
[row_index,~] = find(...);
%^^^^^^^^^ !!! row index !!!
% ^ unused column index
however that will still be buggy, because FIND searches through the entire matrix linearly (i.e. column-by-column) until it finds the requested condition, which might not be the last row in your sense. Consider this simple matrix:
M1 = [0,1;1,0;1,0;0,0]
M1 = 4×2
0 1 1 0 1 0 0 0
[R,C] = find(M1,1,'last')
R = 1
C = 2
Does this mean that the last row with any non-zero element is row one? Of course not, that is just the last element in the entire matrix, with all elements taken linearly. We can also see that your approach is also flawed with finding the first row too, as a simple example demonstrates:
M2 = [0,0;0,1;0,1;1,0]
M2 = 4×2
0 0 0 1 0 1 1 0
[R,C] = find(M2,1,'first')
R = 4
C = 1
Does this mean that the first row with non-zero element is row four? Of course not, that is just the first element of the entire matrix, with all elements taken linearly.
If you want the first/last row containing any non-zero element then you could add some logical operations, e.g. ANY/ALL:
R = find(any(M1,2),1,'last')
R = 3
Specifically for your question:
M = rand(7,3);
M([3,7,8,19]) = NaN
M = 7×3
0.2319 NaN 0.6849 0.1484 0.6545 0.0926 NaN 0.3581 0.7394 0.7489 0.1623 0.2809 0.1710 0.1594 NaN 0.9100 0.8270 0.4823 NaN 0.5785 0.7027
X = all(~isnan(M),2);
find(X,1,'first')
ans = 2
find(X,1,'last')
ans = 6
  1 Comment
Vasileios Papavasileiou
Vasileios Papavasileiou on 11 Jul 2022
Edited: Vasileios Papavasileiou on 11 Jul 2022
Thanks for the detailed explanation, answered many questions at the same time :)
In this case "find" without logical operations works for me, since I know in advance that either all data in a row will be NaN, or none of them will be none, but useful to know for future work.

Sign in to comment.

More Answers (2)

Bruno Luong
Bruno Luong on 11 Jul 2022
Edited: Bruno Luong on 11 Jul 2022
Beside using 'any'/'all', you might make two modifications to get what you want
[Monotonic_last_row, ~] = find(~isnan(Monotonic.' ),1,'last'); % transpose and 2-output call

Steven Lord
Steven Lord on 11 Jul 2022
Another possible solution, beyond calling find with two output arguments, is to call it with one output then convert the linear index into subscripts using ind2sub. This is particularly useful if the input to find is an N-dimensional array (with N > 2.)
rng default % for reproducibility
A = randi(10, 3, 4, 5);
linearIndices = find(A == 9)
linearIndices = 4×1
1 15 23 36
Make a cell array to hold subscripts, one per dimension of the original array.
subscripts = cell(1, ndims(A));
Now fill in the elements of that cell array by treating it as a comma-separated list to call ind2sub with a number of outputs equal to the number of dimensions of A.
[subscripts{:}] = ind2sub(size(A), linearIndices)
subscripts = 1×3 cell array
{4×1 double} {4×1 double} {4×1 double}
Let's spot check one of the elements, say the third one.
r = subscripts{1}(3);
c = subscripts{2}(3);
p = subscripts{3}(3);
shouldBe9 = A(r, c, p);
fprintf("Element A(%d, %d, %d) is %d.\n", r, c, p, shouldBe9)
Element A(2, 4, 2) is 9.
One final check with the hard-coded values (this is where the rng call comes in handy; I know that any time this example runs the A array will be the same so I can hard-code these values.)
A(2, 4, 2)
ans = 9

Tags

Products


Release

R2021b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!