Skip to main content

Calculate Calculate Mean, Median, and Mode (average calculations) in C#.

// ============================================================================
// Source: https://github.com/lukewickstead/DOT-NET-on-Linux/tree/master/SimpleIntroductions/Day2NUnitExample
// ============================================================================

//-----------------------------------------------------------------------
// <copyright file="Mean.cs" >Copyright (c) ThereBNone </copyright>
// <author>Luke Wickstead</author>
namespace MathsLibrary.Average
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Mean calcuation
    /// </summary>
    public class Mean
    {
        /// <summary>
        /// Calculate the specified  meandata.
        /// </summary>
        /// <param name='data'>
        /// Data to have the mean calculated
        /// </param>
        /// <returns>Mean value</returns>
        public static decimal Calculate(List<decimal> data)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }

            if (data.Count == 0)
            {
                throw new ArgumentException("No data provided");
            }

            return data.Sum() / data.Count;
        }
    }
}

//-----------------------------------------------------------------------
// <copyright file="Median.cs" >Copyright (c) ThereBNone </copyright>
// <author>Luke Wickstead</author>
namespace MathsLibrary.Average
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Median calculator
    /// </summary>
    public class Median
    {
        /// <summary>
        /// Calculate the specified data.
        /// </summary>
        /// <param name='data'>
        /// Data to be averaged
        /// </param>
        /// <returns>Median data</returns>
        public static decimal Calculate(List<decimal> data)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }

            if (data.Count == 0)
            {
                throw new ArgumentException("No data provided");
            }

            data.Sort();

            if (IsEven(data.Count))
            {
                return CalculatWithEvenData(data);
            }
            else
            {
                return CalculatWithOddData(data);
            }
        }

        /// <summary>
        /// Calculats the median the with odd data elements.
        /// </summary>
        /// <returns>
        /// The with odd data.
        /// </returns>
        /// <param name='data'>
        /// Data to be averaged
        /// </param>
        /// <returns>Median data</returns>
        private static decimal CalculatWithOddData(List<decimal> data)
        {
            var middlePosition = (decimal)data.Count / 2;

            --middlePosition;

            return data[(int)Math.Ceiling((decimal)middlePosition)];
        }

        /// <summary>Calculats the with even data.</summary>
        /// <returns>The with even data.</returns>
        /// <param name='data'>Data to be averaged</param>
        /// <returns>averaged data</returns>
        private static decimal CalculatWithEvenData(List<decimal> data)
        {
            var middlePosition = data.Count / 2;

            --middlePosition;

            var lower = data[middlePosition];
            var upper = data[middlePosition + 1];

            return (lower + upper) / 2;
        }

        /// <summary>Is the int even</summary>
        /// <returns>True/False is the count is event.</returns>
        /// <param name='count'>Element being analysed</param>
        private static bool IsEven(int count)
        {
            decimal divCheck = (decimal)count / 2;

            return (divCheck - Math.Floor(divCheck)) == 0;
        }
    }
}

//-----------------------------------------------------------------------
// <copyright file="Mode.cs" >Copyright (c) ThereBNone </copyright>
// <author>Luke Wickstead</author>
namespace MathsLibrary.Average
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Mode Average
    /// </summary>
    public class Mode
    {
        /// <summary>
        /// Calculate the mode average
        /// </summary>
        /// <param name='data'>
        /// Data to be averages
        /// </param>
        /// <returns>Mode average</returns>
        public static decimal Calculate(List<decimal> data)
        {
            if (data == null)
            {
                throw new ArgumentNullException();
            }

            if (data.Count == 0)
            {
                throw new ArgumentException("No data provided");
            }

            var occuranceCount = CountOccurances(data);
            var maxOccurance = occuranceCount.Max(x => x.Value);
            var valueWithMaxOccurance = occuranceCount.Where(x => x.Value == maxOccurance).Select(x => x.Key);

            if (valueWithMaxOccurance.ToList().Count > 1)
            {
                throw new NotImplementedException("We have not quite got around to  Mode with grouping...");
            }

            return valueWithMaxOccurance.First();
        }

        /// <summary>
        /// Counts the occurances.
        /// </summary>
        /// <returns>
        /// Dictionary of element vs occurance count
        /// </returns>
        /// <param name='data'>
        /// Data being analysed
        /// </param>
        /// <returns>Dictionary of occurances of elements</returns>
        private static Dictionary<decimal, int> CountOccurances(List<decimal> data)
        {
            var counter = new Dictionary<decimal, int>();

            foreach (decimal value in data)
            {
                if (counter.ContainsKey(value))
                {
                    counter[value] += 1;
                }
                else
                {
                    counter[value] = 1;
                }
            }

            return counter;
        }
    }
}

// --------------------------------------------------------------------------
// Unit tests
// --------------------------------------------------------------------------

//-----------------------------------------------------------------------
// <copyright file="MeanTests.cs" >Copyright (c) ThereBNone </copyright>
// <author>Luke Wickstead</author>
namespace MathsLibrary.Tests
{
    using System;
    using System.Collections.Generic;
    using MathsLibrary.Average;
    using NUnit.Framework;

    /// <summary>
    /// Mean tests.
    /// </summary>
    [TestFixture]
    public class MeanTests
    {
        /// <summary>
        /// Mean test
        /// </summary>
        [Test]
        public void CanCalculateMean()
        {
            var data = new List<decimal> { 1.1m, 2.2m, 3.3m, 4.4m, 5.5m };

            var modeValue = Mean.Calculate(data);

            Assert.AreEqual(modeValue, 3.3);
        }

        /// <summary>
        /// Checks error thrown when null passed in
        /// </summary>
        [Test]
        public void WillThrowArgumentNullExceptionWhenNull()
        {
            Assert.Throws<ArgumentNullException>(() => Mean.Calculate(null));
        }

        /// <summary>
        /// Checks error thrown when an empty collection is passed in.
        /// </summary>
        [Test]
        public void WillThrowArgumentExceptionWhenEmptyCollection()
        {
            Assert.Throws<ArgumentException>(() => Mean.Calculate(new List<decimal>()));
        }
    }
}

//-----------------------------------------------------------------------
// <copyright file="MedianTests.cs" >Copyright (c) ThereBNone </copyright>
// <author>Luke Wickstead</author>
namespace MathsLibrary.Tests
{
    using System;
    using System.Collections.Generic;
    using MathsLibrary.Average;
    using NUnit.Framework;

    /// <summary>
    /// Median tests.
    /// </summary>
    [TestFixture]
    public class MedianTests
    {
        /// <summary>
        /// Determines whether this instance can calculate median with odd number.
        /// </summary>
        [Test]
        public void CanCalculateMedianWithOddNumber()
        {
            var data = new List<decimal> { 1.1m, 2.2m, 3.3m, 4.4m, 5.5m };

            var medianVale = Median.Calculate(data);

            Assert.AreEqual(medianVale, 3.3);
        }

        /// <summary>
        /// Determines whether this instance can calculate median with even number.
        /// </summary>
        [Test]
        public void CanCalculateMedianWithEvenNumber()
        {
            var data = new List<decimal> { 1.1m, 2.2m, 3.3m, 4.4m };

            var medianVale = Median.Calculate(data);

            Assert.AreEqual(medianVale, 2.75);
        }

        /// <summary>
        /// Wills the throw argument null exception when null.
        /// </summary>
        [Test]
        public void WillThrowArgumentNullExceptionWhenNull()
        {
            Assert.Throws<ArgumentNullException>(() => Median.Calculate(null));
        }

        /// <summary>
        /// Wills the throw argument exception when empty collection.
        /// </summary>
        [Test]
        public void WillThrowArgumentExceptionWhenEmptyCollection()
        {
            Assert.Throws<ArgumentException>(() => Median.Calculate(new List<decimal>()));
        }
    }
}

//-----------------------------------------------------------------------
// <copyright file="ModeTests.cs" >Copyright (c) ThereBNone </copyright>
// <author>Luke Wickstead</author>

namespace MathsLibrary.Tests
{
    using System;
    using System.Collections.Generic;
    using MathsLibrary.Average;
    using NUnit.Framework;

    /// <summary>
    /// Mode tests.
    /// </summary>
    [TestFixture]
    public class ModeTests
    {
        /// <summary>
        /// Determines whether this instance can calculate mode with no grouping.
        /// </summary>
        [Test]
        public void CanCalculateModeWithNoGrouping()
        {
            var data = new List<decimal> { 1.1m, 2.2m, 2.2m, 3.3m, 4.4m, 5.5m };

            var modeValue = Mode.Calculate(data);

            Assert.AreEqual(modeValue, 2.2);
        }

        /// <summary>
        /// Determines whether this instance can not calculate modewith grouping.
        /// </summary>
        [Test]
        public void CanNotCalculateModewithGrouping()
        {
            var data = new List<decimal> { 1.1m, 2.2m };

            Assert.Throws<NotImplementedException>(() => Mode.Calculate(data));
        }

        /// <summary>
        /// Wills the throw argument null exception when null.
        /// </summary>
        [Test]
        public void WillThrowArgumentNullExceptionWhenNull()
        {
            Assert.Throws<ArgumentNullException>(() => Mode.Calculate(null));
        }

        /// <summary>
        /// Wills the throw argument exception when empty collection.
        /// </summary>
        [Test]
        public void WillThrowArgumentExceptionWhenEmptyCollection()
        {
            Assert.Throws<ArgumentException>(() => Mode.Calculate(new List<decimal>()));
        }
    }
}