Browse Source

* Add check and unit test for negative weights.

* Optimise polynomial construction.
pull/865/head
richard-allen 5 years ago
parent
commit
93c8446ddd
  1. 6
      src/Numerics.Tests/StatisticsTests/DescriptiveStatisticsTests.cs
  2. 9
      src/Numerics.Tests/StatisticsTests/RunningWeightedStatisticsTests.cs
  3. 26
      src/Numerics/Statistics/DescriptiveStatistics.cs
  4. 11
      src/Numerics/Statistics/RunningWeightedStatistics.cs

6
src/Numerics.Tests/StatisticsTests/DescriptiveStatisticsTests.cs

@ -526,6 +526,12 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests
Assert.That(stats9.Kurtosis, Is.Not.NaN); Assert.That(stats9.Kurtosis, Is.Not.NaN);
} }
[Test]
public void NegativeWeightsThrow()
{
Assert.That(() => new DescriptiveStatistics(new[] { Tuple.Create(-1.0, 1.0) }), Throws.TypeOf<ArgumentOutOfRangeException>());
}
[Test] [Test]
public void ZeroVarianceSequence() public void ZeroVarianceSequence()
{ {

9
src/Numerics.Tests/StatisticsTests/RunningWeightedStatisticsTests.cs

@ -135,6 +135,15 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests
Assert.That(stats.PopulationKurtosis, Is.EqualTo(kurtosisType1).Within(1e-6), "PopulationKurtosis"); Assert.That(stats.PopulationKurtosis, Is.EqualTo(kurtosisType1).Within(1e-6), "PopulationKurtosis");
} }
[Test]
public void NegativeWeightsThrow()
{
Assert.That(() => new RunningWeightedStatistics(new[] { System.Tuple.Create(-1.0, 1.0) }), Throws.TypeOf<System.ArgumentOutOfRangeException>());
var stats0 = new RunningWeightedStatistics(new System.Tuple<double,double>[0]);
Assert.That(() =>stats0.Push(-1.0, 1.0), Throws.TypeOf<System.ArgumentOutOfRangeException>());
Assert.That(() => stats0.PushRange(new[] { System.Tuple.Create(-1.0, 1.0) }), Throws.TypeOf<System.ArgumentOutOfRangeException>());
}
[Test] [Test]
public void ShortSequences() public void ShortSequences()
{ {

26
src/Numerics/Statistics/DescriptiveStatistics.cs

@ -475,7 +475,7 @@ namespace MathNet.Numerics.Statistics
} }
/// <summary> /// <summary>
/// Computes descriptive statistics from a stream of data values. /// Computes descriptive statistics from a stream of data values and weights.
/// </summary> /// </summary>
/// <param name="data">A sequence of datapoints.</param> /// <param name="data">A sequence of datapoints.</param>
void Compute(IEnumerable<Tuple<double, double>> data) void Compute(IEnumerable<Tuple<double, double>> data)
@ -496,7 +496,11 @@ namespace MathNet.Numerics.Statistics
foreach (var (w, xi) in data) foreach (var (w, xi) in data)
{ {
if (w > 0) if (w < 0)
{
throw new ArgumentOutOfRangeException(nameof(data), w, "Expected non-negative weighting of sample");
}
else if (w > 0)
{ {
++n; ++n;
double delta = xi - mean; double delta = xi - mean;
@ -537,7 +541,7 @@ namespace MathNet.Numerics.Statistics
} }
/// <summary> /// <summary>
/// Computes descriptive statistics from a stream of data values. /// Computes descriptive statistics from a stream of data values and weights.
/// </summary> /// </summary>
/// <param name="data">A sequence of datapoints.</param> /// <param name="data">A sequence of datapoints.</param>
void ComputeDecimal(IEnumerable<Tuple<double, double>> data) void ComputeDecimal(IEnumerable<Tuple<double, double>> data)
@ -558,7 +562,11 @@ namespace MathNet.Numerics.Statistics
foreach (var (w, x) in data) foreach (var (w, x) in data)
{ {
if (w > 0) if (w < 0)
{
throw new ArgumentOutOfRangeException(nameof(data), w, "Expected non-negative weighting of sample");
}
else if (w > 0)
{ {
decimal xi = (decimal)x; decimal xi = (decimal)x;
@ -643,10 +651,12 @@ namespace MathNet.Numerics.Statistics
if (n > 3) if (n > 3)
{ {
// common denominator double p2 = w1 * w1;
double poly = w1 * w1 * w1 * w1 - 6.0 * w1 * w1 * w2 + 8.0 * w1 * w3 + 3.0 * w2 * w2 - 6.0 * w4; double p4 = p2 * p2;
double a = w1 * w1 * w1 * w1 - 4.0 * w1 * w3 + 3.0 * w2 * w2; double w2p2 = w2 * w2;
double b = 3.0 * (w1 * w1 * w1 * w1 - 2.0 * w1 * w1 * w2 + 4.0 * w1 * w3 - 3.0 * w2 * w2); double poly = p4 - 6.0 * p2 * w2 + 8.0 * w1 * w3 + 3.0 * w2p2 - 6.0 * w4;
double a = p4 - 4.0 * w1 * w3 + 3.0 * w2p2;
double b = 3.0 * (p4 - 2.0 * p2 * w2 + 4.0 * w1 * w3 - 3.0 * w2p2);
Kurtosis = (a * w1 * kurtosis / (variance * variance) - b) * (den / (w1 * poly)); Kurtosis = (a * w1 * kurtosis / (variance * variance) - b) * (den / (w1 * poly));
} }
} }

11
src/Numerics/Statistics/RunningWeightedStatistics.cs

@ -210,9 +210,12 @@ namespace MathNet.Numerics.Statistics
return double.NaN; return double.NaN;
else else
{ {
double poly = _w1 * _w1 * _w1 * _w1 - 6.0 * _w1 * _w1 * _w2 + 8.0 * _w1 * _w3 + 3.0 * _w2 * _w2 - 6.0 * _w4; double p2 = _w1 * _w1;
double a = _w1 * _w1 * _w1 * _w1 - 4.0 * _w1 * _w3 + 3.0 * _w2 * _w2; double p4 = p2 * p2;
double b = 3.0 * (_w1 * _w1 * _w1 * _w1 - 2.0 * _w1 * _w1 * _w2 + 4.0 * _w1 * _w3 - 3.0 * _w2 * _w2); double w2p2 = _w2 * _w2;
double poly = p4 - 6.0 * p2 * _w2 + 8.0 * _w1 * _w3 + 3.0 * w2p2 - 6.0 * _w4;
double a = p4 - 4.0 * _w1 * _w3 + 3.0 * w2p2;
double b = 3.0 * (p4 - 2.0 * p2 * _w2 + 4.0 * _w1 * _w3 - 3.0 * w2p2);
return (a * _w1 * _m4 / (_m2 * _m2) - b) * (_den / (_w1 * poly)); return (a * _w1 * _m4 / (_m2 * _m2) - b) * (_den / (_w1 * poly));
} }
} }
@ -238,7 +241,7 @@ namespace MathNet.Numerics.Statistics
if (weight == 0.0) if (weight == 0.0)
return; return;
if (weight < 0.0) if (weight < 0.0)
throw new ArgumentException("Expected non-negative weighting of sample", nameof(weight)); throw new ArgumentOutOfRangeException(nameof(weight), weight, "Expected non-negative weighting of sample");
_n++; _n++;
double prevW = _w1; double prevW = _w1;

Loading…
Cancel
Save