Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vectorise foor loop with a variable that is incremented in each iteration

I am trying to optimise the running time of my code by getting rid of some for loops. However, I have a variable that is incremented in each iteration in which sometimes the index is repeated. I provide here a minimal example:

a = [1 4 2 2 1 3 4 2 3 1]
b = [0.5 0.2 0.3 0.4 0.1 0.05 0.7 0.3 0.55 0.8]
c = [3 5 7 9]

for i = 1:10
    c(a(i)) = c(a(i)) + b(i)
end

Ideally, I would like to compute it by writting:

c(a) = c(a) + b 

but obviously it would not give me the same results since I have to recalculate the value for the same index several times so this way to vectorise it would not work. Also, I am working in Matlab or Octave in case that this is important.

Thank you very much for any help, I am not sure that it is possible to be vectorise.


Edit: thank you very much for your answers so far. I have discovered accumarray, which I did not know before and also understood why changing the for loop between Matlab and Octave was giving me such different times. I also understood my problem better. I gave a too simple example which I thought I could extend, however, what if b was a matrix?

(Let's forget about c at the moment):

a = [1 4 2 2 1 3 4 2 3 1]
b =[0.69  -0.41  -0.13  -0.13  -0.42  -0.14  -0.23  -0.17   0.22  -0.24;
   0.34  -0.39  -0.36   0.68  -0.66  -0.19  -0.58   0.78  -0.23   0.25;
  -0.68  -0.54   0.76  -0.58   0.24  -0.23  -0.44   0.09   0.69  -0.41;
   0.11  -0.14   0.32   0.65   0.26   0.82   0.32   0.29  -0.21  -0.13;
  -0.94  -0.15  -0.41  -0.56   0.15   0.09   0.38   0.58   0.72   0.45;
   0.22  -0.59  -0.11  -0.17   0.52   0.13  -0.51   0.28   0.15   0.19;
   0.18  -0.15   0.38  -0.29  -0.87   0.14  -0.13   0.23  -0.92  -0.21;
   0.79  -0.35   0.45  -0.28  -0.13   0.95  -0.45   0.35  -0.25  -0.61;
  -0.42   0.76   0.15   0.99  -0.84  -0.03   0.27   0.09   0.57   0.64;
   0.59   0.82  -0.39   0.13  -0.15  -0.71  -0.84  -0.43   0.93  -0.74]

I understood now that what I would be doing is rowSum per group, and given that I am using Octave I cannot use "splitapply". I tried to generalise your answers, but accumarray would not work for matrices and also I could not generalise @rahnema1 solution. The desired output would be:

[0.34   0.26  -0.93  -0.56  -0.42  -0.76  -0.69  -0.02   1.87  -0.53; 
 0.22  -1.03   1.53  -0.21   0.37   1.54  -0.57   0.73   0.23  -1.15;
-0.20   0.17   0.04   0.82  -0.32   0.10  -0.24   0.37   0.72   0.83;
 0.52  -0.54   0.02   0.39  -1.53  -0.05  -0.71   1.01  -1.15   0.04]

that is "equivalent" to

[sum(b([1 5 10],:))
 sum(b([3 4 8],:))
 sum(b([6 9],:))
 sum(b([2 7],:))]

Thank you very much, If you think I should include this in another question instead of adding the edit I will do so.

like image 822
Aurora González Vidal Avatar asked Aug 08 '20 14:08

Aurora González Vidal


2 Answers

Original question

It can be done with accumarray:

a = [1 4 2 2 1 3 4 2 3 1];
b = [0.5 0.2 0.3 0.4 0.1 0.05 0.7 0.3 0.55 0.8];
c = [3 5 7 9];
c(:) = c(:) + accumarray(a(:), b(:));

This sums the values from b in groups defined by a, and adds that to the original c.

Edited question

If b is a matrix, you can use

full(sparse(repmat(a, 1, size(b,1)), repelem(1:size(b,2), size(b,1)), b))

or

accumarray([repmat(a, 1, size(b,1)).' repelem(1:size(b,2), size(b,1)).'], b(:))
like image 100
Luis Mendo Avatar answered Nov 08 '22 17:11

Luis Mendo


Matrix multiplication and implicit expansion and can be used (Octave):

nc = numel(c);
c += b * (1:nc == a.');

For input of large size it may be more memory efficient to use sparse matrix:

nc = numel(c);
nb = numel(b);
c += b * sparse(1:nb, a, 1, nb, nc);

Edit: When b is a matrix you can extend this solution as:

nc = numel(c);
na = numel(a);
out = sparse(a, 1:na, 1, nc, na) * b;
like image 42
rahnema1 Avatar answered Nov 08 '22 15:11

rahnema1