Bowecho
Bowecho

Reputation: 909

Replace non-NaN values with their row indices within matrix

I have the 4x2 matrix A:

A = [2 NaN 5 8; 14 NaN 23 NaN]';

I want to replace the non-NaN values with their associated indices within each column in A. The output looks like this:

out = [1 NaN 3 4; 1 NaN 3 NaN]'; 

I know how to do it for each column manually, but I would like an automatic solution, as I have much larger matrices to handle. Anyone has any idea?

Upvotes: 12

Views: 540

Answers (4)

Robert Seifert
Robert Seifert

Reputation: 25232

Applying ind2sub to a mask created with isnan will do.

mask = find(~isnan(A));
[rows,~] = ind2sub(size(A),mask)
A(mask) = rows;

Note that the second output of ind2sub needs to be requested (but neglected with ~) as well [rows,~] to indicate you want the output for a 2D-matrix.


A =

     1     1
   NaN   NaN
     3     3
     4   NaN

A.' =

     1   NaN     3     4
     1   NaN     3   NaN

Also be careful the with the two different transpose operators ' and .'.


Alternative

[n,m] = size(A);
B = ndgrid(1:n,1:m);
B(isnan(A)) = NaN;

or even (with a little inspiration by Luis Mendo)

[n,m] = size(A);
B = A-A + ndgrid(1:n,1:m)

or in one line

B = A-A + ndgrid(1:size(A,1),1:size(A,2))

Upvotes: 8

Luis Mendo
Luis Mendo

Reputation: 112679

out = bsxfun(@times, A-A+1, (1:size(A,1)).');

How it works:

  • A-A+1 replaces actual numbers in A by 1, and keeps NaN as NaN
  • (1:size(A,1)).' is a column vector of row indices
  • bsxfun(@times, ...) multiplies both of the above with singleton expansion.

As pointed out by @thewaywewalk, in Matlab R2016 onwards bsxfun(@times...) can be replaced by .*, as singleton expansion is enabled by default:

out = (A-A+1) .* (1:size(A,1)).';

An alternative suggested by @Dev-Il is

out = bsxfun(@plus, A*0, (1:size(A,1)).');

This works because multiplying by 0 replaces actual numbers by 0, and keeps NaN as is.

Upvotes: 11

Dev-iL
Dev-iL

Reputation: 24169

I'm adding another answer for a couple of reasons:

  1. Because overkill (*ahem* kron *ahem*) is fun.
  2. To demonstrate that A*0 does the same as A-A.

A = [2 NaN 5 8; 14 NaN 23 NaN].';
out = A*0 + kron((1:size(A,1)).', ones(1,size(A,2)))

out =

     1     1
   NaN   NaN
     3     3
     4   NaN

Upvotes: 2

Sardar Usama
Sardar Usama

Reputation: 19689

This can be done using repmat and isnan as follows:

A = [ 2  NaN   5    8; 
     14  NaN  23  NaN];
out=repmat([1:size(A,2)],size(A,1),1); % out contains indexes of all the values
out(isnan(A))= NaN                     % Replacing the indexes where NaN exists with NaN

Output:

 1   NaN     3     4
 1   NaN     3   NaN

You can take the transpose if you want.

Upvotes: 6

Related Questions