In various projects, I have to evaluate the mean and/or the variance of relatively large samples.
I wrote the following, to help me evaluate these quantities with a constant footprint. Basically, it adds elements one per one and updates the variables.
namespace DataStructures
{
/// <summary>
/// Holds a "ghost sample" into memory. It has a constant memory print
/// and updates the size, the average and the variance of the sample.
/// </summary>
public interface IGhostSample<T>
{
void Add(T element);
T Mean { get; }
T Variance { get; }
T StandardDev { get; }
}
}
Which I specialized for doubles :
using System;
namespace DataStructures
{
/// <summary>
/// Holds a "ghost sample" into memory. It has a constant memory print
/// and updates the size, the average and the variance of the sample.
/// </summary>
public class GhostSample : IGhostSample<double>
{
#region Private Attributes
private double _mean = 0;
private double _variance = 0;
private int _size = 0;
#endregion
#region Accessors
public int Size
{
get { return _size; }
}
public double Mean
{
get { return _mean; }
}
public double Variance
{
get { return _variance; }
}
public double StandardDev
{
get { return Math.Sqrt(_variance); }
}
#endregion
#region Methods
public void Add(double element)
{
double previousMean = _mean;
_mean = (previousMean * _size + element) / (_size + 1);
_variance = (_size * _variance + (element - previousMean) * (element - _mean)) / (_size + 1);
_size++;
}
#endregion
}
}
And Math.Net Vectors :
using MathNet.Numerics.LinearAlgebra.Double;
using System;
namespace DataStructures
{
/// <summary>
/// Holds a "ghost sample" into memory. It has a constant memory print
/// and updates the size, the average and the variance of the sample.
/// </summary>
public class VectorGhostSample : IGhostSample<DenseVector>
{
#region Private attributes
private int _size = 0;
private int _length;
private DenseVector _mean;
private DenseVector _variance;
#endregion
#region Constructor
/// <summary>
/// Builds a new ghost sample.
/// </summary>
/// <param name="length">The length of the vectors.</param>
public VectorGhostSample(int length)
{
_length = length;
_mean = new DenseVector(_length);
_variance = new DenseVector(_length);
}
#endregion
#region Accessors
/// <summary>
/// The number of elements of the sample.
/// </summary>
public int Size
{
get { return _size; }
}
/// <summary>
/// The (element-wise) mean of the sample
/// </summary>
public DenseVector Mean
{
get { return _mean; }
}
/// <summary>
/// The (element-wise) variance of the sample
/// </summary>
public DenseVector Variance
{
get { return _variance; }
}
/// <summary>
/// The (element-wise) standard deviation of the sample
/// </summary>
public DenseVector StandardDev
{
get
{
DenseVector std = new DenseVector(_variance.Count);
for (int i = 0; i < _variance.Count; i++)
std[i] = Math.Sqrt(_variance[i]);
return std;
}
}
#endregion
#region Methods
/// <summary>
/// Adds an element to the ghost sample (i.e. updates the mean and variance of the sample).
/// </summary>
/// <param name="element">The element to add to the sample</param>
public void Add(DenseVector element)
{
DenseVector previousMean = _mean;
_mean = (previousMean * _size + element) / (_size + 1);
_variance = (DenseVector)(_size * _variance + (element - previousMean).PointwiseMultiply(element - _mean)).Divide(_size + 1);
_size++;
}
#endregion
}
}
I could not find a shorter way to do this. But vector and double behave in the same fashion, I only need addition and multiplication by a scalar to be able to perform these operations. Did I miss an obvious shortcut ?