How to sum parts of a matrix of different sizes, without using for loops?

3 ビュー (過去 30 日間)
ER2018
ER2018 2018 年 6 月 27 日
編集済み: Matt J 2018 年 6 月 29 日
I have a relatively large matrix NxN (N~20,000) and a Nx1 vector identifying the indices that must be grouped together. I want to sum together parts of the matrix, which in principle can have a different number of elements and non-adjacent elements. I quickly wrote a double for-loop that works correctly but of course it is inefficient. The profiler identified these loops as one of the bottlenecks in my code. I tried to find a smart vectorization method to solve the problem. I explored the arrayfun, cellfun, bsxfun functions, and looked for solutions to similar problems,... but I haven't found a final solution yet. I'll be grateful for any help! ER
This is the test code with the two for-loops:
M=rand(10); % test matrix
idxM=[1 2 2 3 4 4 4 1 4 2]; % each element indicates to which group each row/column of M belongs
nT=max(idxM);
sumM=zeros(nT,nT);
for t1=1:nT
for t2=1:nT
sumM(t1,t2)=sum(sum(M(idxM==t1,idxM==t2)));
end
end
PS: Long-term reader, first-time poster.
  5 件のコメント
ER2018
ER2018 2018 年 6 月 27 日
nT is typically 20 to 50 times smaller than N
Matt J
Matt J 2018 年 6 月 28 日
編集済み: Matt J 2018 年 6 月 28 日
You could speed up the for-loops somewhat by rewriting as follows:
for t2=1:nT
partials=sum( M(:,idxM==t2) ,2);
for t1=1:nT
sumM(t1,t2)=sum(partials(idxM==t1)); %1D sums
end
end

サインインしてコメントする。

採用された回答

Matt J
Matt J 2018 年 6 月 27 日
編集済み: Matt J 2018 年 6 月 27 日
S=sparse(1:N,idxM,1);
sumM=S.'*(M*S);
  13 件のコメント
Matt J
Matt J 2018 年 6 月 29 日
編集済み: Matt J 2018 年 6 月 29 日
The comparison between Tests 2 and 4 is interesting, but also a bit surprising. Under the hood, the sparse matrix form is holding the indices in the same form as the cell array. I would have expected much less of a difference.
ER2018
ER2018 2018 年 6 月 29 日
I agree... the first time I tried it it seemed like magic. And this is why I was interested in trying the approach with other functions.

サインインしてコメントする。

その他の回答 (1 件)

Matt J
Matt J 2018 年 6 月 29 日
編集済み: Matt J 2018 年 6 月 29 日
Here's another method. It isn't as fast as the sparse matrix approach, but it is loop-free and adaptable to other operations (min, max, etc...)
tmp=splitapply(@(z)sum(z,2),M,idxM.');
sumM=splitapply(@(z)sum(z,1),tmp,idxM);
  11 件のコメント
ER2018
ER2018 2018 年 6 月 29 日
編集済み: ER2018 2018 年 6 月 29 日
We're talking about the case with log, right?
Sorry, ignore that. I was indeed referring to the max code in the same comment.
Matt J
Matt J 2018 年 6 月 29 日
編集済み: Matt J 2018 年 6 月 29 日
My fastest version of sum(log), and also the most RAM-efficient, is as follows,
% Code 3 - Test
% NOT WORKING
tic;
tmp=splitapply(@(z)log(prod(z,2)),M,idxM.');
logM3=splitapply(@(z)sum(z,1),tmp,idxM);
T3=toc
It's potentially dangerous, because depending on nT and the magnitudes of M(i,j) the prod could overflow. However, with N=2e4,nT=400, and M=rand(N) this does not happen and I get T3=2.54 sec.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangePerformance and Memory についてさらに検索

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by