# Decayed Value (aka. moving average)

A `DecayedValue` can be approximately computed into a moving average. Please see an explanation of different averages here: The Decaying Average.

@johnynek mentioned that:

a DecayedValue is approximately like a moving average value with window size of the half-life. It is EXACTLY a sample of the Laplace transform of the signal of values. Therefore, if we normalize a decayed value with halflife/ln(2), which is the integral of exp(-t(ln(2))/halflife) from 0 to infinity. We get a moving average of the window size equal to halflife.

Here is the code example for computing a `DecayedValue` average:

``````import com.twitter.algebird._

val data = {
val rnd = new scala.util.Random
(1 to 10).map { _ => rnd.nextInt(1000).toDouble }.toSeq
}
// data: scala.collection.immutable.Seq[Double] = Vector(540.0, 447.0, 4.0, 480.0, 155.0, 44.0, 582.0, 139.0, 270.0, 841.0)

val HalfLife = 5.0
// HalfLife: Double = 5.0

val normalization = HalfLife / math.log(2)
// normalization: Double = 7.213475204444817

implicit val dvMonoid = DecayedValue.monoidWithEpsilon(1e-3)

data.zipWithIndex.scanLeft(Monoid.zero[DecayedValue]) { (previous, data) =>
val (value, time) = data
val decayed = Monoid.plus(previous, DecayedValue.build(value, time, HalfLife))
println("At %d: decayed=%f".format(time, (decayed.value / normalization)))
decayed
}
// At 0: decayed=74.859896
// At 1: decayed=127.136682
// At 2: decayed=111.233428
// At 3: decayed=163.376453
// At 4: decayed=163.715026
// At 5: decayed=148.621903
// At 6: decayed=210.065213
// At 7: decayed=202.141881
// At 8: decayed=213.404676
// At 9: decayed=302.366917
// res0: scala.collection.immutable.Seq[com.twitter.algebird.DecayedValue] = Vector(DecayedValue(0.0,-Infinity), DecayedValue(540.0,0.0), DecayedValue(917.097304179907,0.13862943611198905), DecayedValue(802.3795747511749,0.2772588722239781), DecayedValue(1178.51199077694,0.4158883083359671), DecayedValue(1180.9542774221018,0.5545177444479562), DecayedValue(1072.080411436778,0.6931471805599453), DecayedValue(1515.3002060750277,0.8317766166719343), DecayedValue(1458.1454479613483,0.9704060527839233), DecayedValue(1539.389341090431,1.1090354888959124), DecayedValue(2181.116258018324,1.2476649250079015))
``````

Running the above code in comparison with a simple decayed average:

``````data.zipWithIndex.scanLeft(0.0) { (previous, data) =>
val (value, time) = data
val avg = (value + previous * (HalfLife - 1.0)) / HalfLife
println("At %d: windowed=%f".format(time, avg))
avg
}
// At 0: windowed=108.000000
// At 1: windowed=175.800000
// At 2: windowed=141.440000
// At 3: windowed=209.152000
// At 4: windowed=198.321600
// At 5: windowed=167.457280
// At 6: windowed=250.365824
// At 7: windowed=228.092659
// At 8: windowed=236.474127
// At 9: windowed=357.379302
// res1: scala.collection.immutable.Seq[Double] = Vector(0.0, 108.0, 175.8, 141.44, 209.152, 198.3216, 167.45728, 250.36582399999998, 228.09265919999999, 236.47412735999995, 357.379301888)
``````

You’ll see that the averages are pretty close to each other.

## DecayedValue FAQ

### Is a DecayedValue average better than a moving average?

DecayedValue gives an exponential moving average. A standard windowed moving average of `N` points requires `O(N)` memory. An exponential moving average takes `O(1)` memory. That’s the win. Usually one, or a few, exponential moving averages gives you what you need cheaper than the naive approach of keeping 100 or 1000 recent points.

### Is a DecayedValue average better than a simple decayed average?

A simple decayed average looks like this:

``````val avg = (value + previousAvg * (HaflLife - 1.0)) / HalfLife
``````

In a way, a `DecayedValue` average is a simple decayed average with different scaling factor.