Выбор одновременно не NaN значений в нескольких матрицах - PullRequest
0 голосов
/ 21 мая 2018

У меня есть три матрицы Matlab A, B и C с одинаковым размером:

A = [1:3; 4:6; 7:9];
B = [2 NaN 5; NaN NaN 7; 0 1 NaN];
C = [3 NaN 2; 1 NaN NaN; 1 NaN 5];

%>> A =               %>>B =               %>>C =
%     1     2     3   %     2   NaN     5  %     3   NaN     2
%     4     5     6   %   NaN   NaN     7  %     1   NaN   NaN
%     7     8     9   %     0     1   NaN  %     1   NaN     5

Я хотел бы, чтобы три матрицы сохраняли только значения, для которых каждая из 3 матриц не имеет NaNв этой конкретной позиции.То есть я хотел бы получить следующее:

%>> A =               %>>B =               %>>C =
%     1   NaN     3   %     2   NaN     5  %     3   NaN     2
%   NaN   NaN   NaN   %   NaN   NaN   NaN  %   NaN   NaN   NaN
%     7   NaN   NaN   %     0   NaN   NaN  %     1   NaN   NaN

В моей попытке я укладываю три матрицы вдоль третьего измерения новой матрицы ABC размером 3x3x3, а затем используюцикл for, чтобы убедиться, что все три матрицы не имеют NaN в этой конкретной позиции.

ABC(:,:,1)=A; ABC(:,:,2)=B; ABC(:,:,3)=C; 

for i=1:size(A,1)
    for j=1:size(A,2)
    count = squeeze(ABC(i,j,:));
    if sum(~isnan(count))<size(ABC,3)
        A(i,j)=NaN;
        B(i,j)=NaN;
        C(i,j)=NaN;
    end
    end
end

Этот код работает нормально.Однако, поскольку у меня более 30 матриц большего размера, мне было интересно, есть ли более элегантное решение этой проблемы.

Спасибо за вашу помощь.

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Ответ Андерса хорош, но для очень больших матриц создание этой трехмерной матрицы может быть дорогостоящим.

Прежде всего, я бы предложил поместить матрицы в массив ячеек.Это значительно облегчает программное управление многими массивами.То есть вместо A, B и т. Д. Работа с C{1}, C{2} и т. Д .:

C = {A,B,C};

Для внесения этого изменения требуется практически нулевая стоимость.

Теперь, чтобы найти все элементы, в которых одна из матриц имеет значение NaN:

M = isnan(C{1});
for ii=2:numel(C)
   M = M | isnan(C{ii});
end

Аналогичный цикл затем устанавливает соответствующие элементы в NaN:

for ii=1:numel(C)
   C{ii}(M) = NaN,
end

Этот последний цикл может бытьзаменяется вызовом cellfun, но мне нравятся явные циклы.


EDIT: Вот некоторые моменты времени.Это еще один пример того, что циклы в современной MATLAB работают быстрее, чем эквивалентный векторизованный код.В прежние времена код цикла был бы в 100 раз медленнее.

Это тестовый код:

function so(sz) % input argument is the size of the arrays

C3 = cell(1,3);
for ii=1:numel(C3)
   C3{ii} = create(sz,0.05);
end
C20 = cell(1,20);
for ii=1:numel(C20)
   C20{ii} = create(sz,0.01);
end

if(~isequal(method1(C3),method2(C3))), error('not equal!'), end
if(~isequal(method1(C20),method2(C20))), error('not equal!'), end

fprintf('method 1, 3 arrays: %f s\n',timeit(@()method1(C3)))
fprintf('method 2, 3 arrays: %f s\n',timeit(@()method2(C3)))
fprintf('method 1, 20 arrays: %f s\n',timeit(@()method1(C20)))
fprintf('method 2, 20 arrays: %f s\n',timeit(@()method2(C20)))

% method 1 is the vectorized code from Ander:
function mask = method1(C)
mask = sum(isnan(cat(3,C{:})),3)>0;

% method 2 is the loop code from this answer:
function mask = method2(C)
mask = isnan(C{1});
for ii=2:numel(C)
   mask = mask | isnan(C{ii});
end

function mat = create(sz,p)
mat = rand(sz);
mat(mat<p) = nan;

Это результаты на моей машине (с R2017a):

>> so(500)
method 1, 3 arrays: 0.003215 s
method 2, 3 arrays: 0.000386 s
method 1, 20 arrays: 0.016503 s
method 2, 20 arrays: 0.001257 s

Цикл в 10 раз быстрее!Для небольших массивов я вижу гораздо меньшую разницу, но код цикла все еще в несколько раз быстрее, даже для массивов 5x5.

0 голосов
/ 21 мая 2018

Давайте сделаем модную индексацию!

Во-первых, решение:

indnan=sum(isnan(cat(3,A,B,C)),3)>0;
A(indnan)=NaN;
B(indnan)=NaN;
C(indnan)=NaN;

То, что делает этот код, по сути создает трехмерную матрицу и вычисляет, сколько NaN имеется в каждой(i,j,:) массивы.Затем, если их больше 0 (то есть любое из них равно NaN), он получает для них логический индекс.Наконец, мы заполняем все эти значения NaN, оставляя в живых только не-1011 *.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...