how to use accumarray over a matrix avoiding a loop?
29 次查看(过去 30 天)
显示 更早的评论
Hi,
I want to sum elements of a matrix according to an index. If instead of the matrix I had a vector, I could use accumarray. However, accumarray does not accept matrices as second argument. Any ideas on how to do this avoiding a loop to sum for each vector separately?
For example:
A=[1;1;2;3;3;3];
B=rand(6,10);
C=accumarray(A,B);
gives the output ??? Error using ==> accumarray Second input VAL must be a vector with one element for each row in SUBS, or a scalar.
I could do this by columns of B
C=zeros(size(unique(A),1),10);
for i=1:10,
C(:,i)=accumarray(A,B(:,i));
end
But if the number of columns of B is large, this will be quite slow and I would like this step to be done quickly as it has to be done as part of a minimization routine, so it is done every time the objective function is evaluated.
Thanks,
0 个评论
采纳的回答
Sean de Wolski
2013-2-12
编辑:Sean de Wolski
2013-2-12
You could always use accumarray with two dimensions of subs rather than vals:
A=[1;1;2;3;3;3];
B=rand(6,10);
[xx, yy] = ndgrid(A,1:size(B,2));
C=accumarray([xx(:) yy(:)],B(:));
Frankly, I'll bet the for-loop will be fastest.
More And my suspicions are right depending on how big A and B actually are. I am seeing the for-loop be faster with small arrays and accumarray with two-dimensional subs being faster for larger ones.
arrayfun/cell2mat etc are really slow since they require the conversion to cells which are slow and arrayfun is just slower than a for-loop anyway.
function timeAccumarray
A=[1;1;2;3;3;3];
B=rand(6,10);
[t1,t2,t3] = deal(0);
for ii = 1:100
tic
[xx, yy] = ndgrid(A,1:size(B,2));
C=accumarray([xx(:) yy(:)],B(:));
t1 = t1+toc;
tic
for jj=size(B,2):-1:1 %dynamically preallocate, avoiding slow unique()
C2(:,jj)=accumarray(A,B(:,jj));
end
t2=t2+toc;
tic
C3=cell2mat(arrayfun(@(x) accumarray(A,B(:,x)),1:size(B,2),'un',0));
t3 = t3+toc;
end
isequal(C,C2,C3)
[t1 t2 t3]
ans = 0.0072 0.0289 0.1381
1 个评论
Baris Gecer
2017-2-24
编辑:Baris Gecer
2017-2-24
This helped me a lot.Thanks! The first code was quite fast in my case where the matrix is 4096*128.
更多回答(3 个)
Jasper van Casteren
2022-2-8
编辑:Jasper van Casteren
2022-2-8
I have the following problem and solution:
for instance, I have a set of nodes of which I have the indices in A, and a set of measured injections at these nodes. I have multiple injections at the same node. B thus contains the same indices as A, but in random order and non-unique.
How to accumulate the injections in B per node in A?
A = unique node indices for which I need the matrix of injections
B = non-unique node indices for which I have a matrix of injections. All indices in B are also in A
V = matrix of injections for the nodes in B.
I use,
A = unique(randi([1,1000], 300, 1)); % few hundred random node indices
B = A(randi([1, numel(A)], 1000, 1)); % 1000 random measurement locations
nA = numel(A); % for convenience
nB = numel(B); % for convenience
V = randi([10,100], nB, 8760); % 8760 random measurements at 1000 locations
[~, B2A] = ismember(B,A);
printf('%d', any(B2A<1)); % should be zero
tic;
C = full(sparse(B2A, 1:nB, 1, nA, nB) * V);
toc
This produces
'0'
0.013 seconds
This is pretty fast for large matrices.
0 个评论
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Resizing and Reshaping Matrices 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!