// // Math.NET Numerics, part of the Math.NET Project // http://mathnet.opensourcedotnet.info // // Copyright (c) 2009 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // namespace MathNet.Numerics.Statistics { using System; using System.Collections.Generic; using Properties; /// /// Extension methods to return basic statistics on set of data. /// public static class Statistics { /// /// Calculates the sample mean. /// /// The data to calculate the mean of. /// The mean of the sample. public static double Mean(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double mean = 0; int m = 0; foreach (var item in data) { mean += (item - mean) / ++m; } return mean; } /// /// Calculates the sample mean. /// /// The data to calculate the mean of. /// The mean of the sample. public static double Mean(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double mean = 0; int m = 0; foreach (var item in data) { if (item.HasValue) { mean += (item.Value - mean) / ++m; } } return mean; } /// /// Calculates the unbiased population variance estimator (on a dataset of size N will use an N-1 normalizer). /// /// The data to calculate the variance of. /// The unbiased population variance of the sample. public static double Variance(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double variance = 0; double t = 0; int j = 0; IEnumerator iterator = data.GetEnumerator(); if (iterator.MoveNext()) { j++; t = iterator.Current; } while (iterator.MoveNext()) { j++; double xi = iterator.Current; t += xi; double diff = (j * xi) - t; variance += (diff * diff) / (j * (j - 1)); } return variance / (j - 1); } /// /// Computes the unbiased population variance estimator (on a dataset of size N will use an N-1 normalizer) for nullable data. /// /// The data to calculate the variance of. /// The population variance of the sample. public static double Variance(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double variance = 0; double t = 0; int j = 0; IEnumerator iterator = data.GetEnumerator(); while (true) { bool hasNext = iterator.MoveNext(); if (!hasNext) { break; } if (iterator.Current.HasValue) { j++; t = iterator.Current.Value; break; } } while (iterator.MoveNext()) { if (iterator.Current.HasValue) { j++; double xi = iterator.Current.Value; t += xi; double diff = (j * xi) - t; variance += (diff * diff) / (j * (j - 1)); } } return variance / (j - 1); } /// /// Calculates the biased population variance estimator (on a dataset of size N will use an N normalizer). /// /// The data to calculate the variance of. /// The biased population variance of the sample. public static double PopulationVariance(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double variance = 0; double t = 0; int j = 0; IEnumerator iterator = data.GetEnumerator(); if (iterator.MoveNext()) { j++; t = iterator.Current; } while (iterator.MoveNext()) { j++; double xi = iterator.Current; t += xi; double diff = (j * xi) - t; variance += (diff * diff) / (j * (j - 1)); } return variance / j; } /// /// Computes the biased population variance estimator (on a dataset of size N will use an N normalizer) for nullable data. /// /// The data to calculate the variance of. /// The population variance of the sample. public static double PopulationVariance(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double variance = 0; double t = 0; int j = 0; IEnumerator iterator = data.GetEnumerator(); while (true) { bool hasNext = iterator.MoveNext(); if (!hasNext) { break; } if (iterator.Current.HasValue) { j++; t = iterator.Current.Value; break; } } while (iterator.MoveNext()) { if (iterator.Current.HasValue) { j++; double xi = iterator.Current.Value; t += xi; double diff = (j * xi) - t; variance += (diff * diff) / (j * (j - 1)); } } return variance / j; } /// /// Calculates the unbiased sample standard deviation (on a dataset of size N will use an N-1 normalizer). /// /// The data to calculate the standard deviation of. /// The standard deviation of the sample. public static double StandardDeviation(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } return Math.Sqrt(Variance(data)); } /// /// Calculates the unbiased sample standard deviation (on a dataset of size N will use an N-1 normalizer). /// /// The data to calculate the standard deviation of. /// The standard deviation of the sample. public static double StandardDeviation(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } return Math.Sqrt(Variance(data)); } /// /// Calculates the biased sample standard deviation (on a dataset of size N will use an N normalizer). /// /// The data to calculate the standard deviation of. /// The standard deviation of the sample. public static double PopulationStandardDeviation(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } return Math.Sqrt(PopulationVariance(data)); } /// /// Calculates the biased sample standard deviation (on a dataset of size N will use an N normalizer). /// /// The data to calculate the standard deviation of. /// The standard deviation of the sample. public static double PopulationStandardDeviation(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } return Math.Sqrt(PopulationVariance(data)); } /// /// Returns the minimum value in the sample data. /// /// The sample data. /// The minimum value in the sample data. public static double Minimum(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double min = double.MaxValue; int count = 0; foreach (double? d in data) { if (d.HasValue) { min = Math.Min(min, d.Value); count++; } } if (count == 0) { throw new ArgumentException(Resources.CollectionEmpty, "data"); } return min; } /// /// Returns the maximum value in the sample data. /// /// The sample data. /// The maximum value in the sample data. public static double Maximum(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double max = double.MinValue; int count = 0; foreach (double? d in data) { if (d.HasValue) { max = Math.Max(max, d.Value); count++; } } if (count == 0) { throw new ArgumentException(Resources.CollectionEmpty, "data"); } return max; } /// /// Returns the minimum value in the sample data. /// /// The sample data. /// The minimum value in the sample data. public static double Minimum(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double min = double.MaxValue; int count = 0; foreach (double d in data) { min = Math.Min(min, d); count++; } if (count == 0) { throw new ArgumentException(Resources.CollectionEmpty, "data"); } return min; } /// /// Returns the maximum value in the sample data. /// /// The sample data. /// The maximum value in the sample data. public static double Maximum(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } double max = double.MinValue; int count = 0; foreach (double d in data) { max = Math.Max(max, d); count++; } if (count == 0) { throw new ArgumentException(Resources.CollectionEmpty, "data"); } return max; } /// /// Calculates the sample median. /// /// The data to calculate the median of. /// The median of the sample. public static double Median(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } var dataArray = new List(data); int index = (dataArray.Count / 2) + 1; if (dataArray.Count % 2 == 0) { double lower = OrderSelect(dataArray, 0, dataArray.Count - 1, index - 1); double upper = OrderSelect(dataArray, 0, dataArray.Count - 1, index); return (lower + upper) / 2.0; } return OrderSelect(dataArray, 0, dataArray.Count - 1, index); } /// /// Calculates the sample median. /// /// The data to calculate the median of. /// The median of the sample. public static double Median(this IEnumerable data) { if (data == null) { throw new ArgumentNullException("data"); } var nonNull = new List(); foreach (double? value in data) { if (value.HasValue) { nonNull.Add(value.Value); } } if (nonNull.Count == 0) { throw new ArgumentException(Resources.CollectionEmpty, "data"); } return nonNull.Median(); } /// /// Evaluate the i-order (1..N) statistic of the provided samples. /// /// The sample data. /// Order of the statistic to evaluate. /// The i'th order statistic in the sample data. public static double OrderStatistic(IEnumerable samples, int order) { if (order == 1) { // Can be done in linear time by Min() return Minimum(samples); } var list = new List(samples); if (order < 1 || order > list.Count) { throw new ArgumentOutOfRangeException("order", Resources.ArgumentInIntervalXYInclusive); } if (order == list.Count) { // Can be done in linear time by Max() return Maximum(list); } return OrderSelect(list, 0, list.Count - 1, order); } /// /// Implementation of the order statistics finding algorithm based on the algorithm in /// "Introduction to Algorithms", Cormen et al. section 7.1. /// /// The sample data. /// The left bound in which to order select. /// The right bound in which to order select. /// The order we are trying to find. /// The order statistic. private static double OrderSelect(IList samples, int left, int right, int order) { System.Diagnostics.Debug.Assert(order > 0, "Order must always be positive."); System.Diagnostics.Debug.Assert(left >= 0 && left <= right, "Left side must always be positive and smaller than right side."); System.Diagnostics.Debug.Assert(right < samples.Count, "Right side must always be smaller than number of elements in list."); System.Diagnostics.Debug.Assert(right - left + 1 >= order, "Make sure there are at least order items in the segment [left, right]."); if (left == right) { return samples[left]; } // The pivot point. double pivot = samples[right]; // The partioning code. int i = left - 1; for (int j = left; j <= right - 1; j++) { if (samples[j] <= pivot) { i++; Sorting.Swap(samples, i, j); } } Sorting.Swap(samples, i + 1, right); // Recursive order finding algorithm. if (order == (i - left) + 2) { return pivot; } if (order < (i - left) + 2) { return OrderSelect(samples, left, i, order); } return OrderSelect(samples, i + 2, right, order - i + left - 2); } } }