# Restricted accumulation of values

Please consider the following list data. I was trying to accumulate data until the result turns positive the first time and finally found an approach. Maybe it's useful to others and maybe there are other (more efficient) approaches:

data={-1050, 50, 1001, 1950, 1950, 1950, 525, 0};


The same what Accumulate does can be done by FoldList:

Rest@FoldList[Plus, 0, data] == Rest@FoldList[#1 + #2 &, 0, data] == Accumulate@data
(*True*)


Now, one can add constraints to FoldList. For my problem I had to write:

Rest[FoldList[If[#1 + #2 < #2, #1 + #2, #2] &, 0, #]]&@data
(*{-1050, -1000, 1, 1950, 1950, 1950, 525, 0}*)


Question

How can this be improved, either in terms of speed or elegance?

-
I'd suggest you add "How can this be improved, either in terms of speed or elegance" or something like that at the end. Then it's a question! – acl Nov 1 '12 at 21:44
At the very least, you should have started with simplifying the two-argument function: If[#1 < 0, #1, 0] + #2 &. The versions using Boole[] or UnitStep[] seemed a bit slower in my tests; someone might want to do more testing on those. – J. M. Nov 2 '12 at 1:29
Do you want your output to stop at the point the accumulation goes positive? The answers provide some examples that do this and some that don't. Might be useful to clarify. You could use something like LengthWhile or TakeWhile to obtain that output from the full list. – Mike Honeychurch Nov 2 '12 at 6:50
@MikeHoneychurch The code-line with FoldList is only there to describe how I got to the last code-line. The most crucial point to me was to formulate an alternative to Accumulate with the addition of implementing conditions. – John Nov 2 '12 at 15:06

Perhaps if you like recursion this might seem more elegant, it certainly doesn't win on speed.

Recursive

NestWhile[{First@#~Plus~#[[2, 1]], Rest@#[[2]]} &, {0, data},
NonPositive@First@# && Length@#[[2]] >= 1 &]


{1, {1950, 1950, 1950, 525, 0}}

Timing:

data = RandomInteger[{-1000, 10}, 100000];

AbsoluteTiming[
NestWhile[{First@#~Plus~#[[2, 1]], Rest@#[[2]]} &, {0, data},
NonPositive@First@# && Length@#[[2]] >= 1 &];]


{1.005087, Null}

rm -rf

AbsoluteTiming[
With[{acc=Accumulate@data},acc[[;; -#]]~Join~data[[-# + 1 ;;]] &@Count[acc,_?Positive]];]


{0.024329, Null}

Rojo

AbsoluteTiming[
Join[#, data[[Length@# + 1 ;;]]] &@
Reap[Fold[If[NonPositive@#1, Sow[#1 + #2], Break[Null, Fold]] &, 0,
data]][[-1, 1]];]


{0.104658, Null}

-
The second method is very fast but is correct only assuming the cumulative sums remain positive forever after they first turn positive. – whuber Nov 29 '12 at 21:36
@whuber Yes indeed, thank you for noting that :) The second method, I think, came from an earlier version of the answer from rm -rf which has now been updated in light of comments from Simon Woods which I'd missed. I'll update my answer accordingly. – image_doctor Nov 30 '12 at 9:56

You could also do

Join[#, data[[Length@# + 1 ;;]]] &@
Reap[Fold[If[NonPositive@#1, Sow[#1 + #2], Break[Null, Fold]] &, 0,
data]][[-1, 1]]


{-1050, -1000, 1, 1950, 1950, 1950, 525, 0}

-

Here is another way:

With[{acc = Accumulate@data}, acc[[;; -#]] ~Join~ data[[-# + 1 ;;]] &@Count[acc, _?Positive]]
(* {-1050, -1000, 1, 1950, 1950, 1950, 525, 0} *)


You can also use Tr@Boole@Thread[acc > 0]] instead of Count[...] or if you want ≥0, then you can use Tr@UnitStep[acc].

-
HeavisideTheta[0] is undefined, so this has problems if the total reaches zero. – Simon Woods Nov 1 '12 at 22:33
@SimonWoods Good point. Fixed now :) – R. M. Nov 1 '12 at 23:03
UnitStep doesn't have that problem – Rojolalalalalalalalalalalalala Nov 2 '12 at 0:28
@Rojo Yep, I mentioned it in my answer, but note that UnitStep is equivalent to ≥0, whereas OP only mentioned >0. – R. M. Nov 2 '12 at 3:00
Also I think this approach would only work for data whose accumulation doesn't go negative after getting positive for the first time, right? – Rojolalalalalalalalalalalalala Nov 2 '12 at 3:24