Замените значения, отличные от NaN, с их индексами строк в матрице

У меня есть матрица 4x2 A:

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

Я хочу заменить значения, отличные от NaN, их ассоциированными индексами в каждом столбце в A. Результат выглядит следующим образом:

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

Я знаю, как это сделать для каждого столбца вручную, но я бы хотел автоматическое решение, так как у меня есть гораздо большие матрицы для обработки. У кого-нибудь есть идея?

Ответ 1

Применение ind2sub к маске, созданной с помощью isnan.

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

Обратите внимание, что второй вывод ind2sub должен быть запрошен (но пренебрег с помощью ~), а также [rows,~], чтобы указать, что вы хотите получить для 2D-матрицы.


A =

     1     1
   NaN   NaN
     3     3
     4   NaN

A.' =

     1   NaN     3     4
     1   NaN     3   NaN

Также будьте осторожны с двумя разными транспонированными операторами ' и .'.


Alternative

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

или даже (с небольшим вдохновением Луис Мендо)

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

или в одной строке

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

Ответ 2

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

Как это работает:

  • A-A+1 заменяет действительные числа в A на 1 и сохраняет NaN как NaN
  • (1:size(A,1)).' - это вектор столбца индексов строк
  • bsxfun(@times, ...) умножает оба из вышеперечисленного с однотонным расширением.

Как указано @thewaywewalk, в Matlab R2016 и далее bsxfun(@times...) можно заменить на .*, поскольку однопользовательское расширение включено по умолчанию:

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

Альтернативой, предложенной @Dev-Il, является

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

Это работает, потому что умножение на 0 заменяет действительные числа на 0 и сохраняет NaN как есть.

Ответ 3

Это можно сделать, используя repmat и isnan следующим образом:

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

Вывод:

 1   NaN     3     4
 1   NaN     3   NaN

Вы можете взять транспонирование, если хотите.

Ответ 4

Я добавляю еще один ответ по нескольким причинам:

  • Потому что overkill (* ahem * kron * ahem *) - это весело.
  • Чтобы продемонстрировать, что A*0 делает то же самое, что и 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