【问题标题】:How to leave only the elements that appear once per specific row x column per page?如何只保留每页特定行 x 列出现一次的元素?
【发布时间】:2019-09-12 09:43:09
【问题描述】:

我有一个 3D 数组。我需要删除位于同一行、同一列位置但在下一页(第 3 维)上的任何元素,并且只使用该位置的第一次出现。因此,如果所有页面都相乘,结果将为 0。

由于 3D 数组可以是任意大小,我不能像 isMember 这样对解决方案进行硬编码。我也不能使用unique,因为元素可以相同,只是不能共享相同的位置。

例如输入:

A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 1 1 0];
A(:,:,3) = [ 0 1 0];

想要的输出是:

A(:,:,1) = [ 1 0 2];
A(:,:,2) = [ 0 1 0];
A(:,:,3) = [ 0 0 0];

我怎样才能做到这一点?

【问题讨论】:

  • 添加不使用循环的明确要求是 A) 基于错误的前提(因为 JIT 大修循环不再慢),并且 B) 使现有答案无效。如果您想要一个无循环的解决方案,请首先验证提供的答案是否适用于您的案例,然后尝试自己对其进行矢量化。 Stack Overflow 不是免费的编码服务。

标签: matlab matrix


【解决方案1】:

不是最优雅的,但至少它有效。

A(:,:,1) = [ 1 0 2 ];
A(:,:,2) = [ 1 1 0 ];
A(:,:,3) = [ 0 1 0 ];

for ii = 1:size(A,1)
    for jj = 1:size(A,2)
        unique_el = unique(A(ii, jj, :)); % Grab unique elements
        for kk = 1:numel(unique_el)
            idx = find(A(ii,jj,:) == kk);  % Contains indices of unique elements
            if numel(idx) > 1  % If an element occurs more than once
                A(ii, jj, idx(2:end)) = 0; % Set to 0
            end
        end
    end
end
A
A(:,:,1) =
     1     0     2
A(:,:,2) =
     0     1     0
A(:,:,3) =
     0     0     0

我遍历A 的前两个维度(行和列),通过第三个维度(页面)查找出现在某个行和列位置的任何唯一元素。然后将所有出现的唯一元素在第一个之后设置为0

考虑到更精细的 3D 矩阵,这仍然有效:

A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4]; 
A(:,:,1) =
     1     0     2     0
     2     1     3     0
A(:,:,2) =
     0     1     0     0
     0     2     1     0
A(:,:,3) =
     0     0     1     3
     1     0     2     4

如果您想要第一个非零元素并丢弃之后出现的 任何 元素,只需摆脱 unique() 调用:

A(:,:,1) = [1 0 2 0; 2 1 3 0];
A(:,:,2) = [1 1 0 0; 2 2 1 0];
A(:,:,3) = [0 1 1 3; 1 2 2 4];


for ii = 1:size(A,1)
    for jj = 1:size(A,2)
        idx = find(A(ii,jj,:) ~= 0);  % Contains indices of nonzero elements
        if numel(idx) > 1  % If more than one element
            A(ii, jj, idx(2:end)) = 0; % Set rest to 0
        end
    end
end
A(:,:,1) =
     1     0     2     0
     2     1     3     0
A(:,:,2) =
     0     1     0     0
     0     0     0     0
A(:,:,3) =
     0     0     0     3
     0     0     0     4

【讨论】:

    【解决方案2】:

    我的解决方案假设,对于给定的“位置”,任何值第一次出现后的每个值都被清除。这里的一些 MATLAB 常客对此进行了一些讨论,来自Adriaan's answer 中也使用的“扩展”示例。

    我使用permutereshape 重新排列输入,以便我们将所有“位置”作为二维数组中的“页面”列。然后,我们可以使用arrayfun 找到第一次出现非零值的正确索引(感谢LuisMendo's answer here)。再次使用这种方法,我们发现所有索引都设置为0

    让我们看看下面的代码:

    A(:,:,1) = [1 0 2 0; 2 1 3 0];
    A(:,:,2) = [1 1 0 0; 2 2 1 0];
    A(:,:,3) = [0 1 1 3; 1 2 2 4]
    
    [m, n, o] = size(A);
    
    B = reshape(permute(A, [3 1 2]), o, m*n);
    
    idx = arrayfun(@(x) find(B(:, x), 1, 'first'), 1:size(B, 2));
    idx = arrayfun(@(x) find(B(idx(x)+1:end, x)) + idx(x) + 3*(x-1), 1:size(B, 2), 'UniformOutput', false);
    idx = vertcat(idx{:});
    B(idx) = 0;
    
    B = permute(reshape(B, o, m , n), [2, 3, 1])
    

    当然,查看中间输出以了解我的方法的功能是有意义的。 (当然有些行可以合并,但我想保持一定的可读性。)

    还有,输出如下:

    A =
    
    ans(:,:,1) =
       1   0   2   0
       2   1   3   0
    
    ans(:,:,2) =
       1   1   0   0
       2   2   1   0
    
    ans(:,:,3) =
       0   1   1   3
       1   2   2   4
    
    B =
    
    ans(:,:,1) =
       1   0   2   0
       2   1   3   0
    
    ans(:,:,2) =
       0   1   0   0
       0   0   0   0
    
    ans(:,:,3) =
       0   0   0   3
       0   0   0   4
    

    如您所见,它与 Adriaan 的第二个版本相同。

    希望有帮助!

    【讨论】:

      【解决方案3】:

      矢量化解决方案。您可以使用max 的第二个输出来查找沿第三维第一次出现非零值的索引,然后使用sub2ind 将其转换为线性索引。

      A(:,:,1) = [ 1 0 2];
      A(:,:,2) = [ 1 1 0];
      A(:,:,3) = [ 0 1 0];
      [~, mi] =max(logical(A) ,[], 3);
      sz=size(A) ;
      [x, y] =ndgrid(1:sz(1),1:sz(2));
      idx=sub2ind( sz, x,y,mi);
      result=zeros(sz) ;
      result(idx) =A(idx);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多