同时选择多个矩阵中的NaN值

时间:2018-05-21 12:24:30

标签: matlab matrix

我有三个相同大小的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

在我的尝试中,我沿着尺寸为3x3x3的新矩阵ABC的第三维堆叠三个矩阵,然后我使用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 :(得分:2)

让我们做一下花哨的索引!

首先,解决方案:

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

此代码的作用基本上是创建一个3D矩阵,并计算每个NaN数组中有多少(i,j,:)。然后,如果有超过0(即其中任何一个是NaN),它将获得它的逻辑索引。最后,我们填写NaN的所有人,只留下非NaN

答案 1 :(得分:1)

Ander’s answer很好,但对于非常大的矩阵,创建3D矩阵可能会很昂贵。

首先,我建议将矩阵放入单元格数组中。这使得以编程方式管理许多数组变得更加容易。也就是说,而不是AB等,与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调用替换,但我喜欢显式循环。

编辑:以下是一些时间安排。这是现代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阵列也是如此。