Math.NET Numerics
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

570 lines
18 KiB

// <copyright file="Statistics.cs" company="Math.NET">
// 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.
// </copyright>
namespace MathNet.Numerics.Statistics
{
using System;
using System.Collections.Generic;
using MathNet.Numerics.Properties;
using MathNet.Numerics.NumberTheory;
/// <summary>
/// Extension methods to return basic statistics on set of data.
/// </summary>
public static class Statistics
{
/// <summary>
/// Calculates the sample mean.
/// </summary>
/// <param name="data">The data to calculate the mean of.</param>
/// <returns>The mean of the sample.</returns>
public static double Mean(this IEnumerable<double> 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;
}
/// <summary>
/// Calculates the sample mean.
/// </summary>
/// <param name="data">The data to calculate the mean of.</param>
/// <returns>The mean of the sample.</returns>
public static double Mean(this IEnumerable<double?> 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;
}
/// <summary>
/// Calculates the unbiased population variance estimator (on a dataset of size N will use an N-1 normalizer).
/// </summary>
/// <param name="data">The data to calculate the variance of.</param>
/// <returns>The unbiased population variance of the sample.</returns>
public static double Variance(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double variance = 0;
double t = 0;
int j = 0;
IEnumerator<double> 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);
}
/// <summary>
/// Computes the unbiased population variance estimator (on a dataset of size N will use an N-1 normalizer) for nullable data.
/// </summary>
/// <param name="data">The data to calculate the variance of.</param>
/// <returns>The population variance of the sample.</returns>
public static double Variance(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double variance = 0;
double t = 0;
int j = 0;
IEnumerator<double?> 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);
}
/// <summary>
/// Calculates the biased population variance estimator (on a dataset of size N will use an N normalizer).
/// </summary>
/// <param name="data">The data to calculate the variance of.</param>
/// <returns>The biased population variance of the sample.</returns>
public static double PopulationVariance(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double variance = 0;
double t = 0;
int j = 0;
IEnumerator<double> 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;
}
/// <summary>
/// Computes the biased population variance estimator (on a dataset of size N will use an N normalizer) for nullable data.
/// </summary>
/// <param name="data">The data to calculate the variance of.</param>
/// <returns>The population variance of the sample.</returns>
public static double PopulationVariance(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double variance = 0;
double t = 0;
int j = 0;
IEnumerator<double?> 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;
}
/// <summary>
/// Calculates the unbiased sample standard deviation (on a dataset of size N will use an N-1 normalizer).
/// </summary>
/// <param name="data">The data to calculate the standard deviation of.</param>
/// <returns>The standard deviation of the sample.</returns>
public static double StandardDeviation(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
return System.Math.Sqrt(Variance(data));
}
/// <summary>
/// Calculates the unbiased sample standard deviation (on a dataset of size N will use an N-1 normalizer).
/// </summary>
/// <param name="data">The data to calculate the standard deviation of.</param>
/// <returns>The standard deviation of the sample.</returns>
public static double StandardDeviation(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
return System.Math.Sqrt(Variance(data));
}
/// <summary>
/// Calculates the biased sample standard deviation (on a dataset of size N will use an N normalizer).
/// </summary>
/// <param name="data">The data to calculate the standard deviation of.</param>
/// <returns>The standard deviation of the sample.</returns>
public static double PopulationStandardDeviation(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
return System.Math.Sqrt(PopulationVariance(data));
}
/// <summary>
/// Calculates the biased sample standard deviation (on a dataset of size N will use an N normalizer).
/// </summary>
/// <param name="data">The data to calculate the standard deviation of.</param>
/// <returns>The standard deviation of the sample.</returns>
public static double PopulationStandardDeviation(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
return System.Math.Sqrt(PopulationVariance(data));
}
/// <summary>
/// Returns the minimum value in the sample data.
/// </summary>
/// <param name="data">The sample data.</param>
/// <returns>The minimum value in the sample data.</returns>
public static double Minimum(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double min = double.MaxValue;
int count = 0;
foreach (double? d in data)
{
if (d.HasValue)
{
min = System.Math.Min(min, d.Value);
count++;
}
}
if (count == 0)
{
throw new ArgumentException(Resources.CollectionEmpty, "data");
}
return min;
}
/// <summary>
/// Returns the maximum value in the sample data.
/// </summary>
/// <param name="data">The sample data.</param>
/// <returns>The maximum value in the sample data.</returns>
public static double Maximum(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double max = double.MinValue;
int count = 0;
foreach (double? d in data)
{
if (d.HasValue)
{
max = System.Math.Max(max, d.Value);
count++;
}
}
if (count == 0)
{
throw new ArgumentException(Resources.CollectionEmpty, "data");
}
return max;
}
/// <summary>
/// Returns the minimum value in the sample data.
/// </summary>
/// <param name="data">The sample data.</param>
/// <returns>The minimum value in the sample data.</returns>
public static double Minimum(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double min = double.MaxValue;
int count = 0;
foreach (double d in data)
{
min = System.Math.Min(min, d);
count++;
}
if (count == 0)
{
throw new ArgumentException(Resources.CollectionEmpty, "data");
}
return min;
}
/// <summary>
/// Returns the maximum value in the sample data.
/// </summary>
/// <param name="data">The sample data.</param>
/// <returns>The maximum value in the sample data.</returns>
public static double Maximum(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
double max = double.MinValue;
int count = 0;
foreach (double d in data)
{
max = System.Math.Max(max, d);
count++;
}
if (count == 0)
{
throw new ArgumentException(Resources.CollectionEmpty, "data");
}
return max;
}
/// <summary>
/// Calculates the sample median.
/// </summary>
/// <param name="data">The data to calculate the median of.</param>
/// <returns>The median of the sample.</returns>
public static double Median(this IEnumerable<double> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
List<double> dataArray = new List<double>(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;
}
else
{
return OrderSelect(dataArray, 0, dataArray.Count - 1, index);
}
}
/// <summary>
/// Calculates the sample median.
/// </summary>
/// <param name="data">The data to calculate the median of.</param>
/// <returns>The median of the sample.</returns>
public static double Median(this IEnumerable<double?> data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
List<double> nonNull = new List<double>();
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();
}
/// <summary>
/// Evaluate the i-order (1..N) statistic of the provided samples.
/// </summary>
/// <param name="data">The sample data.</param>
/// <returns>The i'th order statistic in the sample data.</returns>
public static double OrderStatistic(IEnumerable<double> samples, int order)
{
if (order == 1)
{
// Can be done in linear time by Min()
return Minimum(samples);
}
List<double> list = new List<double>(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);
}
/// <summary>
/// Implementation of the order statistics finding algorithm based on the algorithm in
/// "Introduction to Algorithms", Cormen et al. section 7.1.
/// </summary>
/// <param name="samples">The sample data.</param>
/// <param name="left">The left bound in which to order select.</param>
/// <param name="right">The right bound in which to order select.</param>
/// <param name="order">The order we are trying to find.</param>
/// <returns>The <paramref name="order"/> order statistic.</returns>
static double OrderSelect(IList<double> samples, int left, int right, int order)
{
// Order most always be positive.
System.Diagnostics.Debug.Assert(order > 0);
// Left side must always be positive and smaller than right side.
System.Diagnostics.Debug.Assert(left >= 0 && left <= right);
// Right side must always be smaller than number of elements in list.
System.Diagnostics.Debug.Assert(right < samples.Count);
// Make sure there are at least order items in the segment [left, right].
System.Diagnostics.Debug.Assert(right - left + 1 >= order);
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;
}
else if (order < (i-left)+2)
{
return OrderSelect(samples, left, i, order);
}
else
{
return OrderSelect(samples, i+2, right, order - i + left - 2);
}
}
}
}