how to use accumarray over a matrix avoiding a loop?

33 visualizaciones (últimos 30 días)
Fernando
Fernando el 12 de Feb. de 2013
Editada: Jasper van Casteren el 8 de Feb. de 2022
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,

Respuesta aceptada

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

Iniciar sesión para comentar.

Más respuestas (3)

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

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

Jasper van Casteren
Jasper van Casteren el 8 de Feb. de 2022
Editada: Jasper van Casteren el 8 de Feb. de 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.

Categorías

Más información sobre Resizing and Reshaping Matrices en Help Center y File Exchange.

Community Treasure Hunt

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

Start Hunting!

Translated by