This project has moved. For the latest updates, please go here.

TruncatedNormal Distribution

Jun 4, 2014 at 9:21 PM
I think that there is a TruncatedNormal Distribution that exists but I'm not a mathematician and I don't know if it worth it to add it or not. It seeams to me that it could be a good idea to include it.
Coordinator
Jun 4, 2014 at 11:27 PM
Indeed, we do not support this distribution yet.

For reference: https://en.wikipedia.org/wiki/Truncated_normal_distribution
Jun 5, 2014 at 7:20 PM
This morning I discuss with a colleague about TruncatedNormal.
I realized that in MathLab it is a function that could be applied to a distribution, not a distribution by itself.
But in "r-project" (CRAN) it seems to be distribution function by itself?
Jun 5, 2014 at 8:58 PM
Do you just need to sample from a truncated normal?
If so you could simply use rejection sampling.
Sample from the normal and reject samples which are outside your truncated bounds.
Jun 6, 2014 at 1:49 PM
Hello Matthew,

I would have liked that it would be as easy as you said but...
In fact, I'm not the one who need it. I'm the programmer who will have to code it.
But I think they want the standard way to do it (at least the MathLab way)... I learned yesterday, from a Statitician guy and MathLab that a truncated normal should stay with its area under the curve at 1 always. When we truncate, we have to raise the normal until we get 1 as its area under the curve. That will move the real mean but as far as we understand it, that's the way to do it.
Jun 7, 2014 at 9:06 PM
Edited Jun 8, 2014 at 4:41 AM
How about this?
It is written in VB but I think it's pretty similar in C.
Public Class TruncatedNormal
    Dim Lower_Bound As Double
    Dim Upper_Bound As Double
    Dim ParentDistribution As MathNet.Numerics.Distributions.Normal
    Dim ParentSubArea As Double
    Public Sub New(ByVal Parent_Mean As Double, ByVal Parent_StandardDeviation As Double, _
                   ByVal LowerBound As Double, ByVal UpperBound As Double)
        Lower_Bound = LowerBound
        Upper_Bound = UpperBound
        ParentDistribution = New MathNet.Numerics.Distributions.Normal(Parent_Mean, Parent_StandardDeviation)
        ParentSubArea = ParentDistribution.CumulativeDistribution(Upper_Bound) - ParentDistribution.CumulativeDistribution(Lower_Bound)
        '#ParentSubArea is the area under the curve of the parent distribution between the bounds
    End Sub
    Public Function Sample() As Double
        Dim Accepted As Boolean = False
        Dim Candidate As Double
        Do Until Accepted
            Candidate = ParentDistribution.Sample()
            If Candidate > Lower_Bound And Candidate < Upper_Bound Then
                Accepted = True
            End If
        Loop
        Return Candidate / ParentSubArea
        '#Division by ParentSubArea makes the area under the curve of the truncated PDF 1.0
    End Function

    Public Function Samples(ByVal Number_of_Samples As Integer) As Double()
        Dim sample_array(Number_of_Samples - 1) As Double
        For s = 0 To Number_of_Samples - 1
            sample_array(s) = Sample()
        Next s
        Return sample_array
    End Function

End Class
The correction term for the area under the curve only needs to be computed once per distribution so it is handled in the constructor.
I included two methods for sampling: single sample and a sample array (similar to Math.Net)

Note: there is no validation checking for UpperBound>LowerBound... so include that if you need it.
Jun 7, 2014 at 9:29 PM
Edited Jun 7, 2014 at 9:55 PM
Did you want the truncated distribution to always have area under curve of 1.0 or to have area under curve the same as the area under the un-truncated parent normal?
I think that what Matlab does is the former, which would be as follows:
Public Class TruncatedNormal
    Dim Lower_Bound As Double
    Dim Upper_Bound As Double
    Dim ParentDistribution As MathNet.Numerics.Distributions.Normal
    Private CorrectionFactor As Double
    Public Sub New(ByVal Parent_Mean As Double, ByVal Parent_StandardDeviation As Double, _
                   ByVal LowerBound As Double, ByVal UpperBound As Double)
        Lower_Bound = LowerBound
        Upper_Bound = UpperBound
        ParentDistribution = New MathNet.Numerics.Distributions.Normal(Parent_Mean, Parent_StandardDeviation)
              CorrectionFactor = Parent_StandardDeviation / _
            (ParentDistribution.CumulativeDistribution(Upper_Bound) - ParentDistribution.CumulativeDistribution(Lower_Bound))
        '#ParentSubArea is the area under the curve of the parent distribution between the bounds
        '#Parent_StandardDeviation is the total area under the untrunctaed parent normal (neg inf to pos inf)

    End Sub
    Public Function Sample() As Double
        Dim Accepted As Boolean = False
        Dim Candidate As Double
        Do Until Accepted
            Candidate = ParentDistribution.Sample()
            If Candidate > Lower_Bound And Candidate < Upper_Bound Then
                Accepted = True
            End If
        Loop
        Return Candidate * CorrectionFactor
        '#Division by ParentSubArea makes the area under the curve of the truncated PDF 1.0
        '#Multiplication by Parent_StandardDeviation makes the area the same as total parent area.
    End Function

    Public Function Samples(ByVal Number_of_Samples As Integer) As Double()
        Dim sample_array(Number_of_Samples - 1) As Double
        For s = 0 To Number_of_Samples - 1
            sample_array(s) = Sample()
        Next s
        Return sample_array
    End Function

End Class
Jun 16, 2014 at 6:53 PM
Hello Matthew,

Sorry for the delay, I had a surgery last Monday...
Yes I think it should stay always with an area under the curve at 1.0 according to my project lead and a statistician that work here.
About the code, I'm really not sure. I'm a big zero in math, I don't know integral but I will check with my project leader.

Thanks a lot.
Eric