Reputation: 159
I have a series of columns with positive values and then NaNs, for example:
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 NaN 2
NaN NaN 1
NaN NaN NaN
If this matrix is A
, then the (row) position of the first NaN in each column can be found using: sum(~isnan(A))
I would now like to replace the NaNs in each of these locations with a zero, for example:
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 0 2
0 NaN 1
NaN NaN 0
So far, my attempts using combinations of logicals, indices and loop approaches have failed, creating a new matrix with all NaNs as zeros, all values in each found row as zeros, or the NaN in only the first or last column as a zero.
What is the best way to achieve this? Thanks.
Upvotes: 1
Views: 837
Reputation: 112659
Approach based on accumarray
:
[ii jj] = find(isnan(A)); %// rows and columns of NaNs
ind = accumarray(jj,ii, [], @min); %// minimum row for each column with NaNs
ind = ind(ind~=0); %// a 0 indicates no NaNs in that column, so it should be removed
A(size(A,1)*(unique(jj)-1)+ind) = 0; %// set those entries to 0 using linear indexing
For example,
A = [ 12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 NaN 2
4 NaN NaN
1 5 NaN ]
is transformed into
A =
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 0 2
4 NaN 0
1 5 NaN
Upvotes: 2
Reputation: 180867
Divakar's solution works well, but as an alternate solution, you could use two nested cumsum
to get a mask with the first NaN per row, and use it to reset those values to 0;
A =
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 NaN 2
NaN 7 1
NaN NaN NaN
>>> A(cumsum(cumsum(isnan(A))) == 1) = 0
A =
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 0 2
0 7 1
NaN NaN 0
Upvotes: 5
Reputation: 221504
Approach #1
[m,n] = size(A) %// get size of input data
[v,ind] = max(isnan(A)) %// positions of first nans in each column
ind2 = bsxfun(@plus,ind,[0:n-1]*m) %// linear indices of those positions
A(ind2(v))=0 %// set values of all those positions to zero
Code run on a sample input (slightly different than the one in question for a better demo) -
A (Input) =
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 NaN 2
4 NaN 1
1 NaN NaN
A (Output) =
12 16 10
11 13 9
8 10 7
7 6 5
4 1 4
2 0 2
4 NaN 1
1 NaN 0
Approach #2
If you would like to use your sum(~isnan(A)
code, that could form another approach, but please note that this assumes that there are no numeric values once NaN elements start appearing in a column and as such approach #1 is a safer one. Here's the code for approach #2 -
[m,n] = size(A); %// get size of input data
ind = sum(~isnan(A))+1; %// positions of first nans in each column
v = ind<=m; %// position of valid ind values
ind2 = bsxfun(@plus,ind,[0:n-1]*m); %// linear indices of those positions
A(ind2(v))=0 %// set values of all those positions to zero
Upvotes: 5