简体   繁体   中英

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?

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.

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))

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.

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 AA .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM