How to calculate conditional sum of a part of vector/array based on a 2nd one

11 views (last 30 days)
Hello,
I want to get the conditional sum of part of a vector based on a second vector.
For example:
A = [143 14 -4 299 -29 83 151 190 178 -61 -109 ]
and
B = [1 1 -1 1 -1 1 1 1 1 -1 -1 ]
then I want to get the output:
C=[0 157 -4 299 -29 0 0 0 602 -170 ]
and
D=[0 2 1 1 1 0 0 0 4 0 2 ]
The idea is, when successive B's = 1, I add the corresponding values from A and store in C. Similarly if successive B's = -1, I add the values up and store in C. If more than 1 value in A is added, I fill the gaps in C with 0's. It would also be great if I could get a seperate array D mentioning how many values in A have been added to produce the value in C!
Please help! Thanks a lot!
  1 Comment
Cedric
Cedric on 30 Apr 2013
How large are these vectors? There is a vector way to do it (requires quite a few operations) and a way based on a simple FOR loop. The vector way is likely to be very efficiency on large vectors/arrays, and the FOR loop is likely to be more efficient on small vectors/arrays.

Sign in to comment.

Accepted Answer

Teja Muppirala
Teja Muppirala on 30 Apr 2013
Just for comparison, here is a FOR loop solution. FOR loops can actually be very fast, even for large arrays, especially when you are doing simple operations like this.
C = zeros(size(A));
D = zeros(size(A));
previous = B(1);
runningSum = A(1);
numValues = 1;
for n = 2:numel(B)
if B(n) == previous
runningSum = runningSum + A(n);
numValues = numValues+1;
else
C(n-1) = runningSum;
D(n-1) = numValues;
runningSum = A(n);
numValues = 1;
previous = B(n);
end
end
C(end) = runningSum;
D(end) = numValues;

More Answers (1)

Cedric
Cedric on 30 Apr 2013
Edited: Cedric on 30 Apr 2013
The following is an example of "vector approach":
dif = [true, diff(B)~=0] ;
blockId = cumsum(dif) ;
blockStart = find(dif) ;
blockEnd = [blockStart(2:end)-1, numel(A)] ;
blockSum = accumarray(blockId(:), A(:)) ;
C = zeros(size(A)) ;
C(blockEnd) = blockSum ;
blockSize = accumarray(blockId(:), ones(size(A))) ;
D = zeros(size(A)) ;
D(blockEnd) = blockSize ;
Running this produces:
>> C
C =
0 157 -4 299 -29 0 0 0 602 0 -170
>> D
D =
0 2 1 1 1 0 0 0 4 0 2
As you can see, a simpler FOR loop is likely to be more efficient for small array sizes, but this vector approach certainly wins if A and B are large.
I don't have time to check it extensively though, and I leave that to you. Check well boundary cases in particular.
Note that you can probably simplify the first part by computing directly blockEnd without defining blockStart, but I leave it this way for sake of clarity (or more honestly by lack of time ;-)).

Community Treasure Hunt

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

Start Hunting!