Browse Source

Statistics: MovingStatistics should handle NaN such that it only affects its window

cuda
Christoph Ruegg 12 years ago
parent
commit
ac29a58b3d
  1. 72
      src/Numerics/Statistics/MovingStatistics.cs
  2. 66
      src/UnitTests/StatisticsTests/MovingStatisticsTests.cs

72
src/Numerics/Statistics/MovingStatistics.cs

@ -3,9 +3,9 @@
// http://numerics.mathdotnet.com // http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics // http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com // http://mathnetnumerics.codeplex.com
// //
// Copyright (c) 2009-2015 Math.NET // Copyright (c) 2009-2015 Math.NET
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without // files (the "Software"), to deal in the Software without
@ -14,10 +14,10 @@
// copies of the Software, and to permit persons to whom the // copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following // Software is furnished to do so, subject to the following
// conditions: // conditions:
// //
// The above copyright notice and this permission notice shall be // The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. // included in all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@ -41,7 +41,12 @@ namespace MathNet.Numerics.Statistics
{ {
readonly double[] _oldValues; readonly double[] _oldValues;
readonly int _windowSize; readonly int _windowSize;
long _count;
long _totalCountOffset;
int _lastIndex; int _lastIndex;
int _lastNaNTimeToLive;
double _m1; double _m1;
double _m2; double _m2;
double _m3; double _m3;
@ -72,7 +77,10 @@ namespace MathNet.Numerics.Statistics
/// <summary> /// <summary>
/// Gets the total number of samples. /// Gets the total number of samples.
/// </summary> /// </summary>
public long Count { get; private set; } public long Count
{
get { return _totalCountOffset + _count; }
}
/// <summary> /// <summary>
/// Returns the minimum value in the sample data. /// Returns the minimum value in the sample data.
@ -80,7 +88,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Minimum public double Minimum
{ {
get { return Count > 0 ? _min : double.NaN; } get { return _count > 0 && _lastNaNTimeToLive == 0 ? _min : double.NaN; }
} }
/// <summary> /// <summary>
@ -89,7 +97,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Maximum public double Maximum
{ {
get { return Count > 0 ? _max : double.NaN; } get { return _count > 0 && _lastNaNTimeToLive == 0 ? _max : double.NaN; }
} }
/// <summary> /// <summary>
@ -98,7 +106,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Mean public double Mean
{ {
get { return Count > 0 ? _m1 : double.NaN; } get { return _count > 0 && _lastNaNTimeToLive == 0 ? _m1 : double.NaN; }
} }
/// <summary> /// <summary>
@ -108,7 +116,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Variance public double Variance
{ {
get { return Count < 2 ? double.NaN : _m2/(Count - 1); } get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : _m2/(_count - 1); }
} }
/// <summary> /// <summary>
@ -118,7 +126,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double PopulationVariance public double PopulationVariance
{ {
get { return Count < 2 ? double.NaN : _m2/Count; } get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : _m2/_count; }
} }
/// <summary> /// <summary>
@ -128,7 +136,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double StandardDeviation 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)); }
} }
/// <summary> /// <summary>
@ -138,7 +146,7 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double PopulationStandardDeviation 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); }
} }
/* /// <summary> /* /// <summary>
@ -186,26 +194,46 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public void Push(double value) 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; _oldValues[_count] = value;
Count++; _count++;
var d = value - _m1; var d = value - _m1;
var s = d/Count; var s = d/_count;
// var s2 = s * s; // var s2 = s * s;
var t = d*s*(Count - 1); var t = d*s*(_count - 1);
_m1 += s; _m1 += s;
// _m4 += t * s2 * (Count * Count - 3 * Count + 3) + 6 * s2 * _m2 - 4 * s * _m3; // _m4 += t * s2 * (Count * Count - 3 * Count + 3) + 6 * s2 * _m2 - 4 * s * _m3;
// _m3 += t * s * (Count - 2) - 3 * s * _m2; // _m3 += t * s * (Count - 2) - 3 * s * _m2;
_m2 += t; _m2 += t;
if (value < _min || double.IsNaN(value)) if (value < _min)
{ {
_min = value; _min = value;
} }
if (value > _max || double.IsNaN(value)) if (value > _max)
{ {
_max = value; _max = value;
} }
@ -214,7 +242,7 @@ namespace MathNet.Numerics.Statistics
{ {
var oldValue = _oldValues[_lastIndex]; var oldValue = _oldValues[_lastIndex];
var d = value - oldValue; var d = value - oldValue;
var s = d/Count; var s = d/_count;
// var s2 = s * s; // var s2 = s * s;
var oldM1 = _m1; var oldM1 = _m1;
_m1 += s; _m1 += s;
@ -232,8 +260,8 @@ namespace MathNet.Numerics.Statistics
{ {
_lastIndex = 0; _lastIndex = 0;
} }
_max = value > _max || double.IsNaN(value) ? value : _oldValues.Maximum(); _max = value > _max ? value : _oldValues.Maximum();
_min = value < _min || double.IsNaN(value)? value : _oldValues.Minimum(); _min = value < _min ? value : _oldValues.Minimum();
} }
} }

66
src/UnitTests/StatisticsTests/MovingStatisticsTests.cs

@ -3,9 +3,9 @@
// http://numerics.mathdotnet.com // http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics // http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com // http://mathnetnumerics.codeplex.com
// //
// Copyright (c) 2009-2015 Math.NET // Copyright (c) 2009-2015 Math.NET
// //
// Permission is hereby granted, free of charge, to any person // Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation // obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without // files (the "Software"), to deal in the Software without
@ -14,10 +14,10 @@
// copies of the Software, and to permit persons to whom the // copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following // Software is furnished to do so, subject to the following
// conditions: // conditions:
// //
// The above copyright notice and this permission notice shall be // The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software. // included in all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@ -28,10 +28,6 @@
// OTHER DEALINGS IN THE SOFTWARE. // OTHER DEALINGS IN THE SOFTWARE.
// </copyright> // </copyright>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using MathNet.Numerics.Distributions; using MathNet.Numerics.Distributions;
using MathNet.Numerics.Random; using MathNet.Numerics.Random;
using MathNet.Numerics.Statistics; using MathNet.Numerics.Statistics;
@ -39,17 +35,18 @@ using NUnit.Framework;
namespace MathNet.Numerics.UnitTests.StatisticsTests namespace MathNet.Numerics.UnitTests.StatisticsTests
{ {
#if !PORTABLE
[TestFixture, Category("Statistics")] [TestFixture, Category("Statistics")]
public class MovingStatisticsTests public class MovingStatisticsTests
{ {
[Test] [Test]
public void QuickTest() public void StabilityTest()
{ {
var data = new double[1000000]; 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); var ms = new MovingStatistics(5, data);
ms.PushRange(new[] { 11.11, 22.22, 33.33, 44.44, 55.55 }); ms.PushRange(new[] { 11.11, 22.22, 33.33, 44.44, 55.55 });
Assert.AreEqual(5, ms.Count); Assert.AreEqual(5, ms.Count);
Assert.AreEqual(11.11, ms.Minimum); Assert.AreEqual(11.11, ms.Minimum);
Assert.AreEqual(55.55, ms.Maximum); 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.Variance, ms.Variance, 14);
//AssertHelpers.AlmostEqualRelative(stats0.StandardDeviation, ms.StandardDeviation, 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
} }

Loading…
Cancel
Save