Merge pull request #16436 from jagathkumarks/master
Calculating Moving averages
This commit is contained in:
commit
29b5206cf7
@ -13,4 +13,11 @@
|
|||||||
<version>1.0.0-SNAPSHOT</version>
|
<version>1.0.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
</project>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-math3</artifactId>
|
||||||
|
<version>${commons-math3.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
public class ExponentialMovingAverage {
|
||||||
|
|
||||||
|
private final double alpha;
|
||||||
|
private Double previousEMA;
|
||||||
|
|
||||||
|
public ExponentialMovingAverage(double alpha) {
|
||||||
|
if (alpha <= 0 || alpha > 1) {
|
||||||
|
throw new IllegalArgumentException("Alpha must be in the range (0, 1]");
|
||||||
|
}
|
||||||
|
this.alpha = alpha;
|
||||||
|
this.previousEMA = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double calculateEMA(double newValue) {
|
||||||
|
if (previousEMA == null) {
|
||||||
|
previousEMA = newValue;
|
||||||
|
} else {
|
||||||
|
previousEMA = alpha * newValue + (1 - alpha) * previousEMA;
|
||||||
|
}
|
||||||
|
return previousEMA;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
public class MovingAverageByCircularBuffer {
|
||||||
|
|
||||||
|
private final double[] buffer;
|
||||||
|
private int head;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
public MovingAverageByCircularBuffer(int windowSize) {
|
||||||
|
this.buffer = new double[windowSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(double value) {
|
||||||
|
buffer[head] = value;
|
||||||
|
head = (head + 1) % buffer.length;
|
||||||
|
if (count < buffer.length) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMovingAverage() {
|
||||||
|
if (count == 0) {
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
double sum = 0;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
sum += buffer[i];
|
||||||
|
}
|
||||||
|
return sum / count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
|
||||||
|
|
||||||
|
public class MovingAverageWithApacheCommonsMath {
|
||||||
|
|
||||||
|
private final DescriptiveStatistics stats;
|
||||||
|
|
||||||
|
public MovingAverageWithApacheCommonsMath(int windowSize) {
|
||||||
|
this.stats = new DescriptiveStatistics(windowSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(double value) {
|
||||||
|
stats.addValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMovingAverage() {
|
||||||
|
return stats.getMean();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
import java.util.stream.DoubleStream;
|
||||||
|
|
||||||
|
public class MovingAverageWithStreamBasedApproach {
|
||||||
|
private int windowSize;
|
||||||
|
|
||||||
|
public MovingAverageWithStreamBasedApproach(int windowSize) {
|
||||||
|
this.windowSize = windowSize;
|
||||||
|
}
|
||||||
|
public double calculateAverage(double[] data) {
|
||||||
|
return DoubleStream.of(data)
|
||||||
|
.skip(Math.max(0, data.length - windowSize))
|
||||||
|
.limit(Math.min(data.length, windowSize))
|
||||||
|
.summaryStatistics()
|
||||||
|
.getAverage();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ExponentialMovingAverageUnitTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void whenAlphaIsInvalid_shouldThrowException() {
|
||||||
|
new ExponentialMovingAverage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenFirstValueIsAdded_shouldHaveExponentialMovingAverageSameAsValue() {
|
||||||
|
ExponentialMovingAverage ema = new ExponentialMovingAverage(0.5);
|
||||||
|
assertEquals(10.0, ema.calculateEMA(10.0), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValuesAreAdded_shouldUpdateExponentialMovingAverageCorrectly() {
|
||||||
|
ExponentialMovingAverage ema = new ExponentialMovingAverage(0.4);
|
||||||
|
assertEquals(10.0, ema.calculateEMA(10.0), 0.001);
|
||||||
|
assertEquals(14.0, ema.calculateEMA(20.0), 0.001);
|
||||||
|
assertEquals(20.4, ema.calculateEMA(30.0), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenAlphaIsCloserToOne_exponentialMovingAverageShouldRespondFasterToChanges() {
|
||||||
|
ExponentialMovingAverage ema1 = new ExponentialMovingAverage(0.2);
|
||||||
|
ExponentialMovingAverage ema2 = new ExponentialMovingAverage(0.8);
|
||||||
|
|
||||||
|
assertEquals(10.0, ema1.calculateEMA(10.0), 0.001);
|
||||||
|
assertEquals(10.0, ema2.calculateEMA(10.0), 0.001);
|
||||||
|
|
||||||
|
assertEquals(12.0, ema1.calculateEMA(20.0), 0.001);
|
||||||
|
assertEquals(18.0, ema2.calculateEMA(20.0), 0.001);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MovingAverageByCircularBufferUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenInitialAverageIsCalculated_shouldReturnNAN() {
|
||||||
|
MovingAverageByCircularBuffer ma = new MovingAverageByCircularBuffer(5);
|
||||||
|
assertEquals(Double.NaN, ma.getMovingAverage(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValuesAreAdded_shouldUpdateAverageCorrectly() {
|
||||||
|
MovingAverageByCircularBuffer ma = new MovingAverageByCircularBuffer(3);
|
||||||
|
ma.add(10);
|
||||||
|
assertEquals(10.0, ma.getMovingAverage(), 0.001);
|
||||||
|
ma.add(20);
|
||||||
|
assertEquals(15.0, ma.getMovingAverage(), 0.001);
|
||||||
|
ma.add(30);
|
||||||
|
assertEquals(20.0, ma.getMovingAverage(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWindowSizeIsFull_shouldOverrideTheOldestValue() {
|
||||||
|
MovingAverageByCircularBuffer ma = new MovingAverageByCircularBuffer(2);
|
||||||
|
ma.add(10);
|
||||||
|
ma.add(20);
|
||||||
|
assertEquals(15.0, ma.getMovingAverage(), 0.001);
|
||||||
|
ma.add(30);
|
||||||
|
assertEquals(25.0, ma.getMovingAverage(), 0.001);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MovingAverageWithApacheCommonsMathUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenInitialAverageIsCalculated_shouldReturnNAN() {
|
||||||
|
MovingAverageWithApacheCommonsMath movingAverageCalculator = new MovingAverageWithApacheCommonsMath(5);
|
||||||
|
assertEquals(Double.NaN, movingAverageCalculator.getMovingAverage(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValuesAreAdded_shouldUpdateAverageCorrectly() {
|
||||||
|
MovingAverageWithApacheCommonsMath movingAverageCalculator = new MovingAverageWithApacheCommonsMath(3);
|
||||||
|
movingAverageCalculator.add(10);
|
||||||
|
assertEquals(10.0, movingAverageCalculator.getMovingAverage(), 0.001);
|
||||||
|
movingAverageCalculator.add(20);
|
||||||
|
assertEquals(15.0, movingAverageCalculator.getMovingAverage(), 0.001);
|
||||||
|
movingAverageCalculator.add(30);
|
||||||
|
assertEquals(20.0, movingAverageCalculator.getMovingAverage(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenWindowSizeIsFull_shouldDropTheOldestValue() {
|
||||||
|
MovingAverageWithApacheCommonsMath ma = new MovingAverageWithApacheCommonsMath(2);
|
||||||
|
ma.add(10);
|
||||||
|
ma.add(20);
|
||||||
|
assertEquals(15.0, ma.getMovingAverage(), 0.001);
|
||||||
|
ma.add(30);
|
||||||
|
assertEquals(25.0, ma.getMovingAverage(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.baeldung.algorithms.movingaverages;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class MovingAverageWithStreamBasedApproachUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenEmptyDataIsPassed_shouldReturnZero() {
|
||||||
|
double[] data = {};
|
||||||
|
int windowSize = 3;
|
||||||
|
double expectedAverage = 0;
|
||||||
|
MovingAverageWithStreamBasedApproach calculator = new MovingAverageWithStreamBasedApproach(windowSize);
|
||||||
|
double actualAverage = calculator.calculateAverage(data);
|
||||||
|
assertEquals(expectedAverage, actualAverage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValidDataIsPassed_shouldReturnCorrectAverage() {
|
||||||
|
double[] data = {10, 20, 30, 40, 50};
|
||||||
|
int windowSize = 3;
|
||||||
|
double expectedAverage = 40;
|
||||||
|
MovingAverageWithStreamBasedApproach calculator = new MovingAverageWithStreamBasedApproach(windowSize);
|
||||||
|
double actualAverage = calculator.calculateAverage(data);
|
||||||
|
assertEquals(expectedAverage, actualAverage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenValidDataIsPassedWithLongerWindowSize_shouldReturnCorrectAverage() {
|
||||||
|
double[] data = {10, 20, 30, 40, 50};
|
||||||
|
int windowSize = 5;
|
||||||
|
double expectedAverage = 30;
|
||||||
|
MovingAverageWithStreamBasedApproach calculator = new MovingAverageWithStreamBasedApproach(windowSize);
|
||||||
|
double actualAverage = calculator.calculateAverage(data);
|
||||||
|
assertEquals(expectedAverage, actualAverage);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user