Beeminder Forum

What is the "rosy" algorithm?


#1

I’m using it on a weight loss tracker and it seems more optimistic than the moving average. Curious what the actual computation is.


#2

Great question! First I’ll quote the handwavy description from the graph legend:

Optimistic Projection

An optimistic view of progress, i.e., a view of your data through rose-colored glasses. It shows your inexorable march towards your goal. If you’re bouncing around it just shows you flat. When you go down (or whichever direction your goal is), it shows you that right away.

It never shows you going up (or whatever the wrong direction is) unless it’s sure you’ve screwed up.

If you find the random fluctuation unnerving, you can focus on the pink dots day-to-day.

And here’s the algorithm:

First compute what we’ll call adjusted deltas between successive pairs of datapoints as follows:

Get the list of daily deltas between all the points, but adjust each delta by the road rate (eg, if the delta is equal to the delta of the road itself, that’s an adjusted delta of 0).

Let stdflux be the 90% quantile of those adjusted deltas. (Until we finish the Yellow Brick Half-Plane project, this is actually maxed with the lane width, lnw.)

And then, ok, I’m falling back to Python code at this point…

First some generic utility functions:

# clip(x, a,b) = min(b,max(a,x))
def clip(x, a,b):
  if a > b: a,b = b,a
  if x < a: x = a
  if x > b: x = b
  return x

# Helper for foldlist; this one returns a generator instead of a list
def foldlist0(f, x, l): 
  yield x
  for i in l: x = f(x,i); yield x

# foldlist(f,x, [e1, e2, ...]) -> [x, f(x,e1), f(f(x,e1), e2), ...]
def foldlist(f, x, l): return list(foldlist0(f, x, l))

Then this fancy “inertia” function:

# Start at the first data point plus sign*delta and walk forward making the next
# point be equal to the previous point, clipped by the next point plus or minus 
# delta. Used for the rose-colored dots.
def inertia0(x, d, sgn):
  return foldlist(lambda a, b: clip(a, b-d, b+d), x[0]+sgn*d, x[1:])
def inertia(dat, delt, sgn):  # data, delta, sign (-1 or +1)
  tdata = zip(*dat) # transpose of data
  tdata[1] = inertia0(tdata[1], delt, sgn)
  return zip(*tdata)
# Same thing but start at the last data point and walk backwards.
def inertiaRev(dat, dlt, sgn): return reversed(inertia(reversed(dat), dlt, sgn))

And finally the function to actually plot the rose-colored line:

# Plot the rosy progress line
def grRosy():
  delta = max(lnw, stdflux)
  if dir > 0:
    lo = inertia(   data, delta, -1)
    hi = inertiaRev(data, delta, +1)
  else:
    lo = inertiaRev(data, delta, -1)
    hi = inertia(   data, delta, +1)
  yveclo = [v for (t,v) in lo]
  yvechi = [v for (t,v) in hi]
  yvec = [(l+h)/2.0 for (l,h) in zip(yveclo, yvechi)]
  xvec = [t for (t,v) in data]
  pdxy(xvec, yvec, fmt='bo', marker='o', color=ROSE,
                   linestyle='-', markersize=dsz(2.7), markeredgecolor=ROSE,
                   markeredgewidth=0, linewidth=.8*scalf)