diff --git a/src/Numerics/Statistics/MovingStatistics.cs b/src/Numerics/Statistics/MovingStatistics.cs index 6e97b720..71c0ee44 100644 --- a/src/Numerics/Statistics/MovingStatistics.cs +++ b/src/Numerics/Statistics/MovingStatistics.cs @@ -3,9 +3,9 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com -// +// // Copyright (c) 2009-2015 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 @@ -14,10 +14,10 @@ // 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 @@ -41,7 +41,12 @@ namespace MathNet.Numerics.Statistics { readonly double[] _oldValues; readonly int _windowSize; + + long _count; + long _totalCountOffset; int _lastIndex; + int _lastNaNTimeToLive; + double _m1; double _m2; double _m3; @@ -72,7 +77,10 @@ namespace MathNet.Numerics.Statistics /// /// Gets the total number of samples. /// - public long Count { get; private set; } + public long Count + { + get { return _totalCountOffset + _count; } + } /// /// Returns the minimum value in the sample data. @@ -80,7 +88,7 @@ namespace MathNet.Numerics.Statistics /// public double Minimum { - get { return Count > 0 ? _min : double.NaN; } + get { return _count > 0 && _lastNaNTimeToLive == 0 ? _min : double.NaN; } } /// @@ -89,7 +97,7 @@ namespace MathNet.Numerics.Statistics /// public double Maximum { - get { return Count > 0 ? _max : double.NaN; } + get { return _count > 0 && _lastNaNTimeToLive == 0 ? _max : double.NaN; } } /// @@ -98,7 +106,7 @@ namespace MathNet.Numerics.Statistics /// public double Mean { - get { return Count > 0 ? _m1 : double.NaN; } + get { return _count > 0 && _lastNaNTimeToLive == 0 ? _m1 : double.NaN; } } /// @@ -108,7 +116,7 @@ namespace MathNet.Numerics.Statistics /// public double Variance { - get { return Count < 2 ? double.NaN : _m2/(Count - 1); } + get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : _m2/(_count - 1); } } /// @@ -118,7 +126,7 @@ namespace MathNet.Numerics.Statistics /// public double PopulationVariance { - get { return Count < 2 ? double.NaN : _m2/Count; } + get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : _m2/_count; } } /// @@ -128,7 +136,7 @@ namespace MathNet.Numerics.Statistics /// public double StandardDeviation { - get { return Count < 2 ? double.NaN : Math.Sqrt(_m2/(Count - 1)); } + get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : Math.Sqrt(_m2/(_count - 1)); } } /// @@ -138,7 +146,7 @@ namespace MathNet.Numerics.Statistics /// public double PopulationStandardDeviation { - get { return Count < 2 ? double.NaN : Math.Sqrt(_m2/Count); } + get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : Math.Sqrt(_m2/_count); } } /* /// @@ -186,26 +194,46 @@ namespace MathNet.Numerics.Statistics /// public void Push(double value) { - if (Count < _windowSize) + if (double.IsNaN(value)) + { + _totalCountOffset += _count + 1; + _count = 0; + _lastNaNTimeToLive = _windowSize; + + _m1 = 0.0; + _m2 = 0.0; + _m3 = 0.0; + _m4 = 0.0; + _max = double.NegativeInfinity; + _min = double.PositiveInfinity; + return; + } + + if (_lastNaNTimeToLive > 0) + { + _lastNaNTimeToLive--; + } + + if (_count < _windowSize) { - _oldValues[Count] = value; - Count++; + _oldValues[_count] = value; + _count++; var d = value - _m1; - var s = d/Count; + var s = d/_count; // var s2 = s * s; - var t = d*s*(Count - 1); + var t = d*s*(_count - 1); _m1 += s; // _m4 += t * s2 * (Count * Count - 3 * Count + 3) + 6 * s2 * _m2 - 4 * s * _m3; // _m3 += t * s * (Count - 2) - 3 * s * _m2; _m2 += t; - if (value < _min || double.IsNaN(value)) + if (value < _min) { _min = value; } - if (value > _max || double.IsNaN(value)) + if (value > _max) { _max = value; } @@ -214,7 +242,7 @@ namespace MathNet.Numerics.Statistics { var oldValue = _oldValues[_lastIndex]; var d = value - oldValue; - var s = d/Count; + var s = d/_count; // var s2 = s * s; var oldM1 = _m1; _m1 += s; @@ -232,8 +260,8 @@ namespace MathNet.Numerics.Statistics { _lastIndex = 0; } - _max = value > _max || double.IsNaN(value) ? value : _oldValues.Maximum(); - _min = value < _min || double.IsNaN(value)? value : _oldValues.Minimum(); + _max = value > _max ? value : _oldValues.Maximum(); + _min = value < _min ? value : _oldValues.Minimum(); } } diff --git a/src/UnitTests/StatisticsTests/MovingStatisticsTests.cs b/src/UnitTests/StatisticsTests/MovingStatisticsTests.cs index 60d77f07..3753d706 100644 --- a/src/UnitTests/StatisticsTests/MovingStatisticsTests.cs +++ b/src/UnitTests/StatisticsTests/MovingStatisticsTests.cs @@ -3,9 +3,9 @@ // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com -// +// // Copyright (c) 2009-2015 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 @@ -14,10 +14,10 @@ // 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 @@ -28,10 +28,6 @@ // OTHER DEALINGS IN THE SOFTWARE. // -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; using MathNet.Numerics.Distributions; using MathNet.Numerics.Random; using MathNet.Numerics.Statistics; @@ -39,17 +35,18 @@ using NUnit.Framework; namespace MathNet.Numerics.UnitTests.StatisticsTests { -#if !PORTABLE [TestFixture, Category("Statistics")] public class MovingStatisticsTests { [Test] - public void QuickTest() + public void StabilityTest() { var data = new double[1000000]; - (new Normal(50, 10, new SystemRandomSource(0))).Samples(data); + Normal.Samples(new SystemRandomSource(0), data, 50, 10); + var ms = new MovingStatistics(5, data); ms.PushRange(new[] { 11.11, 22.22, 33.33, 44.44, 55.55 }); + Assert.AreEqual(5, ms.Count); Assert.AreEqual(11.11, ms.Minimum); Assert.AreEqual(55.55, ms.Maximum); @@ -61,6 +58,51 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests //AssertHelpers.AlmostEqualRelative(stats0.Variance, ms.Variance, 14); //AssertHelpers.AlmostEqualRelative(stats0.StandardDeviation, ms.StandardDeviation, 14); } + + [Test] + public void NaNTest() + { + var ms = new MovingStatistics(3); + Assert.That(ms.Minimum, Is.NaN); + Assert.That(ms.Maximum, Is.NaN); + Assert.That(ms.Mean, Is.NaN); + Assert.That(ms.StandardDeviation, Is.NaN); + + ms.Push(1.0); + Assert.That(ms.Minimum, Is.Not.NaN); + Assert.That(ms.Maximum, Is.Not.NaN); + Assert.That(ms.Mean, Is.Not.NaN); + Assert.That(ms.StandardDeviation, Is.NaN); + + ms.Push(2.0); + Assert.That(ms.Minimum, Is.Not.NaN); + Assert.That(ms.Maximum, Is.Not.NaN); + Assert.That(ms.Mean, Is.Not.NaN); + Assert.That(ms.StandardDeviation, Is.Not.NaN); + + ms.Push(double.NaN); + Assert.That(ms.Minimum, Is.NaN); + Assert.That(ms.Maximum, Is.NaN); + Assert.That(ms.Mean, Is.NaN); + Assert.That(ms.StandardDeviation, Is.NaN); + + ms.Push(1.0); + Assert.That(ms.Minimum, Is.NaN); + Assert.That(ms.Maximum, Is.NaN); + Assert.That(ms.Mean, Is.NaN); + Assert.That(ms.StandardDeviation, Is.NaN); + + ms.Push(2.0); + Assert.That(ms.Minimum, Is.NaN); + Assert.That(ms.Maximum, Is.NaN); + Assert.That(ms.Mean, Is.NaN); + Assert.That(ms.StandardDeviation, Is.NaN); + + ms.Push(3.0); + Assert.That(ms.Minimum, Is.Not.NaN); + Assert.That(ms.Maximum, Is.Not.NaN); + Assert.That(ms.Mean, Is.Not.NaN); + Assert.That(ms.StandardDeviation, Is.Not.NaN); + } } -#endif }