What is the fastest library/algorithm for calculating simple moving average? I wrote my own, but it takes too long on 330 000 items decimal dataset.
public class MovingAverage
{
private Queue<Decimal> samples = new Queue<Decimal>();
private int windowSize = 16;
private Decimal sampleAccumulator;
public Decimal Average { get; private set; }
/// <summary>
/// Computes a new windowed average each time a new sample arrives
/// </summary>
/// <param name="newSample"></param>
public void ComputeAverage(Decimal newSample)
{
sampleAccumulator += newSample;
samples.Enqueue(newSample);
if (samples.Count > windowSize)
{
sampleAccumulator -= samples.Dequeue();
}
Average = sampleAccumulator / samples.Count;
}
}
My MovingAverage
class implementation is:
windowSize
that is power of twoHere is the class:
using System;
using System.Linq;
using System.Threading;
public class MovingAverage
{
private readonly int _mask;
private readonly double?[] _values;
private int _nextIndex = -1;
public MovingAverage(int windowSize)
{
_mask = windowSize - 1;
if (windowSize == 0 || (windowSize & _mask) != 0)
{
throw new ArgumentException("Must be power of two", nameof(windowSize));
}
_values = new double?[windowSize];
}
public void Add(double newValue)
{
var index = Interlocked.Increment(ref _nextIndex) & _mask;
_values[index] = newValue;
}
public double ComputeAverage()
{
return _values.TakeWhile(x => x.HasValue)
.Select(x => x ?? 0)
.DefaultIfEmpty(0)
.Average();
}
}
here is the NUnit test
using NUnit.Framework;
public class MovingAverageTest
{
[Test]
public void Should_compute_average()
{
var sut = new MovingAverage(4);
Assert.That(sut.ComputeAverage(), Is.EqualTo(0));
sut.Add(2);
Assert.That(sut.ComputeAverage(), Is.EqualTo(2));
sut.Add(4);
Assert.That(sut.ComputeAverage(), Is.EqualTo(3));
sut.Add(0);
Assert.That(sut.ComputeAverage(), Is.EqualTo(2));
sut.Add(6);
Assert.That(sut.ComputeAverage(), Is.EqualTo(3));
sut.Add(6);
Assert.That(sut.ComputeAverage(), Is.EqualTo(4));
sut.Add(0);
sut.Add(0);
sut.Add(0);
sut.Add(0);
Assert.That(sut.ComputeAverage(), Is.EqualTo(0));
sut.Add(10);
sut.Add(10);
sut.Add(10);
sut.Add(10);
Assert.That(sut.ComputeAverage(), Is.EqualTo(10));
}
[Test]
public void Should_check_windowsize_param()
{
Assert.That(() => new MovingAverage(3), Throws.ArgumentException);
}
}