Is there a function in java to get moving average
I have a si开发者_StackOverflow社区tuation where I need to process 5000 samples from a device in every 0.5 sec.
Lets say the window size is 100, then there would be 50 points resulting from the moving average. I am trying with conventional method, i.e. with loops. But this is a very inefficient way to do it. Any suggestions ?
Check out the Apache Maths library. This has methods for doing precisely what you want. See DescriptiveStatistics and Mean for more info.
Here's one way.
public class Rolling {
private int size;
private double total = 0d;
private int index = 0;
private double samples[];
public Rolling(int size) {
this.size = size;
samples = new double[size];
for (int i = 0; i < size; i++) samples[i] = 0d;
}
public void add(double x) {
total -= samples[index];
samples[index] = x;
total += x;
if (++index == size) index = 0; // cheaper than modulus
}
public double getAverage() {
return total / size;
}
}
public class RollingTest extends TestCase {
private final static int SIZE = 5;
private static final double FULL_SUM = 12.5d;
private Rolling r;
public void setUp() {
r = new Rolling(SIZE);
}
public void testInitial() {
assertEquals(0d, r.getAverage());
}
public void testOne() {
r.add(3.5d);
assertEquals(3.5d / SIZE, r.getAverage());
}
public void testFillBuffer() {
fillBufferAndTest();
}
public void testForceOverWrite() {
fillBufferAndTest();
double newVal = SIZE + .5d;
r.add(newVal);
// get the 'full sum' from fillBufferAndTest(), add the value we just added,
// and subtract off the value we anticipate overwriting.
assertEquals((FULL_SUM + newVal - .5d) / SIZE, r.getAverage());
}
public void testManyValues() {
for (int i = 0; i < 1003; i++) r.add((double) i);
fillBufferAndTest();
}
private void fillBufferAndTest() {
// Don't write a zero value so we don't confuse an initialized
// buffer element with a data element.
for (int i = 0; i < SIZE; i++) r.add(i + .5d);
assertEquals(FULL_SUM / SIZE, r.getAverage());
}
}
You can do that in O(1): keep a queue of the last 50 entries. When you add an entry and the queue is shorter 50 elements, just update the total and the count. If it is longer than 50 elements, update the total and the count as well. Pseudocode:
add(double x) {
total += x;
addToQueue(x);
if (queueSize > 50) {
total -= removeLastFromQueue();
} else {
count++;
}
}
double getAverage() {
return total / count;
}
Here's a good implementation, using BigDecimal:
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.LinkedList;
import java.util.Queue;
public class MovingAverage {
private final Queue<BigDecimal> window = new LinkedList<BigDecimal>();
private final int period;
private BigDecimal sum = BigDecimal.ZERO;
public MovingAverage(int period) {
assert period > 0 : "Period must be a positive integer";
this.period = period;
}
public void add(BigDecimal num) {
sum = sum.add(num);
window.add(num);
if (window.size() > period) {
sum = sum.subtract(window.remove());
}
}
public BigDecimal getAverage() {
if (window.isEmpty()) return BigDecimal.ZERO; // technically the average is undefined
BigDecimal divisor = BigDecimal.valueOf(window.size());
return sum.divide(divisor, 2, RoundingMode.HALF_UP);
}
}
As far as I know, there is no such function (class) in Java. But you can make one by yourself. Here is a simple example (SMA-Simple Moving Average):
public class MovingAverage {
private int [] window;
private int n, insert;
private long sum;
public MovingAverage(int size) {
window = new int[size];
insert = 0;
sum = 0;
}
public double next(int val) {
if (n < window.length) n++;
sum -= window[insert];
sum += val;
window[insert] = val;
insert = (insert + 1) % window.length;
return (double)sum / n;
}
}
Java 8 has added java.util.IntSummaryStatistics
. There similar classes for Double
and Long
as well. Fairly straightforward to use:
IntSummaryStatistics stats = new IntSummaryStatistics();
stats.accept(1);
stats.accept(3);
stats.getAverage(); // Returns 2.0
static int[] myIntArray = new int[16];
public static double maf(double number)
{
double avgnumber=0;
for(int i=0; i<15; i++)
{
myIntArray[i] = myIntArray[i+1];
}
myIntArray[15]= (int) number;
/* Doing Average */
for(int i=0; i<16; i++)
{
avgnumber=avgnumber+ myIntArray[i];
}
return avgnumber/16;
}
this algorithm can also be called as Moving Average Filter which is working well for me ... i implemented this algo in my graph project!
A solution without cumulative floating point errors.
/**
* Voortschrijdend gemiddelde
* @author Eelco de Lang
*/
public class MovingAverage
{
private final double[] valuesWindow;
private int fill;// = 0;
public MovingAverage(int windowSize)
{
valuesWindow = new double[windowSize];
assert windowSize > 0 : "Window size must be a positive integer";
}
public void add(long number)
{
add((double) number);
}
public void add(double number)
{
// Shift all values up 1 position
System.arraycopy(valuesWindow, 0, valuesWindow, 1, valuesWindow.length - 1);
valuesWindow[0] = number;
if (fill < valuesWindow.length) {
fill++;
}
}
public double getAverage()
{
double result = 0;
for (int i = 0; i < fill; i++) {
result += valuesWindow[i];
}
if (fill == 0) {
// Fine in most cases
return 0;
}
else {
return result / fill;
}
}
}
public class MovingAverageTest
{
@Test
public void test()
{
MovingAverage movingAverage = new MovingAverage(2);
Assert.assertEquals(0, movingAverage.getAverage(), 0.00001);
movingAverage.add(0.5);
Assert.assertEquals(0.5, movingAverage.getAverage(), 0.00001);
movingAverage.add(1.5);
Assert.assertEquals(1.0, movingAverage.getAverage(), 0.00001);
movingAverage.add(1.5);
Assert.assertEquals(1.5, movingAverage.getAverage(), 0.00001);
}
@Test
public void test2()
{
MovingAverage movingAverage = new MovingAverage(3);
Assert.assertEquals(0, movingAverage.getAverage(), 0.00001);
movingAverage.add(123);
Assert.assertEquals(123, movingAverage.getAverage(), 0.00001);
movingAverage.add(123);
Assert.assertEquals(123, movingAverage.getAverage(), 0.00001);
movingAverage.add(123);
Assert.assertEquals(123, movingAverage.getAverage(), 0.00001);
movingAverage.add(123);
Assert.assertEquals(123, movingAverage.getAverage(), 0.00001);
movingAverage.add(123);
Assert.assertEquals(123, movingAverage.getAverage(), 0.00001);
}
}
精彩评论