我在Octave中有一个13146 x 13146矩阵,我想确定它的唯一行.unique(M, "rows")
由于Octave内部和/或内存限制而失败.
我查看了其他有关查找唯一行的帖子,但没有一个用大型矩阵解决了这个问题.
我现在的方法是"分而治之",例如通过
A(:,:,i)=M(r_i:s_i,:)
B(:,:,i)=unique(A(:,:,i), "rows")
Run Code Online (Sandbox Code Playgroud)
与i
子矩阵的指数,r_i
和s_i
子矩阵的开始和结束的行号.要将所有数据返回到一个大矩阵(并再次确定唯一行):
C(:,:,i)=B(:,:,i)'
D=reshape(C,l,n*m)
E=D'
F=unique(E, "rows")
Run Code Online (Sandbox Code Playgroud)
使用n
子矩阵的数量,子矩阵中m
的原始行l
数和列数.
有没有更好的方法来达到预期的效果?
听起来你需要一个内存有效的排序算法.通过先排序行,然后检查相邻行是否有重复项,可以找到唯一的行.您可以为此调整基数排序,按顺序对每列进行排序(而不是按顺序排列每个数字).这将是排序一列而不是整个矩阵的峰值内存成本.然后逐步执行排序结果中的行并消除重复项.这是一个O(n)
操作,只需要足够的内存来容纳两行.
它也可以"稳定".如果在排序过程中除了重新排列的行值之外还跟踪重新排列的行索引,则可以计算输入 - 输出映射索引.(这些是I
Matlab自己的签名[B,I] = sort(A)
.)这反过来将允许您将重复后删除行重新排列回输入中的原始顺序,这样您就可以保留它们的顺序.(就像setOrder='stable'
Matlab的选项一样unique()
.)它们也可用于计算整体唯一性操作的输入输出映射索引,因此您可以重现完整的多输出签名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
Run Code Online (Sandbox Code Playgroud)
当我在OS X上的Matlab R2014b上对你的大小的矩阵进行测试时,它在使用的内存大约3 GB时达到峰值,而仅保留输入矩阵大约为1 GB.不错.
>> m = rand([13146,13146]);
>> tic; rrunique(m); toc
Elapsed time is 17.435783 seconds.
Run Code Online (Sandbox Code Playgroud)
您可以在列上使用滑动窗口,并使用不会导致内存问题的窗口大小。这是一个解决方案
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
Run Code Online (Sandbox Code Playgroud)
为了实际上有一些重复的行,最好生成具有一些重复行的矩阵,否则它只是排序。以下是不同方法之间的比较:
>> 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
Run Code Online (Sandbox Code Playgroud)