diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index ec0826ec..0d27b347 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -206,6 +206,7 @@ + diff --git a/src/Numerics/Statistics/RunningStatistics.cs b/src/Numerics/Statistics/RunningStatistics.cs new file mode 100644 index 00000000..bb5746e4 --- /dev/null +++ b/src/Numerics/Statistics/RunningStatistics.cs @@ -0,0 +1,240 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2014 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. +// +// +// Adapted from the old DescriptiveStatistics and inspired in design +// among others by http://www.johndcook.com/skewness_kurtosis.html + +using System; +using System.Collections.Generic; + +namespace MathNet.Numerics.Statistics +{ + /// + /// Running statistics, allows updating by adding values, + /// or combining + /// + public class RunningStatistics + { + long _n; + double _m1; + double _m2; + double _m3; + double _m4; + double _min = Double.PositiveInfinity; + double _max = Double.NegativeInfinity; + + public RunningStatistics() + { + } + + public RunningStatistics(IEnumerable values) + { + PushRange(values); + } + + /// + /// Gets the total number of samples. + /// + public long Count + { + get { return _n; } + } + + /// + /// Returns the minimum value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + public double Minimum + { + get { return _n > 0 ? _min : double.NaN; } + } + + /// + /// Returns the maximum value in the sample data. + /// Returns NaN if data is empty or if any entry is NaN. + /// + public double Maximum + { + get { return _n > 0 ? _max : double.NaN; } + } + + /// + /// Evaluates the sample mean, an estimate of the population mean. + /// Returns NaN if data is empty or if any entry is NaN. + /// + public double Mean + { + get { return _n > 0 ? _m1 : double.NaN; } + } + + /// + /// Estimates the unbiased population variance from the provided samples. + /// On a dataset of size N will use an N-1 normalizer (Bessel's correction). + /// Returns NaN if data has less than two entries or if any entry is NaN. + /// + public double Variance + { + get { return _n < 2 ? double.NaN : _m2/(_n - 1); } + } + + /// + /// 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. + /// + public double PopulationVariance + { + get { return _n < 2 ? double.NaN : _m2/_n; } + } + + /// + /// Estimates the unbiased population standard deviation from the provided samples. + /// On a dataset of size N will use an N-1 normalizer (Bessel's correction). + /// Returns NaN if data has less than two entries or if any entry is NaN. + /// + public double StandardDeviation + { + get { return _n < 2 ? double.NaN : Math.Sqrt(_m2/(_n - 1)); } + } + + /// + /// 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. + /// + public double PopulationStandardDeviation + { + get { return _n < 2 ? double.NaN : Math.Sqrt(_m2/_n); } + } + + /// + /// 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. + /// + public double Skewness + { + get { return _n < 3 ? double.NaN : (_n*_m3*Math.Sqrt(_m2/(_n - 1))/(_m2*_m2*(_n - 2)))*(_n - 1); } + } + + /// + /// Evaluates the population 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. + /// + public double PopulationSkewness + { + get { return _n < 2 ? double.NaN : _m3*Math.Sqrt(_n*(_n - 1))*Math.Sqrt(_m2/(_n - 1))/(_m2*_m2); } + } + + /// + /// 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. + /// + public double Kurtosis + { + get { return _n < 4 ? double.NaN : ((double)_n*_n - 1)/((_n - 2)*(_n - 3))*(_n*_m4/(_m2*_m2) - 3 + 6.0/(_n + 1)); } + } + + /// + /// Evaluates the population 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. + /// + public double PopulationKurtosis + { + get { return _n < 3 ? double.NaN : (_m4*_n - 3*_m2*_m2)/(_m2*_m2); } + } + + /// + /// Update the running statistics by adding another observed sample (in-place). + /// + public void Push(double value) + { + _n++; + double d = value - _m1; + double s = d/_n; + double s2 = s*s; + double t = d*s*(_n - 1); + + _m1 += s; + _m4 += t*s2*(_n*_n - 3*_n + 3) + 6*s2*_m2 - 4*s*_m3; + _m3 += t*s*(_n - 2) - 3*s*_m2; + _m2 += t; + + if (_min > value) + { + _min = value; + } + if (_max < value) + { + _max = value; + } + } + + /// + /// Update the running statistics by adding a sequence of observed sample (in-place). + /// + public void PushRange(IEnumerable values) + { + foreach (double value in values) + { + Push(value); + } + } + + /// + /// Create a new running statistics over the combined samples of two existing running statistics. + /// + public static RunningStatistics Combine(RunningStatistics a, RunningStatistics b) + { + long n = a._n + b._n; + double d = b._m1 - a._m1; + double d2 = d*d; + double d3 = d2*d; + double d4 = d2*d2; + + 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; + 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; + + return new RunningStatistics { _n = n, _m1 = m1, _m2 = m2, _m3 = m3, _m4 = m4 }; + } + + public static RunningStatistics operator +(RunningStatistics a, RunningStatistics b) + { + return Combine(a, b); + } + } +} diff --git a/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs b/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs new file mode 100644 index 00000000..cc8e7d03 --- /dev/null +++ b/src/UnitTests/StatisticsTests/RunningStatisticsTests.cs @@ -0,0 +1,170 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2014 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.UnitTests.StatisticsTests +{ +#if !PORTABLE + using System.Collections.Generic; + using NUnit.Framework; + using Statistics; + + /// + /// Running statistics tests. + /// + /// NOTE: this class is not included into Silverlight version, because it uses data from local files. + /// In Silverlight access to local files is forbidden, except several cases. + [TestFixture, Category("Statistics")] + public class RunningStatisticsTests + { + /// + /// Statistics data. + /// + readonly IDictionary _data = new Dictionary(); + + /// + /// Initializes a new instance of the DescriptiveStatisticsTests class. + /// + public RunningStatisticsTests() + { + _data.Add("lottery", new StatTestData("./data/NIST/Lottery.dat")); + _data.Add("lew", new StatTestData("./data/NIST/Lew.dat")); + _data.Add("mavro", new StatTestData("./data/NIST/Mavro.dat")); + _data.Add("michelso", new StatTestData("./data/NIST/Michelso.dat")); + _data.Add("numacc1", new StatTestData("./data/NIST/NumAcc1.dat")); + _data.Add("numacc2", new StatTestData("./data/NIST/NumAcc2.dat")); + _data.Add("numacc3", new StatTestData("./data/NIST/NumAcc3.dat")); + _data.Add("numacc4", new StatTestData("./data/NIST/NumAcc4.dat")); + _data.Add("meixner", new StatTestData("./data/NIST/Meixner.dat")); + } + + /// + /// IEnumerable Double. + /// + /// Dataset name. + /// Digits count. + /// Skewness value. + /// Kurtosis value. + /// Median value. + /// Min value. + /// Max value. + /// Count value. + [TestCase("lottery", 14, -0.09333165310779, -1.19256091074856, 522.5, 4, 999, 218)] + [TestCase("lew", 14, -0.050606638756334, -1.49604979214447, -162, -579, 300, 200)] + [TestCase("mavro", 11, 0.64492948110824, -0.82052379677456, 2.0018, 2.0013, 2.0027, 50)] + [TestCase("michelso", 11, -0.0185388637725746, 0.33968459842539, 299.85, 299.62, 300.07, 100)] + [TestCase("numacc1", 15, 0, double.NaN, 10000002, 10000001, 10000003, 3)] + [TestCase("numacc2", 13, 0, -2.003003003003, 1.2, 1.1, 1.3, 1001)] + [TestCase("numacc3", 9, 0, -2.003003003003, 1000000.2, 1000000.1, 1000000.3, 1001)] + [TestCase("numacc4", 7, 0, -2.00300300299913, 10000000.2, 10000000.1, 10000000.3, 1001)] + [TestCase("meixner", 8, -0.016649617280859657, 0.8171318629552635, -0.002042931016531602, -4.825626912281697, 5.3018298664184913, 10000)] + public void ConsistentWithNist(string dataSet, int digits, double skewness, double kurtosis, double median, double min, double max, int count) + { + var data = _data[dataSet]; + var stats = new RunningStatistics(data.Data); + + AssertHelpers.AlmostEqualRelative(data.Mean, stats.Mean, 10); + AssertHelpers.AlmostEqualRelative(data.StandardDeviation, stats.StandardDeviation, digits); + AssertHelpers.AlmostEqualRelative(skewness, stats.Skewness, 8); + AssertHelpers.AlmostEqualRelative(kurtosis, stats.Kurtosis, 8); + Assert.AreEqual(stats.Minimum, min); + Assert.AreEqual(stats.Maximum, max); + Assert.AreEqual(stats.Count, count); + } + + [TestCase("lottery", 1e-8, -0.09268823, -0.09333165)] + [TestCase("lew", 1e-8, -0.0502263, -0.05060664)] + [TestCase("mavro", 1e-6, 0.6254181, 0.6449295)] + [TestCase("michelso", 1e-8, -0.01825961, -0.01853886)] + [TestCase("numacc1", 1e-8, 0, 0)] + //[TestCase("numacc2", 1e-20, 3.254232e-15, 3.259118e-15)] TODO: accuracy + //[TestCase("numacc3", 1e-14, 1.747103e-09, 1.749726e-09)] TODO: accuracy + //[TestCase("numacc4", 1e-13, 2.795364e-08, 2.799561e-08)] TODO: accuracy + [TestCase("meixner", 1e-8, -0.01664712, -0.01664962)] + public void SkewnessConsistentWithR_e1071(string dataSet, double delta, double skewnessType1, double skewnessType2) + { + var data = _data[dataSet]; + var stats = new RunningStatistics(data.Data); + + Assert.That(stats.Skewness, Is.EqualTo(skewnessType2).Within(delta), "Skewness"); + Assert.That(stats.PopulationSkewness, Is.EqualTo(skewnessType1).Within(delta), "PopulationSkewness"); + } + + [TestCase("lottery", -1.192781, -1.192561)] + [TestCase("lew", -1.48876, -1.49605)] + [TestCase("mavro", -0.858384, -0.8205238)] + [TestCase("michelso", 0.2635305, 0.3396846)] + [TestCase("numacc1", -1.5, double.NaN)] + [TestCase("numacc2", -1.999, -2.003003)] + [TestCase("numacc3", -1.999, -2.003003)] + [TestCase("numacc4", -1.999, -2.003003)] + [TestCase("meixner", 0.8161234, 0.8171319)] + public void KurtosisConsistentWithR_e1071(string dataSet, double kurtosisType1, double kurtosisType2) + { + var data = _data[dataSet]; + var stats = new RunningStatistics(data.Data); + + Assert.That(stats.Kurtosis, Is.EqualTo(kurtosisType2).Within(1e-6), "Kurtosis"); + Assert.That(stats.PopulationKurtosis, Is.EqualTo(kurtosisType1).Within(1e-6), "PopulationKurtosis"); + } + + [Test] + public void ShortSequences() + { + var stats0 = new RunningStatistics(new double[0]); + Assert.That(stats0.Skewness, Is.NaN); + Assert.That(stats0.Kurtosis, Is.NaN); + + var stats1 = new RunningStatistics(new[] { 1.0 }); + Assert.That(stats1.Skewness, Is.NaN); + Assert.That(stats1.Kurtosis, Is.NaN); + + var stats2 = new RunningStatistics(new[] { 1.0, 2.0 }); + Assert.That(stats2.Skewness, Is.NaN); + Assert.That(stats2.Kurtosis, Is.NaN); + + var stats3 = new RunningStatistics(new[] { 1.0, 2.0, -3.0 }); + Assert.That(stats3.Skewness, Is.Not.NaN); + Assert.That(stats3.Kurtosis, Is.NaN); + + var stats4 = new RunningStatistics(new[] { 1.0, 2.0, -3.0, -4.0 }); + Assert.That(stats4.Skewness, Is.Not.NaN); + Assert.That(stats4.Kurtosis, Is.Not.NaN); + } + + [Test] + public void ZeroVarianceSequence() + { + var stats = new RunningStatistics(new[] { 2.0, 2.0, 2.0, 2.0 }); + Assert.That(stats.Skewness, Is.NaN); + Assert.That(stats.Kurtosis, Is.NaN); + } + } +#endif +} diff --git a/src/UnitTests/StatisticsTests/StatisticsTests.cs b/src/UnitTests/StatisticsTests/StatisticsTests.cs index 690afc8d..a55b6083 100644 --- a/src/UnitTests/StatisticsTests/StatisticsTests.cs +++ b/src/UnitTests/StatisticsTests/StatisticsTests.cs @@ -113,6 +113,9 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(() => StreamingStatistics.PopulationStandardDeviation(data), Throws.Exception.TypeOf()); Assert.That(() => StreamingStatistics.Covariance(data, data), Throws.Exception.TypeOf()); Assert.That(() => StreamingStatistics.PopulationCovariance(data, data), Throws.Exception.TypeOf()); + + Assert.That(() => new RunningStatistics(data), Throws.Exception); + Assert.That(() => new RunningStatistics().PushRange(data), Throws.Exception); } [Test] @@ -167,6 +170,20 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.DoesNotThrow(() => StreamingStatistics.PopulationStandardDeviation(data)); Assert.DoesNotThrow(() => StreamingStatistics.Covariance(data, data)); Assert.DoesNotThrow(() => StreamingStatistics.PopulationCovariance(data, data)); + + Assert.That(() => new RunningStatistics(data), Throws.Nothing); + Assert.That(() => new RunningStatistics().PushRange(data), Throws.Nothing); + Assert.That(() => new RunningStatistics(data).Minimum, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).Maximum, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).Mean, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).Variance, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).StandardDeviation, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).Skewness, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).Kurtosis, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).PopulationVariance, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).PopulationStandardDeviation, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).PopulationSkewness, Throws.Nothing); + Assert.That(() => new RunningStatistics(data).PopulationKurtosis, Throws.Nothing); } [TestCase("lottery")] @@ -186,6 +203,7 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests AssertHelpers.AlmostEqualRelative(data.Mean, Statistics.MeanVariance(data.Data).Item1, 14); AssertHelpers.AlmostEqualRelative(data.Mean, ArrayStatistics.MeanVariance(data.Data).Item1, 14); AssertHelpers.AlmostEqualRelative(data.Mean, StreamingStatistics.MeanVariance(data.Data).Item1, 14); + AssertHelpers.AlmostEqualRelative(data.Mean, new RunningStatistics(data.Data).Mean, 14); } [TestCase("lottery")] @@ -219,6 +237,7 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests AssertHelpers.AlmostEqualRelative(data.StandardDeviation, Math.Sqrt(Statistics.MeanVariance(data.Data).Item2), digits); AssertHelpers.AlmostEqualRelative(data.StandardDeviation, Math.Sqrt(ArrayStatistics.MeanVariance(data.Data).Item2), digits); AssertHelpers.AlmostEqualRelative(data.StandardDeviation, Math.Sqrt(StreamingStatistics.MeanVariance(data.Data).Item2), digits); + AssertHelpers.AlmostEqualRelative(data.StandardDeviation, new RunningStatistics(data.Data).StandardDeviation, digits); } [TestCase("lottery", 14)] @@ -245,6 +264,8 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(ArrayStatistics.Maximum(samples), Is.EqualTo(10), "Max"); Assert.That(StreamingStatistics.Minimum(samples), Is.EqualTo(-3), "Min"); Assert.That(StreamingStatistics.Maximum(samples), Is.EqualTo(10), "Max"); + Assert.That(new RunningStatistics(samples).Minimum, Is.EqualTo(-3), "Min"); + Assert.That(new RunningStatistics(samples).Maximum, Is.EqualTo(10), "Max"); Array.Sort(samples); Assert.That(SortedArrayStatistics.Minimum(samples), Is.EqualTo(-3), "Min"); @@ -762,6 +783,10 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests AssertHelpers.AlmostEqualRelative(1e+9, StreamingStatistics.Mean(gaussian.Samples().Take(10000)), 10); AssertHelpers.AlmostEqualRelative(4d, StreamingStatistics.Variance(gaussian.Samples().Take(10000)), 0); AssertHelpers.AlmostEqualRelative(2d, StreamingStatistics.StandardDeviation(gaussian.Samples().Take(10000)), 1); + + AssertHelpers.AlmostEqualRelative(1e+9, new RunningStatistics(gaussian.Samples().Take(10000)).Mean, 10); + AssertHelpers.AlmostEqualRelative(4d, new RunningStatistics(gaussian.Samples().Take(10000)).Variance, 0); + AssertHelpers.AlmostEqualRelative(2d, new RunningStatistics(gaussian.Samples().Take(10000)).StandardDeviation, 1); } [TestCase("lottery")] @@ -832,7 +857,9 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(SortedArrayStatistics.Minimum(new double[0]), Is.NaN); Assert.That(SortedArrayStatistics.Minimum(new[] { 2d }), Is.Not.NaN); Assert.That(StreamingStatistics.Minimum(new double[0]), Is.NaN); - Assert.That(StreamingStatistics.Minimum(new[] {2d }), Is.Not.NaN); + Assert.That(StreamingStatistics.Minimum(new[] { 2d }), Is.Not.NaN); + Assert.That(new RunningStatistics(new double[0]).Minimum, Is.NaN); + Assert.That(new RunningStatistics(new[] { 2d }).Minimum, Is.Not.NaN); } [Test] @@ -846,6 +873,8 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(SortedArrayStatistics.Maximum(new[] { 2d }), Is.Not.NaN); Assert.That(StreamingStatistics.Maximum(new double[0]), Is.NaN); Assert.That(StreamingStatistics.Maximum(new[] { 2d }), Is.Not.NaN); + Assert.That(new RunningStatistics(new double[0]).Maximum, Is.NaN); + Assert.That(new RunningStatistics(new[] { 2d }).Maximum, Is.Not.NaN); } [Test] @@ -857,6 +886,8 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(ArrayStatistics.Mean(new[] { 2d }), Is.Not.NaN); Assert.That(StreamingStatistics.Mean(new double[0]), Is.NaN); Assert.That(StreamingStatistics.Mean(new[] { 2d }), Is.Not.NaN); + Assert.That(new RunningStatistics(new double[0]).Mean, Is.NaN); + Assert.That(new RunningStatistics(new[] { 2d }).Mean, Is.Not.NaN); } [Test] @@ -871,6 +902,8 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(StreamingStatistics.Variance(new double[0]), Is.NaN); Assert.That(StreamingStatistics.Variance(new[] { 2d }), Is.NaN); Assert.That(StreamingStatistics.Variance(new[] { 2d, 3d }), Is.Not.NaN); + Assert.That(new RunningStatistics(new[] { 2d }).Variance, Is.NaN); + Assert.That(new RunningStatistics(new[] { 2d, 3d }).Variance, Is.Not.NaN); } [Test] @@ -885,6 +918,8 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.That(StreamingStatistics.PopulationVariance(new double[0]), Is.NaN); Assert.That(StreamingStatistics.PopulationVariance(new[] { 2d }), Is.Not.NaN); Assert.That(StreamingStatistics.PopulationVariance(new[] { 2d, 3d }), Is.Not.NaN); + Assert.That(new RunningStatistics(new[] { 2d }).PopulationVariance, Is.NaN); + Assert.That(new RunningStatistics(new[] { 2d, 3d }).PopulationVariance, Is.Not.NaN); } /// @@ -911,6 +946,9 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests Assert.AreEqual(21.578697, a.Variance(), 1e-5); Assert.AreEqual(21.578231, a.PopulationVariance(), 1e-5); + + Assert.AreEqual(21.578697, new RunningStatistics(a).Variance, 1e-5); + Assert.AreEqual(21.578231, new RunningStatistics(a).PopulationVariance, 1e-5); } [Test] diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 9dba09e9..7cf5ee5e 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -392,6 +392,7 @@ +