user3743235
user3743235

Reputation: 159

MATLAB - How to replace the first NaN in each column with zero?

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

Answers (3)

Luis Mendo
Luis Mendo

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

Joachim Isaksson
Joachim Isaksson

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

Divakar
Divakar

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

Related Questions