首页 文章

MATLAB中每行出现的指标

提问于
浏览
2

我有两个矩阵, AB . ( B1:n 一样连续)

我需要在 A 中找到 B 的每一行的所有出现,并相应地将这些行索引存储在单元格数组 C 中 . 请参阅下面的示例 .

A = [3,4,5;1,3,5;1,4,3;4,2,1]
B = [1;2;3;4;5]

从而,

C = {[2,3,4];[4];[1,2,3];[1,3,4];[1,2]}

注意 C 不需要在我的应用程序的单元格数组中 . 我只建议它因为 C 的行向量长度不等 . 如果你可以建议一个解决方案,这也很好 .

我已经尝试使用循环运行ismember为 B 的每一行,但是当矩阵 AB 很大时,这个太慢了,大约有一百万个条目 . 矢量化的代码表示赞赏 .

(为了给你上下文,这个目的是在网格中识别附加到单个顶点的那些面 . 注意我不能使用函数edgeattachments,因为我的数据在三角剖分表示中不是“TR”形式 . 我只有一个面部列表和顶点列表 . )

2 回答

  • 2

    那么,对此最好的答案需要知道A是如何填充的 . 如果A是稀疏的,也就是说,如果它具有很少的列值并且B非常大,那么我认为节省内存的最佳方法可能是使用稀疏矩阵而不是单元格 .

    % No fancy stuff, just fast and furious 
    bMax = numel(B);
    nRows = size(A,1);
    
    cLogical = sparse(nRows,bMax);
    
    for curRow = 1:nRows
      curIdx = A(curRow,:);
      cLogical(curRow,curIdx) = 1;
    end
    

    回答:

    cLogical =
    
       (2,1)        1
       (3,1)        1
       (4,1)        1
       (4,2)        1
       (1,3)        1
       (2,3)        1
       (3,3)        1
       (1,4)        1
       (3,4)        1
       (4,4)        1
       (1,5)        1
       (2,5)        1
    

    如何阅读答案 . 对于每列,行显示列索引在A中显示的索引 . 即 1 出现在行 [2 3 4] 中, 2 出现在行 [4]3[1 2 3]4[1 3 4]5[1 2] 中 .

    然后,您可以根据需要使用 cLogical 而不是单元格作为索引矩阵 .

    另一种方法是为C分配一个索引在C中出现的次数的预期值 .

    % Fancier solution using some assumed knowledge of A
    bMax = numel(B);
    nRows = size(A,1);
    nColumns = size(A,2);
    
    % Pre-allocating with the expected value, an attempt to reduce re-allocations.
    % tic; for rep=1:10000; C = mat2cell(zeros(bMax,nColumns),ones(1,bMax),nColumns); end; toc 
    % Elapsed time is 1.364558 seconds.
    % tic; for rep=1:10000; C = repmat({zeros(1,nColumns)},bMax,1); end; toc
    % Elapsed time is 0.606266 seconds.
    % So we keep the not fancy repmat solution
    C = repmat({zeros(1,nColumns)},bMax,1);
    for curRow = 1:nRows
      curIdxMsk = A(curRow,:);
      for curCol = 1:nColumns
        curIdx = curIdxMsk(curCol);
        fillIdx = ~C{curIdx};
        if any(fillIdx) 
          fillIdx = find(fillIdx,1);
        else
          fillIdx = numel(fillIdx)+1;
        end
        C{curIdx}(fillIdx) = curRow;
      end
    end
    
    % Squeeze empty indexes:
    for curRow = 1:bMax
      C{curRow}(~C{curRow}) = [];
    end
    

    回答:

    >> C{:}
    
    ans =
    
         2     3     4
    
    
    ans =
    
         4
    
    
    ans =
    
         1     2     3
    
    
    ans =
    
         1     3     4
    
    
    ans =
    
         1     2
    

    哪种解决方案效果最佳?您在代码中进行性能测试,因为它取决于A,bMax,计算机的内存大小等等 . 然而,我仍然对其他人可以为此x)做的解决方案感到好奇 . 我喜欢chappjc的解决方案,尽管它有他指出的缺点 .

    对于给定的例子(10k次):

    Solution 1: Elapsed time is 0.516647 seconds. 
    Solution 2: Elapsed time is 4.201409 seconds (seems that solution 2 is a bad idea hahaha, but since it was created to the specific issue of A having many rows it has to be tested in those conditions).
    chappjc' solution: Elapsed time is 2.405341 seconds.
    
  • 1

    我们可以在不对 B 做任何假设的情况下做到这一点 . 尝试使用 bsxfunmat2cell

    M = squeeze(any(bsxfun(@eq,A,permute(B,[3 2 1])),2)); % 4x3x1 @eq 1x1x5 => 4x3x5
    R = sum(M); % 4x5 -> 1x5
    [ii,jj] = find(M);
    C = mat2cell(ii,R)
    

    上面的 C 中的单元格将是列向量而不是示例中的行 . 要使单元格包含行向量,请改用 C = mat2cell(ii',1,R)' .

    我唯一担心的是 mat2cell 对于数百万的 R 值来说可能会很慢,但如果你想在单元格中输出你的输出,我不确定你能做多少好 . EDIT :如果您可以像Werner的第一个解决方案那样处理稀疏矩阵,请使用以下内容替换上面的最后一行:

    >> Cs = sparse(ii,jj,1)
    Cs =
       (2,1)        1
       (3,1)        1
       (4,1)        1
       (4,2)        1
       (1,3)        1
       (2,3)        1
       (3,3)        1
       (1,4)        1
       (3,4)        1
       (4,4)        1
       (1,5)        1
       (2,5)        1
    

    不幸的是,如果 size(A,1)numel(B) 都很大, bsxfun 可能会耗尽内存!如果内存成为问题,您可能必须循环 AB 的元素 . 这是通过在 B 中循环遍历顶点来实现此目的的一种方法:

    for i=1:numel(B), C{i} = find(any(A==B(i),2)); end
    

    是的,那很容易 . 在MATLAB中,单元阵列的增长非常快,因为它类似于存储对数据的连续引用的序列容器,而不是保持数据本身是连续的 . 也许 ismember 是你测试的瓶颈 .

相关问题