【问题标题】:Octave / Matlab : Determine unique rows in very large matrixOctave / Matlab:确定非常大的矩阵中的唯一行
【发布时间】:2015-04-23 16:16:31
【问题描述】:

我在 Octave 中有一个 13146 x 13146 矩阵,我想确定它的唯一行。 unique(M, "rows") 由于 Octave 内部和/或内存限制而失败。

我查看了有关查找唯一行的其他帖子,但没有一个解决了大型矩阵的这个问题。

我现在的方法是“分而治之”,例如。 G。由

A(:,:,i)=M(r_i:s_i,:)
B(:,:,i)=unique(A(:,:,i), "rows")

i 是子矩阵的索引,r_is_i 是子矩阵的开始和结束行号。 要将所有数据返回到一个大矩阵中(并再次确定唯一行):

C(:,:,i)=B(:,:,i)'
D=reshape(C,l,n*m)
E=D'
F=unique(E, "rows")

n 是子矩阵的数量,m 是子矩阵中的原始行数,l 是列数。

有没有更好的方法来达到预期的效果?

【问题讨论】:

  • 你关心输出中的行顺序吗?它必须与输入数组中的相同吗?
  • @Divakar:不,结果的顺序无关紧要。
  • 太棒了!所以,安德鲁在这里发布的答案一定非常有效。
  • 如果您在进行排序时跟踪重新排列的行索引,则安德鲁的答案在输入排序方面也是稳定的。 :) 可忽略的内存开销。
  • (或者您可以使用这些跟踪索引精确地重现 Matlab 的 unique 签名,以及额外的输出。这可能非常有用。)

标签: matlab octave


【解决方案1】:

基数排序!

听起来您需要一种内存效率高的排序算法。通过首先对行进行排序,然后检查相邻行是否有重复,可以找到唯一行。您可以为此调整基数排序,按顺序对每一列进行排序(而不是按顺序对每个数字进行排序)。这将是排序一列而不是整个矩阵的峰值内存成本。然后逐步遍历排序结果中的行并消除重复项。这是一个O(n) 操作,只需要足够的内存来容纳两行。

它也可以是“稳定的”。如果在排序过程中除了跟踪重新排列的行值之外还跟踪重新排列的行索引,则可以计算输入-输出映射索引。 (这些是 Matlab 自己的 [B,I] = sort(A) 签名中的 I。)这反过来将允许您将删除后的行重新排列回输入中的原始顺序,因此您可以保留它们的顺序。 (如 Matlab 的unique()setOrder='stable' 选项。)它们还可以用于计算整体唯一性操作的输入输出映射索引,因此您可以重现unique() 的完整多输出签名,它可以很有用。

示例代码

这是一个基本的示例实现。我还没有彻底测试过,所以不要在没有自己测试的情况下在生产中使用它。

function A = rrunique(A)
%RRUNIQUE "Radix Row Unique" - find unique rows using radix sort
%
% # Returns the distinct rows in A. Uses the memory-efficient radix sort
% # algorithm, so peak memory usage stays low(ish) for large matrices.

% # This uses a modified radix sort where only the row remapping indexes are
% # rearranged at each step, instead of sorting the whole input, to avoid
% # having to rewrite the large input matrix.

ix = 1:size(A,1); % # Final in-out mapping indexes

% # Radix sort the rows
for iCol = size(A,2):-1:1
    c = A(ix,iCol);
    [~,ixStep] = sort(c);
    % # Don't do this! too slow
    % # A = A(ixStep,:);
    % # Just reorder the mapping indexes
    ix = ix(ixStep);
end    

% # Now, reorder the big array all at once
A = A(ix,:);

% # Remove duplicates
tfKeep = true(size(A,1),1);
priorRow = A(1,:);
for iRow = 2:size(A,1)
    thisRow = A(iRow,:);
    if isequal(thisRow, priorRow)
        tfKeep(iRow) = false;
    else
        priorRow = thisRow;
    end
end
A = A(tfKeep,:);

end

当我在 OS X 上的 Matlab R2014b 上对您大小的矩阵进行测试时,它使用的内存达到了大约 3 GB 的峰值,而仅保存输入矩阵大约需要 1 GB。还不错。

>> m = rand([13146,13146]);
>> tic; rrunique(m); toc
Elapsed time is 17.435783 seconds.

【讨论】:

  • 您发布了答案! :D
  • 也许你可以包含一些代码来举例说明:)
  • 你明白了 - 添加了简单的(无索引跟踪)代码示例。 :)
  • 如果可以的话,我会给你 +10 ......你已经很久没有发布答案了,我很想给你一个赏金。
  • 噢,谢谢!我在 Stack Overflow 上休息了一段时间,但我又开始编写 Matlab 代码了,所以我最近一直在附近。
【解决方案2】:

您可以在列上使用滑动窗口,并使用不会导致内存问题的窗口大小。这是一个解决方案

function A = winuninque(A, winSz)
nCol = size(A,2);
I = zeros(size(A,1), 0);
for k=1:winSz:nCol
    [~, ~, I] = unique([I A(:,k:min(k+winSz-1,end))], 'rows');
end
[~, I] = unique(I);
A = A(I, :);
end

基准测试

为了实际上有一些重复的行,最好生成带有一些重复的矩阵,否则它将只是排序。以下是不同方法之间的比较:

>> A=repmat(rand(13146,13146), 2, 1);
>> A=A(randperm(end), :);
>> A=A(1:end/2,:);

>> tic; B=rrunique(A); toc
Elapsed time is 13.318752 seconds.
>> tic; C=winunique(A, 16); toc
Elapsed time is 6.606122 seconds.
>> tic; D=unique(A, 'rows'); toc
Elapsed time is 29.815333 seconds.
>> isequal(B,C,D)
ans =
     1
>> size(D)
ans =
        9880       13146

【讨论】:

  • 在我的 13146^2 矩阵和 16412^2 矩阵上工作得很好。所以谢谢你,但在接受之前,我还必须尝试其他的。
【解决方案3】:

这里的基本思路和Andrew's answer是一样的,只是后期实现有点不同。因此,我们对输入数组的行进行排序,使重复项彼此重叠。然后,我们遍历行寻找重复项,这可以使用diff 有效地完成。从diff 输出中,我们检测到代表那些重复行的all zeros 行。我们从 detection 中创建一个逻辑掩码,并使用此掩码从输入数组中提取 valid 行。这是实现,这似乎比 unique(...'rows') 使用了一半的内存 -

sM = sortrows(M);
out = sM([true ; any(diff(sM,[],1)~=0,2)],:);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-20
    • 2014-09-07
    • 1970-01-01
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多