HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

On-line evaluation of mean and variance in C#

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
varianceevaluationlinemeanand

Problem

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
{
    /// 
    /// Holds a "ghost sample" into memory. It has a constant memory print
    /// and updates the size, the average and the variance of the sample.
    /// 
    public interface IGhostSample
    {
        void Add(T element);

        T Mean { get; }

        T Variance { get; }

        T StandardDev { get; }
    }
}


Which I specialized for doubles :

using System;

namespace DataStructures
{
    /// 
    /// Holds a "ghost sample" into memory. It has a constant memory print
    /// and updates the size, the average and the variance of the sample.
    /// 
    public class GhostSample : IGhostSample
    {
        #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
{
///
/// Holds a "ghost sample" into memory. It h

Solution

Unfortunately there is no interface that defines the operators but you could define your own data type and with a litte over-engineering you get his:

Base class for the argument:

public abstract class Argument
{
    protected Argument(T value)
    {
        Value = value;
    }

    public T Value { get; }

    public abstract Argument Add(Argument y);
    public abstract Argument Subtract(Argument y);
    public abstract Argument Multiply(Argument y);
    public abstract Argument Divide(Argument y);
    public abstract Argument Increment();
    public abstract Argument Sqrt();
}


Example double argument:

public class DoubleArgument : Argument
{
    public static DoubleArgument Default = new DoubleArgument(0d);  
    public DoubleArgument(double value) : base(value) { }
    public override Argument Add(Argument y) => (DoubleArgument)(Value + y.Value);
    public override Argument Subtract(Argument y) => (DoubleArgument)(Value - y.Value);
    public override Argument Multiply(Argument y) => (DoubleArgument)(Value * y.Value);
    public override Argument Divide(Argument y) => (DoubleArgument)(Value / y.Value);
    public override Argument Increment() => (DoubleArgument)(Value + 1.0);
    public override Argument Sqrt() => (DoubleArgument)(Math.Sqrt(Value));
    public static implicit operator DoubleArgument(double value) => new DoubleArgument(value);
}


Calculation:

public class GhostSample
{
    private Argument _mean;
    private Argument _variance;
    private Argument _size;

    public GhostSample(Argument size, Argument mean, Argument variance) 
    {
        _size = size;
        _mean = mean;
        _variance = variance;
    }

    public Argument Size => _size;
    public Argument Mean => _mean;
    public Argument Variance => _variance;
    public Argument StandardDev => _variance.Sqrt();    

    public void Add(Argument element)
    {
        _size = _size.Increment();        
        var previousMean = _mean;
        _mean = previousMean.Multiply(_size).Add(element).Divide(_size); 
        _variance = _size.Multiply(_variance).Add(element.Subtract(previousMean).Multiply(element.Subtract(_mean)).Divide(_size));
    }
}


Usage:

var doubleGhostSample = new GhostSample(DoubleArgument.Default, DoubleArgument.Default, DoubleArgument.Default);
doubleGhostSample.Add((DoubleArgument)2.0);
doubleGhostSample.Add((DoubleArgument)3.0);
doubleGhostSample.Add((DoubleArgument)8.0);


I didn't know how to get rid of the default-parameters :-(

Code Snippets

public abstract class Argument<T>
{
    protected Argument(T value)
    {
        Value = value;
    }

    public T Value { get; }

    public abstract Argument<T> Add(Argument<T> y);
    public abstract Argument<T> Subtract(Argument<T> y);
    public abstract Argument<T> Multiply(Argument<T> y);
    public abstract Argument<T> Divide(Argument<T> y);
    public abstract Argument<T> Increment();
    public abstract Argument<T> Sqrt();
}
public class DoubleArgument : Argument<double>
{
    public static DoubleArgument Default = new DoubleArgument(0d);  
    public DoubleArgument(double value) : base(value) { }
    public override Argument<double> Add(Argument<double> y) => (DoubleArgument)(Value + y.Value);
    public override Argument<double> Subtract(Argument<double> y) => (DoubleArgument)(Value - y.Value);
    public override Argument<double> Multiply(Argument<double> y) => (DoubleArgument)(Value * y.Value);
    public override Argument<double> Divide(Argument<double> y) => (DoubleArgument)(Value / y.Value);
    public override Argument<double> Increment() => (DoubleArgument)(Value + 1.0);
    public override Argument<double> Sqrt() => (DoubleArgument)(Math.Sqrt(Value));
    public static implicit operator DoubleArgument(double value) => new DoubleArgument(value);
}
public class GhostSample<T>
{
    private Argument<T> _mean;
    private Argument<T> _variance;
    private Argument<T> _size;

    public GhostSample(Argument<T> size, Argument<T> mean, Argument<T> variance) 
    {
        _size = size;
        _mean = mean;
        _variance = variance;
    }

    public Argument<T> Size => _size;
    public Argument<T> Mean => _mean;
    public Argument<T> Variance => _variance;
    public Argument<T> StandardDev => _variance.Sqrt();    

    public void Add(Argument<T> element)
    {
        _size = _size.Increment();        
        var previousMean = _mean;
        _mean = previousMean.Multiply(_size).Add(element).Divide(_size); 
        _variance = _size.Multiply(_variance).Add(element.Subtract(previousMean).Multiply(element.Subtract(_mean)).Divide(_size));
    }
}
var doubleGhostSample = new GhostSample<double>(DoubleArgument.Default, DoubleArgument.Default, DoubleArgument.Default);
doubleGhostSample.Add((DoubleArgument)2.0);
doubleGhostSample.Add((DoubleArgument)3.0);
doubleGhostSample.Add((DoubleArgument)8.0);

Context

StackExchange Code Review Q#132562, answer score: 3

Revisions (0)

No revisions yet.