MATLAB中每行的出现指数

时间:2013-10-04 02:56:01

标签: arrays matlab row matching indices

我有两个矩阵,AB。 (B是连续的,如1:n

我需要找到BA的每一行的所有出现次数,并将这些行索引相应地存储在单元格数组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的行向量长度不等。如果你可以建议一个解决办法,这也很好。

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

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

2 个答案:

答案 0 :(得分: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]中,{{1}行3中的行[1 2 3]4[1 3 4]5

然后,您可以根据需要使用[1 2]而不是单元格作为索引矩阵。

另一种方法是分配C,其中包含索引在C中出现的次数的预期值。

cLogical

答案:

% 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

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

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

>> C{:}

ans =

     2     3     4


ans =

     4


ans =

     1     2     3


ans =

     1     3     4


ans =

     1     2

答案 1 :(得分: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的值来说可能会很慢,但是如果你想要输出一个单元格,我不确定你能做多少好。 编辑:如果您可以处理类似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

不幸的是,如果 bsxfunsize(A,1)都很大,numel(B)可能会耗尽内存!如果内存成为问题,您可能必须遍历AB的元素。这是通过在B

中循环遍历顶点来实现此目的的一种方法
for i=1:numel(B), C{i} = find(any(A==B(i),2)); end
是的,这很容易。在MATLAB中,单元阵列的增长非常快,因为它类似于存储对数据的连续引用的序列容器,而不是保持数据本身是连续的。也许ismember是你测试的瓶颈。