diff --git a/src/Numerics/Distributions/Discrete/Categorical.cs b/src/Numerics/Distributions/Discrete/Categorical.cs index 9cde7e6d..75a4e7af 100644 --- a/src/Numerics/Distributions/Discrete/Categorical.cs +++ b/src/Numerics/Distributions/Discrete/Categorical.cs @@ -50,7 +50,7 @@ namespace MathNet.Numerics.Distributions public class Categorical : IDiscreteDistribution { /// - /// Stores the normalized categorical probabilities. + /// Stores the unnormalized categorical probabilities. /// private double[] _p; @@ -71,24 +71,31 @@ namespace MathNet.Numerics.Distributions RandomSource = new System.Random(); } - /* TODO /// - /// Generate a categorical distribution from histogram . The distribution will - /// not be automatically updated when the histogram changes. + /// Initializes a new instance of the Categorical class from a histogram . The distribution + /// will not be automatically updated when the histogram changes. The categorical distribution will have + /// one value for each bucket and a probability for that value proportional to the bucket count. /// - public Categorical(Histogram h) + /// The histogram from which to create the categorical variable. + public Categorical(Histogram histogram) { + if (histogram == null) + { + throw new ArgumentNullException("Cannot create a categorical variable from a null histogram."); + } + // The probability distribution vector. - _p = new double[h.BinCount]; + double[] p = new double[histogram.BucketCount]; // Fill in the distribution vector. - for (int i = 0; i < h.BinCount; i++) + for (int i = 0; i < histogram.BucketCount; i++) { - _p[i] = h[i]; + p[i] = histogram[i].Count; } - RandomNumberGenerator = new System.Random(); - }*/ + SetParameters(p); + RandomSource = new System.Random(); + } /// /// A string representation of the distribution. @@ -144,13 +151,28 @@ namespace MathNet.Numerics.Distributions } /// - /// Gets or sets the probability of generating a one. + /// Gets or sets the normalized probability vector of the multinomial. /// + /// Note that sometimes the normalized probability vector cannot be represented + /// exactly in a floating point representation. public double[] P { get { - return (double[]) _p.Clone(); + double[] p = (double[]) _p.Clone(); + + double sum = 0.0; + for (int i = 0; i < p.Length; i++) + { + sum += p[i]; + } + + for (int i = 0; i < p.Length; i++) + { + p[i] /= sum; + } + + return p; } set diff --git a/src/UnitTests/DistributionTests/Discrete/CategoricalTests.cs b/src/UnitTests/DistributionTests/Discrete/CategoricalTests.cs index 34e21bf2..e1b9e915 100644 --- a/src/UnitTests/DistributionTests/Discrete/CategoricalTests.cs +++ b/src/UnitTests/DistributionTests/Discrete/CategoricalTests.cs @@ -32,6 +32,7 @@ namespace MathNet.Numerics.UnitTests.DistributionTests using System.Linq; using MbUnit.Framework; using MathNet.Numerics.Distributions; + using MathNet.Numerics.Statistics; [TestFixture] public class CategoricalTests @@ -55,7 +56,28 @@ namespace MathNet.Numerics.UnitTests.DistributionTests public void CanCreateCategorical() { var m = new Categorical(largeP); - AssertEx.AreEqual(largeP, m.P); + } + + [Test] + [MultipleAsserts] + public void CanCreateCategoricalFromHistogram() + { + double[] smallDataset = { 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5 }; + Histogram hist = new Histogram(smallDataset, 10, 0.0, 10.0); + var m = new Categorical(hist); + + for (int i = 0; i <= m.Maximum; i++) + { + AssertEx.AreEqual(1.0/10.0, m.P[i]); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException))] + public void CategoricalCreateFailsWithNullHistogram() + { + Histogram h = null; + var m = new Categorical(h); } [Test]