Browse Source

Percentile: Bug fixes and compliance with Excel.

High boundary check was against 100, should be 1.0.
Close to 0 or 1 percentile values for Nist and Interpolation methods
caused array out of bounds exceptions.
Seemingly arbitrary small dataset restriction removed.
Out of range percentile values and empty dataset returns double.NaN
instead of throwing, similar to Excel returning #NUM! in such cases.
pull/103/head
IUser 14 years ago
parent
commit
20de4b1702
  1. 19
      src/Numerics/Statistics/Percentile.cs
  2. 23
      src/UnitTests/StatisticsTests/PercentileTests.cs

19
src/Numerics/Statistics/Percentile.cs

@ -90,11 +90,6 @@ namespace MathNet.Numerics.Statistics
throw new ArgumentNullException("data");
}
if (data.Count() < 3)
{
throw new ArgumentException(string.Format(Properties.Resources.MustContainAtLeast, 3), "data");
}
_data = new List<double>(data);
_data.Sort();
}
@ -106,12 +101,12 @@ namespace MathNet.Numerics.Statistics
/// <returns>the requested percentile.</returns>
public double Compute(double percentile)
{
if (percentile < 0 || percentile > 100)
if (percentile < 0 || percentile > 1 || _data.Count == 0)
{
throw new ArgumentException("Percentile value must be between 0 and 100.");
return double.NaN;
}
if (percentile == 0.0)
if (percentile == 0.0 || _data.Count == 1)
{
return _data[0];
}
@ -190,6 +185,9 @@ namespace MathNet.Numerics.Statistics
{
var k = (int)(_data.Count * percentile);
var pk = (k - 0.5) / _data.Count;
if(k == 0)
return _data[0];
return _data[k - 1] + (_data.Count * (percentile - pk) * (_data[k] - _data[k - 1]));
}
@ -202,6 +200,11 @@ namespace MathNet.Numerics.Statistics
{
var tmp = percentile * (_data.Count + 1.0);
var k = (int)tmp;
if(k == 0)
return _data[0];
if(k == _data.Count)
return _data[k - 1];
var d = tmp - k;
return _data[k - 1] + (d * (_data[k] - _data[k - 1]));

23
src/UnitTests/StatisticsTests/PercentileTests.cs

@ -99,32 +99,25 @@ namespace MathNet.Numerics.UnitTests.StatisticsTests
}
/// <summary>
/// Small dataset throws <c>ArgumentException</c>.
/// Empty dataset returns NaN.
/// </summary>
[Test]
public void SmallDataSetThrowArgumentException()
public void EmptyDataSetReturnsNaN()
{
var data = new double[] { };
var data1 = data;
Assert.Throws<ArgumentException>(() => new Percentile(data1));
data = new double[] { 1 };
var data2 = data;
Assert.Throws<ArgumentException>(() => new Percentile(data2));
data = new double[] { 1, 2 };
Assert.Throws<ArgumentException>(() => new Percentile(data));
var percentile = new Percentile(data);
Assert.IsTrue(double.IsNaN(percentile.Compute(0)));
}
/// <summary>
/// Invalid percentile values throw <c>ArgumentException</c>.
/// Invalid percentile values return NaN.
/// </summary>
[Test]
public void InvalidPercentileValuesThrowArgumentException()
public void InvalidPercentileValuesReturnNaN()
{
var percentile = new Percentile(Data);
Assert.Throws<ArgumentException>(() => percentile.Compute(-0.1));
Assert.Throws<ArgumentException>(() => percentile.Compute(100.1));
Assert.IsTrue(double.IsNaN(percentile.Compute(-0.1)));
Assert.IsTrue(double.IsNaN(percentile.Compute(1.1)));
}
}
}

Loading…
Cancel
Save