Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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?

share|improve this question
1  
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
    
@acl I added your advice. –  John Nov 1 '12 at 21:47
    
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
show 3 more comments

3 Answers

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}

share|improve this answer
    
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
1  
@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
add comment

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}

share|improve this answer
add comment

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].

share|improve this answer
    
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 :) –  rm -rf Nov 1 '12 at 23:03
1  
UnitStep doesn't have that problem –  Rojo 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. –  rm -rf Nov 2 '12 at 3:00
1  
Also I think this approach would only work for data whose accumulation doesn't go negative after getting positive for the first time, right? –  Rojo Nov 2 '12 at 3:24
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.