Skip to content

Extension method to compute standard deviation of IEnumerable<TimeSpan>

by Alex Peck on June 22nd, 2011

Recently, I have been writing some quick and dirty performance tests. I repeat each test scenario a few times and take an average of the execution time. In order to verify that I have a reasonably stable measurement, I wanted to also display the coefficient of variation, which is defined as the ratio of the standard deviation to the mean.

Given a set of TimeSpan values, I want to get the standard deviation as a TimeSpan. I implemented this based on the extension method I found here on StackOverflow. I implemented both sample (with Bessel’s correction) and population standard deviation.

public static class StandardDeviationExtensions
{
    public static double SampleStandardDeviation(this IEnumerable<double> values)
    {
        int count = values.Count();
 
        if (count > 1)
        {
            double average = values.Average();
            double sumOfSquaredDifferences = values.Sum(v => Math.Pow(v - average, 2));
            return Math.Sqrt(sumOfSquaredDifferences / (count - 1));
        }
 
        return 0.0;
    }
 
    public static double PopulationStandardDeviation(this IEnumerable<double> values)
    {
        if (values.Count() > 1)
        {
            double average = values.Average();
            return Math.Sqrt(values.Average(v => Math.Pow(v - average, 2)));
        }
 
        return 0.0;
    }
 
    public static TimeSpan SampleStandardDeviation(this IEnumerable<TimeSpan> values)
    {
        return new TimeSpan(
            (long)values
                .Select(v => (double)v.Ticks)
                .SampleStandardDeviation());
    }
 
    public static TimeSpan PopulationStandardDeviation(this IEnumerable<TimeSpan> values)
    {
        return new TimeSpan(
            (long)values
                .Select(v => (double)v.Ticks)
                .PopulationStandardDeviation());
    }
}

And here are the tests:

[TestClass]
public class StandardDeviationTest
{
    private readonly double[] EmptySet = new double[] {};
    private readonly double[] SingleValue = new double[] { 1.0 };
    private readonly double[] SameValues = new double[] { 5.0, 5.0, 5.0 };
    private readonly double[] ScenarioValues = new double[] { 2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0 };
 
    private const double ScenarioValuesSampleDeviation = 2.13809;
    private const double ScenarioValuesPopulationDeviation = 2.0;
 
    [TestMethod]
    public void SampleStandardDeviation_EmptySet_HasZeroDeviation()
    {
        Assert.AreEqual(0.0, EmptySet.SampleStandardDeviation());
    }
 
    [TestMethod]
    public void SampleStandardDeviation_SingleValue_HasZeroDeviation()
    {
        Assert.AreEqual(0.0, SingleValue.SampleStandardDeviation());
    }
 
    [TestMethod]
    public void SampleStandardDeviation_SetOfSameValues_HasZeroDeviation()
    {
        Assert.AreEqual(0.0, SameValues.SampleStandardDeviation());
    }
 
    [TestMethod]
    public void SampleStandardDeviation_ScenarioValues_HaveExpectedDeviation()
    {
        Assert.AreEqual(ScenarioValuesSampleDeviation, Math.Round(ScenarioValues.SampleStandardDeviation(), 5));
    }
 
    [TestMethod]
    public void SampleStandardDeviation_TimeSpanScenarioValues_HaveExpectedDeviation()
    {
        var timeSpanValues = ScenarioValues.Select(seconds => new TimeSpan(0, 0, (int)seconds));
        var result = timeSpanValues.SampleStandardDeviation().TotalSeconds;
        Assert.AreEqual(ScenarioValuesSampleDeviation, Math.Round(result, 5));
    }
 
    [TestMethod]
    public void PopulationStandardDeviation_EmptySet_HasZeroDeviation()
    {
        Assert.AreEqual(0.0, EmptySet.PopulationStandardDeviation());
    }
 
    [TestMethod]
    public void PopulationStandardDeviation_SingleValue_HasZeroDeviation()
    {
        Assert.AreEqual(0.0, SingleValue.PopulationStandardDeviation());
    }
 
    [TestMethod]
    public void PopulationStandardDeviation_SetOfSameValues_HasZeroDeviation()
    {
        Assert.AreEqual(0.0, SameValues.PopulationStandardDeviation());
    }
 
    [TestMethod]
    public void PopulationStandardDeviation_ScenarioValues_HaveExpectedDeviation()
    {
        Assert.AreEqual(ScenarioValuesPopulationDeviation, Math.Round(ScenarioValues.PopulationStandardDeviation(), 5));
    }
 
    [TestMethod]
    public void PopulationStandardDeviation_TimeSpanScenarioValues_HaveExpectedDeviation()
    {
        var timeSpanValues = ScenarioValues.Select(seconds => new TimeSpan(0, 0, (int)seconds));
        var result = timeSpanValues.PopulationStandardDeviation().TotalSeconds;
        Assert.AreEqual(ScenarioValuesPopulationDeviation, Math.Round(result, 5));
    }
}
No comments yet

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS