Замените значения, отличные от 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