Skip to content

Commit 93c8446

Browse files
committed
* Add check and unit test for negative weights.
* Optimise polynomial construction.
1 parent 2c8359d commit 93c8446

File tree

4 files changed

+40
-12
lines changed

4 files changed

+40
-12
lines changed

src/Numerics.Tests/StatisticsTests/DescriptiveStatisticsTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,12 @@ public void ShortSequences()
526526
Assert.That(stats9.Kurtosis, Is.Not.NaN);
527527
}
528528

529+
[Test]
530+
public void NegativeWeightsThrow()
531+
{
532+
Assert.That(() => new DescriptiveStatistics(new[] { Tuple.Create(-1.0, 1.0) }), Throws.TypeOf<ArgumentOutOfRangeException>());
533+
}
534+
529535
[Test]
530536
public void ZeroVarianceSequence()
531537
{

src/Numerics.Tests/StatisticsTests/RunningWeightedStatisticsTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,15 @@ public void KurtosisConsistentWithR_e1071(string dataSet, double kurtosisType1,
135135
Assert.That(stats.PopulationKurtosis, Is.EqualTo(kurtosisType1).Within(1e-6), "PopulationKurtosis");
136136
}
137137

138+
[Test]
139+
public void NegativeWeightsThrow()
140+
{
141+
Assert.That(() => new RunningWeightedStatistics(new[] { System.Tuple.Create(-1.0, 1.0) }), Throws.TypeOf<System.ArgumentOutOfRangeException>());
142+
var stats0 = new RunningWeightedStatistics(new System.Tuple<double,double>[0]);
143+
Assert.That(() =>stats0.Push(-1.0, 1.0), Throws.TypeOf<System.ArgumentOutOfRangeException>());
144+
Assert.That(() => stats0.PushRange(new[] { System.Tuple.Create(-1.0, 1.0) }), Throws.TypeOf<System.ArgumentOutOfRangeException>());
145+
}
146+
138147
[Test]
139148
public void ShortSequences()
140149
{

src/Numerics/Statistics/DescriptiveStatistics.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ void SetStatistics(double mean, double variance, double skewness, double kurtosi
475475
}
476476

477477
/// <summary>
478-
/// Computes descriptive statistics from a stream of data values.
478+
/// Computes descriptive statistics from a stream of data values and weights.
479479
/// </summary>
480480
/// <param name="data">A sequence of datapoints.</param>
481481
void Compute(IEnumerable<Tuple<double, double>> data)
@@ -496,7 +496,11 @@ void Compute(IEnumerable<Tuple<double, double>> data)
496496

497497
foreach (var (w, xi) in data)
498498
{
499-
if (w > 0)
499+
if (w < 0)
500+
{
501+
throw new ArgumentOutOfRangeException(nameof(data), w, "Expected non-negative weighting of sample");
502+
}
503+
else if (w > 0)
500504
{
501505
++n;
502506
double delta = xi - mean;
@@ -537,7 +541,7 @@ void Compute(IEnumerable<Tuple<double, double>> data)
537541
}
538542

539543
/// <summary>
540-
/// Computes descriptive statistics from a stream of data values.
544+
/// Computes descriptive statistics from a stream of data values and weights.
541545
/// </summary>
542546
/// <param name="data">A sequence of datapoints.</param>
543547
void ComputeDecimal(IEnumerable<Tuple<double, double>> data)
@@ -558,7 +562,11 @@ void ComputeDecimal(IEnumerable<Tuple<double, double>> data)
558562

559563
foreach (var (w, x) in data)
560564
{
561-
if (w > 0)
565+
if (w < 0)
566+
{
567+
throw new ArgumentOutOfRangeException(nameof(data), w, "Expected non-negative weighting of sample");
568+
}
569+
else if (w > 0)
562570
{
563571

564572
decimal xi = (decimal)x;
@@ -643,10 +651,12 @@ void SetStatisticsWeighted(double mean, double variance, double skewness, double
643651

644652
if (n > 3)
645653
{
646-
// common denominator
647-
double poly = w1 * w1 * w1 * w1 - 6.0 * w1 * w1 * w2 + 8.0 * w1 * w3 + 3.0 * w2 * w2 - 6.0 * w4;
648-
double a = w1 * w1 * w1 * w1 - 4.0 * w1 * w3 + 3.0 * w2 * w2;
649-
double b = 3.0 * (w1 * w1 * w1 * w1 - 2.0 * w1 * w1 * w2 + 4.0 * w1 * w3 - 3.0 * w2 * w2);
654+
double p2 = w1 * w1;
655+
double p4 = p2 * p2;
656+
double w2p2 = w2 * w2;
657+
double poly = p4 - 6.0 * p2 * w2 + 8.0 * w1 * w3 + 3.0 * w2p2 - 6.0 * w4;
658+
double a = p4 - 4.0 * w1 * w3 + 3.0 * w2p2;
659+
double b = 3.0 * (p4 - 2.0 * p2 * w2 + 4.0 * w1 * w3 - 3.0 * w2p2);
650660
Kurtosis = (a * w1 * kurtosis / (variance * variance) - b) * (den / (w1 * poly));
651661
}
652662
}

src/Numerics/Statistics/RunningWeightedStatistics.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,12 @@ public double Kurtosis
210210
return double.NaN;
211211
else
212212
{
213-
double poly = _w1 * _w1 * _w1 * _w1 - 6.0 * _w1 * _w1 * _w2 + 8.0 * _w1 * _w3 + 3.0 * _w2 * _w2 - 6.0 * _w4;
214-
double a = _w1 * _w1 * _w1 * _w1 - 4.0 * _w1 * _w3 + 3.0 * _w2 * _w2;
215-
double b = 3.0 * (_w1 * _w1 * _w1 * _w1 - 2.0 * _w1 * _w1 * _w2 + 4.0 * _w1 * _w3 - 3.0 * _w2 * _w2);
213+
double p2 = _w1 * _w1;
214+
double p4 = p2 * p2;
215+
double w2p2 = _w2 * _w2;
216+
double poly = p4 - 6.0 * p2 * _w2 + 8.0 * _w1 * _w3 + 3.0 * w2p2 - 6.0 * _w4;
217+
double a = p4 - 4.0 * _w1 * _w3 + 3.0 * w2p2;
218+
double b = 3.0 * (p4 - 2.0 * p2 * _w2 + 4.0 * _w1 * _w3 - 3.0 * w2p2);
216219
return (a * _w1 * _m4 / (_m2 * _m2) - b) * (_den / (_w1 * poly));
217220
}
218221
}
@@ -238,7 +241,7 @@ public void Push(double weight, double value)
238241
if (weight == 0.0)
239242
return;
240243
if (weight < 0.0)
241-
throw new ArgumentException("Expected non-negative weighting of sample", nameof(weight));
244+
throw new ArgumentOutOfRangeException(nameof(weight), weight, "Expected non-negative weighting of sample");
242245

243246
_n++;
244247
double prevW = _w1;

0 commit comments

Comments
 (0)