diff --git a/src/Numerics/Statistics/RunningStatistics.cs b/src/Numerics/Statistics/RunningStatistics.cs index bb5746e4..6af0bf9b 100644 --- a/src/Numerics/Statistics/RunningStatistics.cs +++ b/src/Numerics/Statistics/RunningStatistics.cs @@ -225,7 +225,7 @@ namespace MathNet.Numerics.Statistics double m1 = (a._n*a._m1 + b._n*b._m1)/n; double m2 = a._m2 + b._m2 + d2*a._n*b._n/n; double m3 = a._m3 + b._m3 + d3*a._n*b._n*(a._n - b._n)/(n*n) - + 3*d3*(a._n*b._m2 - b._n*a._m2)/n; + + 3*d*(a._n*b._m2 - b._n*a._m2)/n; double m4 = a._m4 + b._m4 + d4*a._n*b._n*(a._n*a._n - a._n*b._n + b._n*b._n)/(n*n*n) + 6*d2*(a._n*a._n*b._m2 + b._n*b._n*a._m2)/(n*n) + 4*d*(a._n*b._m3 - b._n*a._m3)/n; diff --git a/src/Numerics/Statistics/Statistics.cs b/src/Numerics/Statistics/Statistics.cs index 6cd3fa0f..b687310e 100644 --- a/src/Numerics/Statistics/Statistics.cs +++ b/src/Numerics/Statistics/Statistics.cs @@ -91,7 +91,7 @@ namespace MathNet.Numerics.Statistics } /// - /// Estimates the sample mean. + /// Evaluates the sample mean, an estimate of the population mean. /// Returns NaN if data is empty or if any entry is NaN. /// /// The data to calculate the mean of. @@ -105,7 +105,7 @@ namespace MathNet.Numerics.Statistics } /// - /// Estimates the sample mean. + /// Evaluates the sample mean, an estimate of the population mean. /// Returns NaN if data is empty or if any entry is NaN. /// Null-entries are ignored. /// @@ -143,7 +143,7 @@ namespace MathNet.Numerics.Statistics } /// - /// Evaluates the population variance from the provided full population. + /// Evaluates the variance from the provided full population. /// On a dataset of size N will use an N normalizer and would thus be biased if applied to a subset. /// Returns NaN if data is empty or if any entry is NaN. /// @@ -157,7 +157,7 @@ namespace MathNet.Numerics.Statistics } /// - /// Evaluates the population variance from the provided full population. + /// Evaluates the variance from the provided full population. /// On a dataset of size N will use an N normalize and would thus be biased if applied to a subsetr. /// Returns NaN if data is empty or if any entry is NaN. /// Null-entries are ignored. @@ -195,7 +195,7 @@ namespace MathNet.Numerics.Statistics } /// - /// Evaluates the population standard deviation from the provided full population. + /// Evaluates the standard deviation from the provided full population. /// On a dataset of size N will use an N normalizer and would thus be biased if applied to a subset. /// Returns NaN if data is empty or if any entry is NaN. /// @@ -209,7 +209,7 @@ namespace MathNet.Numerics.Statistics } /// - /// Evaluates the population standard deviation from the provided full population. + /// Evaluates the standard deviation from the provided full population. /// On a dataset of size N will use an N normalizer and would thus be biased if applied to a subset. /// Returns NaN if data is empty or if any entry is NaN. /// Null-entries are ignored. @@ -220,6 +220,98 @@ namespace MathNet.Numerics.Statistics return StreamingStatistics.PopulationStandardDeviation(population.Where(d => d.HasValue).Select(d => d.Value)); } + /// + /// Estimates the unbiased population skewness from the provided samples. + /// Uses a normalizer (Bessel's correction; type 2). + /// Returns NaN if data has less than three entries or if any entry is NaN. + /// + /// A subset of samples, sampled from the full population. + public static double Skewness(this IEnumerable samples) + { + return new RunningStatistics(samples).Skewness; + } + + /// + /// Estimates the unbiased population skewness from the provided samples. + /// Uses a normalizer (Bessel's correction; type 2). + /// Returns NaN if data has less than three entries or if any entry is NaN. + /// Null-entries are ignored. + /// + /// A subset of samples, sampled from the full population. + public static double Skewness(this IEnumerable samples) + { + return new RunningStatistics(samples.Where(d => d.HasValue).Select(d => d.Value)).Skewness; + } + + /// + /// Evaluates the skewness from the full population. + /// Does not use a normalizer and would thus be biased if applied to a subset (type 1). + /// Returns NaN if data has less than two entries or if any entry is NaN. + /// + /// The full population data. + public static double PopulationSkewness(this IEnumerable population) + { + return new RunningStatistics(population).PopulationSkewness; + } + + /// + /// Evaluates the skewness from the full population. + /// Does not use a normalizer and would thus be biased if applied to a subset (type 1). + /// Returns NaN if data has less than two entries or if any entry is NaN. + /// Null-entries are ignored. + /// + /// The full population data. + public static double PopulationSkewness(this IEnumerable population) + { + return new RunningStatistics(population.Where(d => d.HasValue).Select(d => d.Value)).PopulationSkewness; + } + + /// + /// Estimates the unbiased population kurtosis from the provided samples. + /// Uses a normalizer (Bessel's correction; type 2). + /// Returns NaN if data has less than four entries or if any entry is NaN. + /// + /// A subset of samples, sampled from the full population. + public static double Kurtosis(this IEnumerable samples) + { + return new RunningStatistics(samples).Kurtosis; + } + + /// + /// Estimates the unbiased population kurtosis from the provided samples. + /// Uses a normalizer (Bessel's correction; type 2). + /// Returns NaN if data has less than four entries or if any entry is NaN. + /// Null-entries are ignored. + /// + /// A subset of samples, sampled from the full population. + public static double Kurtosis(this IEnumerable samples) + { + return new RunningStatistics(samples.Where(d => d.HasValue).Select(d => d.Value)).Kurtosis; + } + + /// + /// Evaluates the kurtosis from the full population. + /// Does not use a normalizer and would thus be biased if applied to a subset (type 1). + /// Returns NaN if data has less than three entries or if any entry is NaN. + /// + /// The full population data. + public static double PopulationKurtosis(this IEnumerable population) + { + return new RunningStatistics(population).PopulationKurtosis; + } + + /// + /// Evaluates the kurtosis from the full population. + /// Does not use a normalizer and would thus be biased if applied to a subset (type 1). + /// Returns NaN if data has less than three entries or if any entry is NaN. + /// Null-entries are ignored. + /// + /// The full population data. + public static double PopulationKurtosis(this IEnumerable population) + { + return new RunningStatistics(population.Where(d => d.HasValue).Select(d => d.Value)).PopulationKurtosis; + } + /// /// Estimates the sample mean and the unbiased population variance from the provided samples. /// On a dataset of size N will use an N-1 normalizer (Bessel's correction). @@ -250,6 +342,28 @@ namespace MathNet.Numerics.Statistics : StreamingStatistics.MeanStandardDeviation(samples); } + /// + /// Estimates the unbiased population skewness and kurtosis from the provided samples in a single pass. + /// Uses a normalizer (Bessel's correction; type 2). + /// + /// A subset of samples, sampled from the full population. + public static Tuple SkewnessKurtosis(this IEnumerable samples) + { + var stats = new RunningStatistics(samples); + return new Tuple(stats.Skewness, stats.Kurtosis); + } + + /// + /// Evaluates the skewness and kurtosis from the full population. + /// Does not use a normalizer and would thus be biased if applied to a subset (type 1). + /// + /// The full population data. + public static Tuple PopulationSkewnessKurtosis(this IEnumerable population) + { + var stats = new RunningStatistics(population); + return new Tuple(stats.PopulationSkewness, stats.PopulationKurtosis); + } + /// /// Estimates the unbiased population covariance from the provided samples. /// On a dataset of size N will use an N-1 normalizer (Bessel's correction). @@ -297,7 +411,7 @@ namespace MathNet.Numerics.Statistics /// /// Evaluates the population covariance from the provided full populations. - /// On a dataset of size N will use an N normalize and would thus be biased if applied to a subsetr. + /// On a dataset of size N will use an N normalize and would thus be biased if applied to a subset. /// Returns NaN if data is empty or if any entry is NaN. /// Null-entries are ignored. /// diff --git a/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs b/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs index cc8e7d03..bb87c8c4 100644 --- a/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs +++ b/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs @@ -28,6 +28,10 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using System.Linq; +using MathNet.Numerics.Distributions; +using MathNet.Numerics.Random; + namespace MathNet.Numerics.UnitTests.StatisticsTests { #if !PORTABLE @@ -165,6 +169,64 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(stats.Skewness, Is.NaN); Assert.That(stats.Kurtosis, Is.NaN); } + + [Test] + public void Combine() + { + var rnd = new SystemRandomSource(10); + var a = Generate.Random(200, new Erlang(2, 0.2, rnd)); + var b = Generate.Random(100, new Beta(1.2, 1.4, rnd)); + var c = Generate.Random(150, new Rayleigh(0.8, rnd)); + + var d = a.Concat(b).Concat(c).ToArray(); + + var x = new RunningStatistics(d); + + var y = new RunningStatistics(a); + y.PushRange(b); + y.PushRange(c); + + var za = new RunningStatistics(a); + var zb = new RunningStatistics(b); + var zc = new RunningStatistics(c); + var z = za + zb + zc; + + Assert.That(x.Mean, Is.EqualTo(d.Mean()).Within(1e-12), "Mean Reference"); + Assert.That(y.Mean, Is.EqualTo(x.Mean).Within(1e-12), "Mean y"); + Assert.That(z.Mean, Is.EqualTo(x.Mean).Within(1e-12), "Mean z"); + + Assert.That(x.Variance, Is.EqualTo(d.Variance()).Within(1e-12), "Variance Reference"); + Assert.That(y.Variance, Is.EqualTo(x.Variance).Within(1e-12), "Variance y"); + Assert.That(z.Variance, Is.EqualTo(x.Variance).Within(1e-12), "Variance z"); + + Assert.That(x.PopulationVariance, Is.EqualTo(d.PopulationVariance()).Within(1e-12), "PopulationVariance Reference"); + Assert.That(y.PopulationVariance, Is.EqualTo(x.PopulationVariance).Within(1e-12), "PopulationVariance y"); + Assert.That(z.PopulationVariance, Is.EqualTo(x.PopulationVariance).Within(1e-12), "PopulationVariance z"); + + Assert.That(x.StandardDeviation, Is.EqualTo(d.StandardDeviation()).Within(1e-12), "StandardDeviation Reference"); + Assert.That(y.StandardDeviation, Is.EqualTo(x.StandardDeviation).Within(1e-12), "StandardDeviation y"); + Assert.That(z.StandardDeviation, Is.EqualTo(x.StandardDeviation).Within(1e-12), "StandardDeviation z"); + + Assert.That(x.PopulationStandardDeviation, Is.EqualTo(d.PopulationStandardDeviation()).Within(1e-12), "PopulationStandardDeviation Reference"); + Assert.That(y.PopulationStandardDeviation, Is.EqualTo(x.PopulationStandardDeviation).Within(1e-12), "PopulationStandardDeviation y"); + Assert.That(z.PopulationStandardDeviation, Is.EqualTo(x.PopulationStandardDeviation).Within(1e-12), "PopulationStandardDeviation z"); + + Assert.That(x.Skewness, Is.EqualTo(d.Skewness()).Within(1e-12), "Skewness Reference (not independent!)"); + Assert.That(y.Skewness, Is.EqualTo(x.Skewness).Within(1e-12), "Skewness y"); + Assert.That(z.Skewness, Is.EqualTo(x.Skewness).Within(1e-12), "Skewness z"); + + Assert.That(x.PopulationSkewness, Is.EqualTo(d.PopulationSkewness()).Within(1e-12), "PopulationSkewness Reference (not independent!)"); + Assert.That(y.PopulationSkewness, Is.EqualTo(x.PopulationSkewness).Within(1e-12), "PopulationSkewness y"); + Assert.That(z.PopulationSkewness, Is.EqualTo(x.PopulationSkewness).Within(1e-12), "PopulationSkewness z"); + + Assert.That(x.Kurtosis, Is.EqualTo(d.Kurtosis()).Within(1e-12), "Kurtosis Reference (not independent!)"); + Assert.That(y.Kurtosis, Is.EqualTo(x.Kurtosis).Within(1e-12), "Kurtosis y"); + Assert.That(z.Kurtosis, Is.EqualTo(x.Kurtosis).Within(1e-12), "Kurtosis z"); + + Assert.That(x.PopulationKurtosis, Is.EqualTo(d.PopulationKurtosis()).Within(1e-12), "PopulationKurtosis Reference (not independent!)"); + Assert.That(y.PopulationKurtosis, Is.EqualTo(x.PopulationKurtosis).Within(1e-12), "PopulationKurtosis y"); + Assert.That(z.PopulationKurtosis, Is.EqualTo(x.PopulationKurtosis).Within(1e-12), "PopulationKurtosis z"); + } } #endif }