Effacer les filtres
Effacer les filtres

how to use accumarray over a matrix avoiding a loop?

43 vues (au cours des 30 derniers jours)
Fernando
Fernando le 12 Fév 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,

Réponse acceptée

Sean de Wolski
Sean de Wolski le 12 Fév 2013
Modifié(e) : Sean de Wolski le 12 Fév 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 commentaire
Baris Gecer
Baris Gecer le 24 Fév 2017
Modifié(e) : Baris Gecer le 24 Fév 2017
This helped me a lot.Thanks! The first code was quite fast in my case where the matrix is 4096*128.

Connectez-vous pour commenter.

Plus de réponses (3)

Azzi Abdelmalek
Azzi Abdelmalek le 12 Fév 2013
C=cell2mat(arrayfun(@(x) accumarray(A,B(:,x)),1:size(B,2),'un',0));

Fernando
Fernando le 12 Fév 2013
Thanks for your help. I will check which of the three alternatives is better for my data.

Jasper van Casteren
Jasper van Casteren le 8 Fév 2022
Modifié(e) : Jasper van Casteren le 8 Fév 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.

Catégories

En savoir plus sur Resizing and Reshaping Matrices dans Help Center et File Exchange

Community Treasure Hunt

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

Start Hunting!

Translated by