Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum up vector values till threshold, then start again

I have a vector a = [1 3 4 2 1 5 6 3 2]. Now I want to create a new vector 'b' with the cumsum of a, but after reaching a threshold, let's say 5, cumsum should reset and start again till it reaches the threshold again, so the new vector should look like this:

b = [1 4 4 2 3 5 6 3 5]

Any ideas?

like image 793
Toxicsmile Avatar asked Dec 11 '25 04:12

Toxicsmile


2 Answers

You could build a sparse matrix that, when multiplied by the original vector, returns the cumulative sums. I haven't timed this solution versus others, but I strongly suspect this will be the fastest for large arrays of a.

% Original data
a = [1 3 4 2 1 5 6 3 2];
% Threshold
th = 5;
% Cumulative sum corrected by threshold
b = cumsum(a)/th;
% Group indices to be summed by checking for equality,
% rounded down, between each cumsum value and its next value. We add one to
% prevent NaNs from occuring in the next step. 
c = cumsum(floor(b) ~= floor([0,b(1:end-1)]))+1;
% Build the sparse matrix, remove all values that are in the upper
% triangle.
S = tril(sparse(c.'./c == 1));
% In case you use matlab 2016a or older:
% S = tril(sparse(bsxfun(@rdivide,c.',c) == 1));
% Matrix multiplication to create o.
o = S*a.';
like image 119
Poelie Avatar answered Dec 13 '25 23:12

Poelie


By normalizing the arguments of cumsum with the threshold and flooring you can get grouping indizes for accumarray, which then can do the cumsumming groupwise:

t = 5;
a = [1 3 4 2 1 5 6 3 2];

%// cumulative sum of normalized vector a 
n = cumsum(a/t);
%// subs for accumarray
subs = floor( n ) + 1;
%// cumsum of every group
aout = accumarray( subs(:), (1:numel(subs)).', [], @(x) {cumsum(a(x))});
%// gather results;
b = [aout{:}]
like image 39
Robert Seifert Avatar answered Dec 13 '25 23:12

Robert Seifert



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!