// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.stats;
import java.util.concurrent.LinkedBlockingDeque;
import com.google.common.base.Preconditions;
/**
* Function to compute the moving average of a time series.
*
* @author William Farner
*/
public class MovingAverage<T extends Number> extends SampledStat<Double> {
private static final int DEFAULT_WINDOW = 10;
private final Stat<T> input;
private final LinkedBlockingDeque<T> samples;
private double sampleSum = 0;
private MovingAverage(String name, Stat<T> input, int windowSize) {
super(name, 0d);
Preconditions.checkArgument(windowSize > 1);
this.input = Preconditions.checkNotNull(input);
this.samples = new LinkedBlockingDeque<T>(windowSize);
Stats.export(input);
}
public static <T extends Number> MovingAverage<T> of(Stat<T> input) {
return MovingAverage.of(input, DEFAULT_WINDOW);
}
public static <T extends Number> MovingAverage<T> of(Stat<T> input, int windowSize) {
return MovingAverage.of(String.format("%s_avg", input.getName()), input, windowSize);
}
public static <T extends Number> MovingAverage<T> of(String name, Stat<T> input,
int windowSize) {
return new MovingAverage<T>(name, input, windowSize);
}
@Override
public Double doSample() {
T sample = input.read();
if (samples.remainingCapacity() == 0) {
sampleSum -= samples.removeLast().doubleValue();
}
samples.addFirst(sample);
sampleSum += sample.doubleValue();
return sampleSum / samples.size();
}
}