Browse Source

added support for infinity

cuda
Marcus Cuda 12 years ago
parent
commit
644ea702de
  1. 218
      src/Numerics/Statistics/MovingStatistics.cs
  2. 155
      src/UnitTests/StatisticsTests/MovingStatisticsTests.cs

218
src/Numerics/Statistics/MovingStatistics.cs

@ -46,11 +46,11 @@ namespace MathNet.Numerics.Statistics
long _totalCountOffset; long _totalCountOffset;
int _lastIndex; int _lastIndex;
int _lastNaNTimeToLive; int _lastNaNTimeToLive;
int _lastPosInfTimeToLive;
int _lastNegInfTimeToLive;
double _m1; double _m1;
double _m2; double _m2;
double _m3;
double _m4;
double _max = double.NegativeInfinity; double _max = double.NegativeInfinity;
double _min = double.PositiveInfinity; double _min = double.PositiveInfinity;
@ -64,7 +64,8 @@ namespace MathNet.Numerics.Statistics
_oldValues = new double[_windowSize]; _oldValues = new double[_windowSize];
} }
public MovingStatistics(int windowSize, IEnumerable<double> values) : this(windowSize) public MovingStatistics(int windowSize, IEnumerable<double> values)
: this(windowSize)
{ {
PushRange(values); PushRange(values);
} }
@ -88,7 +89,20 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Minimum public double Minimum
{ {
get { return _count > 0 && _lastNaNTimeToLive == 0 ? _min : double.NaN; } get
{
if (_lastNaNTimeToLive > 0)
{
return double.NaN;
}
if (_lastNegInfTimeToLive > 0)
{
return double.NegativeInfinity;
}
return (_count > 0 || _lastPosInfTimeToLive > 0) ? _min : double.NaN;
}
} }
/// <summary> /// <summary>
@ -97,7 +111,20 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Maximum public double Maximum
{ {
get { return _count > 0 && _lastNaNTimeToLive == 0 ? _max : double.NaN; } get
{
if (_lastNaNTimeToLive > 0)
{
return double.NaN;
}
if (_lastPosInfTimeToLive > 0)
{
return double.PositiveInfinity;
}
return (_count > 0 || _lastNegInfTimeToLive > 0) ? _max : double.NaN;
}
} }
/// <summary> /// <summary>
@ -106,7 +133,25 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Mean public double Mean
{ {
get { return _count > 0 && _lastNaNTimeToLive == 0 ? _m1 : double.NaN; } get
{
if (_lastNaNTimeToLive > 0 || (_lastPosInfTimeToLive > 0 && _lastNegInfTimeToLive > 0))
{
return double.NaN;
}
if (_lastPosInfTimeToLive > 0)
{
return double.PositiveInfinity;
}
if (_lastNegInfTimeToLive > 0)
{
return double.NegativeInfinity;
}
return _count == 0 ? double.NaN : _m1;
}
} }
/// <summary> /// <summary>
@ -116,7 +161,20 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double Variance public double Variance
{ {
get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : _m2/(_count - 1); } get
{
if (_lastNaNTimeToLive > 0 || _lastNegInfTimeToLive > 0 || (_lastPosInfTimeToLive > 0 && _lastNegInfTimeToLive > 0))
{
return double.NaN;
}
if (_lastPosInfTimeToLive > 0)
{
return double.PositiveInfinity;
}
return _count < 2 ? double.NaN : _m2 / (_count - 1);
}
} }
/// <summary> /// <summary>
@ -126,7 +184,20 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double PopulationVariance public double PopulationVariance
{ {
get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : _m2/_count; } get
{
if (_lastNaNTimeToLive > 0 || _lastNegInfTimeToLive > 0 || (_lastPosInfTimeToLive > 0 && _lastNegInfTimeToLive > 0))
{
return double.NaN;
}
if (_lastPosInfTimeToLive > 0)
{
return double.PositiveInfinity;
}
return _count < 2 ? double.NaN : _m2 / _count;
}
} }
/// <summary> /// <summary>
@ -136,7 +207,20 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double StandardDeviation public double StandardDeviation
{ {
get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : Math.Sqrt(_m2/(_count - 1)); } get
{
if (_lastNaNTimeToLive > 0 || _lastNegInfTimeToLive > 0 || (_lastPosInfTimeToLive > 0 && _lastNegInfTimeToLive > 0))
{
return double.NaN;
}
if (_lastPosInfTimeToLive > 0)
{
return double.PositiveInfinity;
}
return _count < 2 ? double.NaN : Math.Sqrt(_m2 / (_count - 1));
}
} }
/// <summary> /// <summary>
@ -146,72 +230,48 @@ namespace MathNet.Numerics.Statistics
/// </summary> /// </summary>
public double PopulationStandardDeviation public double PopulationStandardDeviation
{ {
get { return _count < 2 || _lastNaNTimeToLive > 0 ? double.NaN : Math.Sqrt(_m2/_count); } get
} {
if (_lastNaNTimeToLive > 0 || _lastNegInfTimeToLive > 0 || (_lastPosInfTimeToLive > 0 && _lastNegInfTimeToLive > 0))
/* /// <summary> {
/// Estimates the unbiased population skewness from the provided samples. return double.NaN;
/// Uses a normalizer (Bessel's correction; type 2). }
/// Returns NaN if data has less than three entries or if any entry is NaN.
/// </summary>
public double Skewness
{
get { return Count < 3 ? double.NaN : (Count*_m3*Math.Sqrt(_m2/(Count - 1))/(_m2*_m2*(Count - 2)))*(Count - 1); }
}
/// <summary> if (_lastPosInfTimeToLive > 0)
/// 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). return double.PositiveInfinity;
/// Returns NaN if data has less than two entries or if any entry is NaN. }
/// </summary>
public double PopulationSkewness
{
get { return Count < 2 ? double.NaN : Math.Sqrt(Count)*_m3/Math.Pow(_m2, 1.5); }
}
/// <summary> return _count < 2 ? double.NaN : Math.Sqrt(_m2 / _count);
/// 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.
/// </summary>
public double Kurtosis
{
get { return Count < 4 ? double.NaN : ((double) Count*Count - 1)/((Count - 2)*(Count - 3))*(Count*_m4/(_m2*_m2) - 3 + 6.0/(Count + 1)); }
} }
/// <summary>
/// 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.
/// </summary>
public double PopulationKurtosis
{
get { return Count < 3 ? double.NaN : Count*_m4/(_m2*_m2) - 3.0; }
}*/
/// <summary> /// <summary>
/// Update the running statistics by adding another observed sample (in-place). /// Update the running statistics by adding another observed sample (in-place).
/// </summary> /// </summary>
public void Push(double value) public void Push(double value)
{ {
DecrementTimeToLive();
if (double.IsNaN(value)) if (double.IsNaN(value))
{ {
_totalCountOffset += _count + 1;
_count = 0;
_lastNaNTimeToLive = _windowSize; _lastNaNTimeToLive = _windowSize;
Reset(double.PositiveInfinity, double.NegativeInfinity);
return;
}
_m1 = 0.0; if (double.IsPositiveInfinity(value))
_m2 = 0.0; {
_m3 = 0.0; _lastPosInfTimeToLive = _windowSize;
_m4 = 0.0; Reset(_min, double.NegativeInfinity);
_max = double.NegativeInfinity;
_min = double.PositiveInfinity;
return; return;
} }
if (_lastNaNTimeToLive > 0) if (double.IsNegativeInfinity(value))
{ {
_lastNaNTimeToLive--; _lastNegInfTimeToLive = _windowSize;
Reset(double.PositiveInfinity, _max);
return;
} }
if (_count < _windowSize) if (_count < _windowSize)
@ -219,13 +279,10 @@ namespace MathNet.Numerics.Statistics
_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 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;
// _m3 += t * s * (Count - 2) - 3 * s * _m2;
_m2 += t; _m2 += t;
if (value < _min) if (value < _min)
@ -242,18 +299,14 @@ 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 oldM1 = _m1; var oldM1 = _m1;
_m1 += s; _m1 += s;
var x = (value - _m1 + oldValue - oldM1); var x = (value - _m1 + oldValue - oldM1);
var t = d*x; var t = d * x;
_m2 += t; _m2 += t;
// _m4 += t * s2 * (Count * Count - 3 * Count + 3) + 6 * s2 * _m2 - 4 * s * _m3;
// _m3 += t * (x /(Count-1) - 3 * s * _m2;
_oldValues[_lastIndex] = value; _oldValues[_lastIndex] = value;
_lastIndex++; _lastIndex++;
if (_lastIndex == WindowSize) if (_lastIndex == WindowSize)
@ -275,5 +328,32 @@ namespace MathNet.Numerics.Statistics
Push(value); Push(value);
} }
} }
private void DecrementTimeToLive()
{
if (_lastNaNTimeToLive > 0)
{
_lastNaNTimeToLive--;
}
if (_lastPosInfTimeToLive > 0)
{
_lastPosInfTimeToLive--;
}
if (_lastNegInfTimeToLive > 0)
{
_lastNegInfTimeToLive--;
}
}
private void Reset(double min, double max)
{
_totalCountOffset += _count + 1;
_count = 0;
_m1 = 0;
_max = max;
_min = min;
}
} }
} }

155
src/UnitTests/StatisticsTests/MovingStatisticsTests.cs

@ -28,6 +28,7 @@
// OTHER DEALINGS IN THE SOFTWARE. // OTHER DEALINGS IN THE SOFTWARE.
// </copyright> // </copyright>
using System;
using MathNet.Numerics.Distributions; using MathNet.Numerics.Distributions;
using MathNet.Numerics.Random; using MathNet.Numerics.Random;
using MathNet.Numerics.Statistics; using MathNet.Numerics.Statistics;
@ -38,6 +39,153 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests
[TestFixture, Category("Statistics")] [TestFixture, Category("Statistics")]
public class MovingStatisticsTests public class MovingStatisticsTests
{ {
[Test]
public void PositiveInfinityTest()
{
var ms = new MovingStatistics(3);
ms.Push(1.0);
ms.Push(2.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.Not.EqualTo(double.PositiveInfinity));
ms.Push(double.PositiveInfinity);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(1.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(double.PositiveInfinity);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(2.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(3.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(4.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.Not.EqualTo(double.PositiveInfinity));
}
[Test]
public void NegativeInfinityTest()
{
var ms = new MovingStatistics(3);
ms.Push(1.0);
ms.Push(2.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.Not.EqualTo(double.NegativeInfinity));
ms.Push(double.NegativeInfinity);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(1.0);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(double.NegativeInfinity);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(2.0);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(3.0);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(4.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.Not.NaN);
}
[Test]
public void MixedInfinityTest()
{
var ms = new MovingStatistics(3);
ms.Push(1.0);
ms.Push(2.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.Not.EqualTo(double.PositiveInfinity));
ms.Push(double.NegativeInfinity);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(1.0);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(double.PositiveInfinity);
Assert.That(ms.Minimum, Is.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.NaN);
Assert.That(ms.StandardDeviation, Is.NaN);
ms.Push(2.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(3.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.EqualTo(double.PositiveInfinity));
ms.Push(4.0);
Assert.That(ms.Minimum, Is.Not.EqualTo(double.NegativeInfinity));
Assert.That(ms.Maximum, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.Mean, Is.Not.EqualTo(double.PositiveInfinity));
Assert.That(ms.StandardDeviation, Is.Not.EqualTo(double.PositiveInfinity));
}
[Test] [Test]
public void StabilityTest() public void StabilityTest()
{ {
@ -53,10 +201,9 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests
Assert.AreEqual(33.33, ms.Mean, 1e-11); Assert.AreEqual(33.33, ms.Mean, 1e-11);
Assert.AreEqual(308.58025, ms.Variance, 1e-10); Assert.AreEqual(308.58025, ms.Variance, 1e-10);
Assert.AreEqual(17.5664524022354, ms.StandardDeviation, 1e-11);
//AssertHelpers.AlmostEqualRelative(stats0.Mean, ms.Mean, 14); Assert.AreEqual(246.8642, ms.PopulationVariance, 1e-10);
//AssertHelpers.AlmostEqualRelative(stats0.Variance, ms.Variance, 14); Assert.AreEqual(15.7119126779651, ms.PopulationStandardDeviation, 1e-10);
//AssertHelpers.AlmostEqualRelative(stats0.StandardDeviation, ms.StandardDeviation, 14);
} }
[Test] [Test]

Loading…
Cancel
Save