倍频程/Matlab:确定超大矩阵中的唯一行

倍频程/Matlab:确定超大矩阵中的唯一行,matlab,octave,Matlab,Octave,我有一个八度的13146 x 13146矩阵,我想确定它的唯一行唯一(M,“行”)由于倍频程内部和/或内存限制而失败 我查看了其他关于查找唯一行的帖子,但没有一篇文章用大型矩阵来解决这个问题 我现在的做法是“分而治之”。G借 A(:,:,i)=M(r_i:s_i,:) B(:,:,i)=unique(A(:,:,i), "rows") 子矩阵的索引为i,r\u i和s\u i子矩阵的起始行号和结束行号。 要将所有数据返回到一个大矩阵中(并再次确定唯一行),请执行以下操作: 使用n子矩阵的数量,

我有一个八度的13146 x 13146矩阵,我想确定它的唯一行<代码>唯一(M,“行”)由于倍频程内部和/或内存限制而失败

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

我现在的做法是“分而治之”。G借

A(:,:,i)=M(r_i:s_i,:)
B(:,:,i)=unique(A(:,:,i), "rows")
子矩阵的索引为
i
r\u i
s\u i
子矩阵的起始行号和结束行号。 要将所有数据返回到一个大矩阵中(并再次确定唯一行),请执行以下操作:

使用
n
子矩阵的数量,
m
子矩阵中的原始行数和
l
列数

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

对其进行排序! 听起来你需要一个高效的内存排序算法。通过首先对行进行排序,然后检查相邻行中是否有重复行,可以找到唯一的行。您可以为此调整基数排序,按顺序对每个列进行排序(而不是按顺序对每个数字进行排序)。这将是排序一列而不是整个矩阵的最大内存开销。然后逐步遍历排序结果中的行并消除重复项。这是一个
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.

这里的基本思想与相同,只是后期的实现略有不同。因此,我们对输入数组的行进行排序,使重复项彼此重叠。然后,我们在各行中查找重复项,这可以通过
diff
有效地完成。从
diff
输出中,我们检测到
所有零
行,它们表示那些重复的行。我们在检测之外创建一个逻辑掩码,并使用该掩码从输入数组中提取有效行。这是实现,它似乎使用了
unique(…'rows')的一半内存。
-

sM = sortrows(M);
out = sM([true ; any(diff(sM,[],1)~=0,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

您关心输出中的行顺序吗?它必须和输入数组中的一样吗?@Divakar:不,结果的顺序不重要。太棒了!因此,这里发布的Andrew的答案一定非常有效。如果您在排序时跟踪重新排列的行索引,Andrew的答案在输入顺序方面也是稳定的。:)可以忽略不计的内存开销。(或者,您可以使用这些跟踪索引精确地再现Matlab的
唯一
签名,并提供额外的输出。这可能非常有用。)您发布了一个答案!:DPerhaps您可以包括一些代码来举例说明:)您明白了-添加了简单(无索引跟踪)代码示例。:)如果可以的话,我会给你+10。。。。你已经很久没有回复了,我很想给你一笔赏金。噢,谢谢!有一段时间,我经历了堆栈溢出中断,但我正在重新编写Matlab,所以我最近一直在工作。在我的13146^2矩阵和16412^2矩阵上工作得非常好。所以谢谢你,但在接受之前,我还得试试其他的。
>> 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