/*
* Copyright 2009-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.streaminer.stream.avg;
/**
* Cumulative statistics for a series of real numbers with higher weight given to recent data but without storing any
* history. Clients call {@link #append(double)} every time there is a new measurement, and then can collect summary
* statistics from the convenience getters (e.g. {@link #getStatistics()}). Older values are given exponentially smaller
* weight, with a decay factor determined by a "window" size chosen by the caller. The result is a good approximation to
* the statistics of the series but with more weight given to recent measurements, so if the statistics change over time
* those trends can be approximately reflected.
*
* @author Dave Syer
*
*/
public class ExponentialMovingAverage implements IAverage {
private volatile int count;
private volatile double weight;
private volatile double sum;
private volatile double sumSquares;
private volatile double min;
private volatile double max;
private final double decay;
/**
* Create a moving average accumulator with decay lapse window provided. Measurements older than this will have
* smaller weight than <code>1/e</code>.
*
* @param window the exponential lapse window (number of measurements)
*/
public ExponentialMovingAverage(int window) {
this.decay = 1 - 1. / window;
}
public synchronized void clear() {
weight = 0;
sum = 0;
sumSquares = 0;
count = 0;
min = 0;
max = 0;
}
/**
* Add a new measurement to the series.
*
* @param value the measurement to append
*/
public synchronized void add(double value) {
if (value > max || count == 0)
max = value;
if (value < min || count == 0)
min = value;
sum = decay * sum + value;
sumSquares = decay * sumSquares + value * value;
weight = decay * weight + 1;
count++;
}
/**
* @return the number of measurements recorded
*/
public int getCount() {
return count;
}
/**
* @return the mean value
*/
public double getAverage() {
return weight > 0 ? sum / weight : 0.;
}
/**
* @return the approximate standard deviation
*/
public double getStandardDeviation() {
double mean = getAverage();
double var = weight > 0 ? sumSquares / weight - mean * mean : 0.;
return var > 0 ? Math.sqrt(var) : 0;
}
/**
* @return the maximum value recorded (not weighted)
*/
public double getMax() {
return max;
}
/**
* @return the minimum value recorded (not weighted)
*/
public double getMin() {
return min;
}
}