diff --git a/src/MathNet.Numerics.5.1.ReSharper b/src/MathNet.Numerics.5.1.ReSharper
index 3bcec989..06d7ae33 100644
--- a/src/MathNet.Numerics.5.1.ReSharper
+++ b/src/MathNet.Numerics.5.1.ReSharper
@@ -29,7 +29,10 @@ kronecker
Cholesky
Eigen
mxn
-nxn
+nxn
+Nist
+NIST's
+Excel's
diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj
index 48affc99..25b87f83 100644
--- a/src/Numerics/Numerics.csproj
+++ b/src/Numerics/Numerics.csproj
@@ -389,6 +389,7 @@
+
diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs
index e457ff6b..dc4c3628 100644
--- a/src/Numerics/Properties/Resources.Designer.cs
+++ b/src/Numerics/Properties/Resources.Designer.cs
@@ -627,6 +627,15 @@ namespace MathNet.Numerics.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Data must contain at least {0} values..
+ ///
+ internal static string MustContainAtLeast {
+ get {
+ return ResourceManager.GetString("MustContainAtLeast", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Name cannot contain a space. name: {0}.
///
diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx
index 8dfc7518..9cee9b16 100644
--- a/src/Numerics/Properties/Resources.resx
+++ b/src/Numerics/Properties/Resources.resx
@@ -345,4 +345,7 @@
ddd MMM dd HH:mm:ss yyyy
+
+ Data must contain at least {0} values.
+
\ No newline at end of file
diff --git a/src/Numerics/Statistics/Percentile.cs b/src/Numerics/Statistics/Percentile.cs
new file mode 100644
index 00000000..00b8e808
--- /dev/null
+++ b/src/Numerics/Statistics/Percentile.cs
@@ -0,0 +1,210 @@
+//
+// Math.NET Numerics, part of the Math.NET Project
+// http://numerics.mathdotnet.com
+// http://github.com/mathnet/mathnet-numerics
+// http://mathnetnumerics.codeplex.com
+// Copyright (c) 2009-2010 Math.NET
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+namespace MathNet.Numerics.Statistics
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ ///
+ /// Methods to calculate the percentiles.
+ ///
+ public enum PercentileMethod
+ {
+ ///
+ /// Using the method recommened my NIST,
+ /// http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm
+ ///
+ Nist = 0,
+
+ ///
+ /// Using the nearest rank, http://en.wikipedia.org/wiki/Percentile#Nearest_Rank
+ ///
+ Nearest,
+
+ ///
+ /// Using the same method as Excel does,
+ /// http://www.itl.nist.gov/div898/handbook/prc/section2/prc252.htm
+ ///
+ Excel,
+
+ ///
+ /// Use linear interpolation between the two nearest ranks,
+ /// http://en.wikipedia.org/wiki/Percentile#Linear_Interpolation_Between_Closest_Ranks
+ ///
+ Interpolation
+ }
+
+ ///
+ /// Class to calculate percentiles.
+ ///
+ public class Percentile
+ {
+ ///
+ /// Holds the data.
+ ///
+ private readonly List _data;
+
+ ///
+ /// Gets or sets the method used to calculate the percentiles.
+ ///
+ /// The calculation method.
+ /// defaults to .
+ public PercentileMethod Method
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The data to calculate the percentiles of.
+ public Percentile(IEnumerable data)
+ {
+ if (data == null)
+ {
+ throw new ArgumentNullException("data");
+ }
+
+ if (data.Count() < 3)
+ {
+ throw new ArgumentException(string.Format(Properties.Resources.MustContainAtLeast, 3), "data");
+ }
+
+ _data = new List(data);
+ _data.Sort();
+ }
+
+ ///
+ /// Computes the percentile.
+ ///
+ /// The percentile, must be between 0.0 and 1.0 (inclusive).
+ /// the requested percentile.
+ public double Compute(double percentile)
+ {
+ if (percentile < 0 || percentile > 100)
+ {
+ throw new ArgumentException("Percentile value must be between 0 and 100.");
+ }
+
+ if (percentile == 0.0)
+ {
+ return _data[0];
+ }
+
+ if (percentile == 1.0)
+ {
+ return _data[_data.Count - 1];
+ }
+
+ var result = double.NaN;
+ switch (Method)
+ {
+ case PercentileMethod.Nist:
+ result = Nist(percentile);
+ break;
+ case PercentileMethod.Nearest:
+ result = Nearest(percentile);
+ break;
+ case PercentileMethod.Interpolation:
+ result = Interpolation(percentile);
+ break;
+ case PercentileMethod.Excel:
+ result = Excel(percentile);
+ break;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Computes the percentiles for the given list.
+ ///
+ /// The percentiles, must be between 0.0 and 1.0 (inclusive)
+ /// the values that correspond to the given percentiles.
+ public IList Compute(IEnumerable percentiles)
+ {
+ if (percentiles == null)
+ {
+ throw new ArgumentNullException("percentiles");
+ }
+
+ return percentiles.Select(Compute).ToList();
+ }
+
+ ///
+ /// Computes the percentile using the nearest value.
+ ///
+ /// The percentile.
+ /// the percentile using the nearest value.
+ private double Nearest(double percentile)
+ {
+ var n = (int)Math.Round((_data.Count * percentile) + 0.5, 0);
+ return _data[n - 1];
+ }
+
+ ///
+ /// Computes the percentile using Excel's method.
+ ///
+ /// The percentile.
+ /// the percentile using Excel's method.
+ private double Excel(double percentile)
+ {
+ var tmp = 1 + (percentile * (_data.Count - 1.0));
+ var k = (int)tmp;
+ var d = tmp - k;
+
+ return _data[k - 1] + (d * (_data[k] - _data[k - 1]));
+ }
+
+ ///
+ /// Computes the percentile using interpolation.
+ ///
+ /// The percentile.
+ /// the percentile using the interpolation.
+ private double Interpolation(double percentile)
+ {
+ var k = (int)(_data.Count * percentile);
+ var pk = (k - 0.5) / _data.Count;
+ return _data[k - 1] + (_data.Count * (percentile - pk) * (_data[k] - _data[k - 1]));
+ }
+
+ ///
+ /// Computes the percentile using NIST's method.
+ ///
+ /// The percentile.
+ /// the percentile using NIST's method.
+ private double Nist(double percentile)
+ {
+ var tmp = percentile * (_data.Count + 1.0);
+ var k = (int)tmp;
+ var d = tmp - k;
+
+ return _data[k - 1] + (d * (_data[k] - _data[k - 1]));
+ }
+ }
+}
diff --git a/src/Silverlight/Silverlight.csproj b/src/Silverlight/Silverlight.csproj
index 1e8cbf48..4dadeb21 100644
--- a/src/Silverlight/Silverlight.csproj
+++ b/src/Silverlight/Silverlight.csproj
@@ -929,6 +929,9 @@
Statistics\MCMC\UnivariateSliceSampler.cs
+
+ Statistics\Percentile.cs
+
Statistics\Statistics.cs
diff --git a/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabWriterTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabWriterTests.cs
new file mode 100644
index 00000000..f8d66bb0
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabWriterTests.cs
@@ -0,0 +1,116 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.IO
+{
+ using System;
+ using System.IO;
+ using System.Numerics;
+ using LinearAlgebra.Complex;
+ using LinearAlgebra.Complex.IO;
+ using LinearAlgebra.IO;
+ using MbUnit.Framework;
+
+ [TestFixture]
+ public class MatlabMatrixWriterTests
+ {
+ [Test]
+ public void Constructor_ThrowsArgumentException()
+ {
+ Assert.Throws(() => new MatlabMatrixWriter(string.Empty));
+ Assert.Throws(() => new MatlabMatrixWriter(null));
+ }
+
+ [Test]
+ public void WriteMatrices_ThrowsArgumentException()
+ {
+ Matrix matrix = new DenseMatrix(1, 1);
+ var writer = new MatlabMatrixWriter("somefile3");
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new[] { string.Empty }));
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new string[] { null }));
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix, matrix }, new[] { "matrix" }));
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new[] { "some matrix" }));
+ writer.Dispose();
+ }
+
+ [Test]
+ public void WriteMatrices_ThrowsArgumentNullException()
+ {
+ var writer = new MatlabMatrixWriter("somefile4");
+ Assert.Throws(() => writer.WriteMatrices(new Matrix[] { null }, new[] { "matrix" }));
+ Matrix matrix = new DenseMatrix(1, 1);
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, null));
+ writer.Dispose();
+ }
+
+ [Test]
+ public void WriteMatricesTest()
+ {
+ Matrix mat1 = new DenseMatrix(5, 3);
+ for (var i = 0; i < mat1.ColumnCount; i++)
+ {
+ mat1[i, i] = new Complex(i + .1, i + .1);
+ }
+
+ Matrix mat2 = new DenseMatrix(4, 5);
+ for (var i = 0; i < mat2.RowCount; i++)
+ {
+ mat2[i, i] = new Complex(i + .1, i + .1);
+ }
+
+ Matrix mat3 = new SparseMatrix(5, 4);
+ for (var i = 0; i < mat3.ColumnCount; i++)
+ {
+ mat3[i, i] = new Complex(i + .1, i + .1);
+ }
+
+ Matrix mat4 = new SparseMatrix(3, 5);
+ for (var i = 0; i < mat4.RowCount; i++)
+ {
+ mat4[i, i] = new Complex(i + .1, i + .1);
+ }
+
+ var write = new[] { mat1, mat2, mat3, mat4 };
+
+ var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" };
+ if (File.Exists("test.mat"))
+ {
+ File.Delete("test.mat");
+ }
+
+ var writer = new MatlabMatrixWriter("test.mat");
+ writer.WriteMatrices(write, names);
+ writer.Dispose();
+
+ var reader = new MatlabMatrixReader("test.mat");
+ var read = reader.ReadMatrices(names);
+
+ Assert.AreEqual(write.Length, read.Count);
+
+ for (var i = 0; i < write.Length; i++ )
+ {
+ var w = write[i];
+ var r = read[names[i]];
+
+ Assert.AreEqual(w.RowCount, r.RowCount);
+ Assert.AreEqual(w.ColumnCount, r.ColumnCount);
+ Assert.IsTrue(w.Equals(r));
+ }
+ }
+
+ [Test]
+ public void WriteMatrix_ThrowsArgumentException()
+ {
+ Matrix matrix = new DenseMatrix(1, 1);
+ var writer = new MatlabMatrixWriter("somefile1");
+ Assert.Throws(() => writer.WriteMatrix(matrix, string.Empty));
+ Assert.Throws(() => writer.WriteMatrix(matrix, null));
+ writer.Dispose();
+ }
+
+ [Test]
+ public void WriteMatrix_ThrowsArgumentNullException()
+ {
+ var writer = new MatlabMatrixWriter("somefile2");
+ Assert.Throws(() => writer.WriteMatrix(null, "matrix"));
+ writer.Dispose();
+ }
+ }
+}
diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabWriterTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabWriterTests.cs
new file mode 100644
index 00000000..e8b2207b
--- /dev/null
+++ b/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabWriterTests.cs
@@ -0,0 +1,116 @@
+namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.IO
+{
+ using System;
+ using System.IO;
+ using LinearAlgebra.Complex32;
+ using LinearAlgebra.Complex32.IO;
+ using LinearAlgebra.IO;
+ using MbUnit.Framework;
+ using Numerics;
+
+ [TestFixture]
+ public class MatlabMatrixWriterTests
+ {
+ [Test]
+ public void Constructor_ThrowsArgumentException()
+ {
+ Assert.Throws(() => new MatlabMatrixWriter(string.Empty));
+ Assert.Throws(() => new MatlabMatrixWriter(null));
+ }
+
+ [Test]
+ public void WriteMatrices_ThrowsArgumentException()
+ {
+ Matrix matrix = new DenseMatrix(1, 1);
+ var writer = new MatlabMatrixWriter("somefile3");
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new[] { string.Empty }));
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new string[] { null }));
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix, matrix }, new[] { "matrix" }));
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new[] { "some matrix" }));
+ writer.Dispose();
+ }
+
+ [Test]
+ public void WriteMatrices_ThrowsArgumentNullException()
+ {
+ var writer = new MatlabMatrixWriter("somefile4");
+ Assert.Throws(() => writer.WriteMatrices(new Matrix[] { null }, new[] { "matrix" }));
+ Matrix matrix = new DenseMatrix(1, 1);
+ Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, null));
+ writer.Dispose();
+ }
+
+ [Test]
+ public void WriteMatricesTest()
+ {
+ Matrix mat1 = new DenseMatrix(5, 3);
+ for (var i = 0; i < mat1.ColumnCount; i++)
+ {
+ mat1[i, i] = new Complex32(i + .1f, i + .1f);
+ }
+
+ Matrix mat2 = new DenseMatrix(4, 5);
+ for (var i = 0; i < mat2.RowCount; i++)
+ {
+ mat2[i, i] = new Complex32(i + .1f, i + .1f);
+ }
+
+ Matrix mat3 = new SparseMatrix(5, 4);
+ for (var i = 0; i < mat3.ColumnCount; i++)
+ {
+ mat3[i, i] = new Complex32(i + .1f, i + .1f);
+ }
+
+ Matrix mat4 = new SparseMatrix(3, 5);
+ for (var i = 0; i < mat4.RowCount; i++)
+ {
+ mat4[i, i] = new Complex32(i + .1f, i + .1f);
+ }
+
+ var write = new[] { mat1, mat2, mat3, mat4 };
+
+ var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" };
+ if (File.Exists("test.mat"))
+ {
+ File.Delete("test.mat");
+ }
+
+ var writer = new MatlabMatrixWriter("test.mat");
+ writer.WriteMatrices(write, names);
+ writer.Dispose();
+
+ var reader = new MatlabMatrixReader("test.mat");
+ var read = reader.ReadMatrices(names);
+
+ Assert.AreEqual(write.Length, read.Count);
+
+ for (var i = 0; i < write.Length; i++ )
+ {
+ var w = write[i];
+ var r = read[names[i]];
+
+ Assert.AreEqual(w.RowCount, r.RowCount);
+ Assert.AreEqual(w.ColumnCount, r.ColumnCount);
+ Assert.IsTrue(w.Equals(r));
+ }
+ }
+
+ [Test]
+ public void WriteMatrix_ThrowsArgumentException()
+ {
+ Matrix matrix = new DenseMatrix(1, 1);
+ var writer = new MatlabMatrixWriter("somefile1");
+ Assert.Throws(() => writer.WriteMatrix(matrix, string.Empty));
+ Assert.Throws(() => writer.WriteMatrix(matrix, null));
+ writer.Dispose();
+ }
+
+ [Test]
+ public void WriteMatrix_ThrowsArgumentNullException()
+ {
+ var writer = new MatlabMatrixWriter("somefile2");
+ Assert.Throws(() => writer.WriteMatrix(null, "matrix"));
+ writer.Dispose();
+ }
+ }
+}
diff --git a/src/UnitTests/StatisticsTests/PercentileTests.cs b/src/UnitTests/StatisticsTests/PercentileTests.cs
new file mode 100644
index 00000000..e964f09f
--- /dev/null
+++ b/src/UnitTests/StatisticsTests/PercentileTests.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Gallio.Framework;
+using MbUnit.Framework;
+using MbUnit.Framework.ContractVerifiers;
+
+namespace MathNet.Numerics.UnitTests.StatisticsTests
+{
+ using Statistics;
+
+ [TestFixture]
+ public class PercentileTests
+ {
+
+ private static readonly double[] _data = {
+ 95.1772,
+ 95.1567,
+ 95.1937,
+ 95.1959,
+ 95.1442,
+ 95.061,
+ 95.1591,
+ 95.1195,
+ 95.1065,
+ 95.0925,
+ 95.199,
+ 95.1682
+ };
+
+
+ [Test]
+ public void CanComputePercentileUsingNistMethod()
+ {
+ var percentile = new Percentile(_data);
+ percentile.Method = PercentileMethod.Nist;
+ Assert.AreEqual(95.19807, percentile.Compute(.9));
+ }
+
+ [Test]
+ public void CanComputePercentileUsingExcelMethod()
+ {
+ var percentile = new Percentile(_data);
+ percentile.Method = PercentileMethod.Excel;
+ Assert.AreEqual(95.19568, percentile.Compute(.9));
+ }
+
+
+ [Test]
+ public void CanComputePercentileUsingNearestMethod()
+ {
+ var percentile = new Percentile(_data);
+ percentile.Method = PercentileMethod.Nearest;
+ Assert.AreEqual(95.1959, percentile.Compute(.9));
+ }
+
+
+ [Test]
+ public void CanComputePercentileUsingInterpolationMethod()
+ {
+ var data = new double[] { 1, 2, 3, 4, 5 };
+ var percentile = new Percentile(data);
+ percentile.Method = PercentileMethod.Interpolation;
+ var values = new[] { .25, .5, .75 };
+ var percentiles = percentile.Compute(values);
+ Assert.AreEqual(1.75, percentiles[0]);
+ Assert.AreEqual(3.0, percentiles[1]);
+ Assert.AreEqual(4.25, percentiles[2]);
+ }
+
+ [Test]
+ public void SmallDataSetThrowArgumentException()
+ {
+ var data = new double[] { };
+ Assert.Throws(() => new Percentile(data));
+ data = new double[] { 1 };
+ Assert.Throws(() => new Percentile(data));
+ data = new double[] { 1, 2 };
+ Assert.Throws(() => new Percentile(data));
+ }
+
+ [Test]
+ public void InvalidPercentileValuesThrowArgumentExecption()
+ {
+ var percentile = new Percentile(_data);
+ Assert.Throws(() => percentile.Compute(-0.1));
+ Assert.Throws(() => percentile.Compute(100.1));
+ }
+ }
+}
diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj
index 8e512ca5..4fa6dcbf 100644
--- a/src/UnitTests/UnitTests.csproj
+++ b/src/UnitTests/UnitTests.csproj
@@ -362,6 +362,7 @@
+