以完全矢量化的方式将行中的零(或NAN)与前一个元素逐行或逐列替换

时间:2014-01-11 21:43:35

标签: matlab loops matrix vectorization

我需要用行中的前一个元素替换矩阵中的零(或NaN),所以基本上我需要这个Matrix X

[0,1,2,2,1,0;  
5,6,3,0,0,2;  
0,0,1,1,0,1]  

变成这样:

[0,1,2,2,1,1;  
5,6,3,3,3,2;  
0,0,1,1,1,1],  

请注意,如果第一行元素为零,它将保持不变。

我知道这已经以矢量化方式解决了单个行或列向量,这是最好的方法之一:

id = find(X);         
X(id(2:end)) = diff(X(id));       
Y = cumsum(X)  

问题是Matlab / Octave中矩阵的索引是连续的并且按列递增,因此它适用于单个行或列,但是不能应用相同的精确概念,但需要使用多行来修改raw / column开始新鲜,必须视为独立。我已经尽力了,谷歌搜索了整个谷歌,但没有找到出路。如果我在循环中应用相同的想法它会变得太慢,因为我的矩阵至少包含3000行。有人可以帮我解决这个问题吗?

5 个答案:

答案 0 :(得分:1)

在每行中分隔零的特殊情况

您可以使用find的双输出版本来定位除第一个之外的所有列中的零和NaN,然后​​使用linear indexing以行前导填充这些条目值:

[ii jj] = find( (X(:,2:end)==0) | isnan(X(:,2:end)) );
X(ii+jj*size(X,1)) = X(ii+(jj-1)*size(X,1));

一般情况(每行允许连续零)

X(isnan(X)) = 0; %// handle NaN's and zeros in a unified way
aux = repmat(2.^(1:size(X,2)), size(X,1), 1) .* ...
    [ones(size(X,1),1) logical(X(:,2:end))]; %// positive powers of 2 or 0
col = floor(log2(cumsum(aux,2))); %// col index
ind = bsxfun(@plus, (col-1)*size(X,1), (1:size(X,1)).'); %'// linear index
Y = X(ind);

诀窍是使用矩阵aux,如果X的相应条目为0且其列号大于1,则包含0;或者包含2个列号。因此,将cumsum行方式应用于此矩阵,取log2并向下舍入(矩阵col)会将最右边非零条目的列索引提供给当前条目,每行(所以这是一种行式“累积最大”功能。)它只能从列号转换为线性索引(带bsxfun;也可以用sub2ind完成)并使用它索引X

仅适用于中等大小的X。对于大尺寸,代码使用的2的幂很快接近realmax并且导致错误的索引。

示例:

X =
     0     1     2     2     1     0     0
     5     6     3     0     0     2     3
     1     1     1     1     0     1     1

给出

>> Y
Y =
     0     1     2     2     1     1     1
     5     6     3     3     3     2     3
     1     1     1     1     1     1     1

答案 1 :(得分:1)

您可以按如下方式概括您自己的解决方案:

Y = X.';                                       %'// Make a transposed copy of X
Y(isnan(Y)) = 0;
idx = find([ones(1, size(X, 1)); Y(2:end, :)]);
Y(idx(2:end)) = diff(Y(idx));
Y = reshape(cumsum(Y(:)), [], size(X, 1)).';   %'// Reshape back into a matrix

这通过将输入数据视为长向量,应用原始解决方案然后将结果重新整形回矩阵来工作。始终将第一列视为非零,以便值不会在整个行中传播。另请注意,原始矩阵是转置的,以便将其转换为行主要顺序的向量。

答案 2 :(得分:0)

x=[0,1,2,2,1,0;
5,6,3,0,1,2;
1,1,1,1,0,1];
%Do it column by column is easier
x=x';
rm=0;
while 1
    %fields to replace
    l=(x==0);
    %do nothing for the first row/column
    l(1,:)=0;
    rm2=sum(sum(l));
    if rm2==rm
        %nothing to do
        break;
    else
        rm=rm2;
    end
    %replace zeros
    x(l) = x(find(l)-1);
end
x=x';

答案 3 :(得分:0)

我有一个函数用于填充NaNs的类似问题。这可能会被削减或进一步加速 - 它是从具有更多功能(前向/后向填充,最大距离等)的预先存在的代码中提取的。

X = [
    0 1 2 2 1 0
    5 6 3 0 0 2
    1 1 1 1 0 1
    0 0 4 5 3 9
];

X(X == 0) = NaN;
Y = nanfill(X,2);
Y(isnan(Y)) = 0

function y = nanfill(x,dim)
if nargin < 2, dim = 1; end
if dim == 2, y = nanfill(x',1)'; return; end
i = find(~isnan(x(:)));
j = 1:size(x,1):numel(x);
j = j(ones(size(x,1),1),:);
ix = max(rep([1; i],diff([1; i; numel(x) + 1])),j(:));
y = reshape(x(ix),size(x));

function y = rep(x,times)
i = find(times);
if length(i) < length(times), x = x(i); times = times(i); end
i = cumsum([1; times(:)]);
j = zeros(i(end)-1,1);
j(i(1:end-1)) = 1;
y = x(cumsum(j));

答案 4 :(得分:0)

Eitan回答的修改版本,以避免跨行传播值:

Y = X'; %'
tf = Y > 0;
tf(1,:) = true;
idx = find(tf);
Y(idx(2:end)) = diff(Y(idx));
Y = reshape(cumsum(Y(:)),fliplr(size(X)))';