how to use accumarray over a matrix avoiding a loop?

33 Ansichten (letzte 30 Tage)
Fernando
Fernando am 12 Feb. 2013
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,

Akzeptierte Antwort

Sean de Wolski
Sean de Wolski am 12 Feb. 2013
Bearbeitet: Sean de Wolski am 12 Feb. 2013
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 Kommentar
Baris Gecer
Baris Gecer am 24 Feb. 2017
Bearbeitet: Baris Gecer am 24 Feb. 2017
This helped me a lot.Thanks! The first code was quite fast in my case where the matrix is 4096*128.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (3)

Azzi Abdelmalek
Azzi Abdelmalek am 12 Feb. 2013
C=cell2mat(arrayfun(@(x) accumarray(A,B(:,x)),1:size(B,2),'un',0));

Fernando
Fernando am 12 Feb. 2013
Thanks for your help. I will check which of the three alternatives is better for my data.

Jasper van Casteren
Jasper van Casteren am 8 Feb. 2022
Bearbeitet: Jasper van Casteren am 8 Feb. 2022
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.

Community Treasure Hunt

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

Start Hunting!

Translated by