root/trunk/mathextra/student.d

Revision 71, 5.4 kB (checked in by Don Clugston, 3 years ago)

Moved all files to 'mathextra' subdirectory, and updated module names accordingly.

Line 
1 /**
2  * Macros:
3  *  GAMMA =  Γ
4  *  INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
5  *  POWER = $1<sup>$2</sup>
6  */
7 module mathextra.student;
8 private import std.math;
9 private import mathextra.betadist;
10 private import mathextra.normaldist;
11 debug private import std.stdio;
12
13 unittest {
14     debug writefln("--- UnitTest: " __FILE__ " ---");
15 } 
16
17
18 /**************************************************
19  * Computes the integral from minus infinity to t of the Student
20  * t distribution with integer nu > 0 degrees of freedom:
21  *
22  *   $(GAMMA)( (nu+1)/2) / ( sqrt(nu &pi;) $(GAMMA)(nu/2) ) *
23  * $(INTEGRATE -&infin;, t) $(POWER (1+$(POWER x, 2)/nu), -(nu+1)/2) dx
24  *   
25  * It is related to the incomplete beta integral:
26  *        1 - studentsDistribution(nu,t) = 0.5 * betaDistribution( nu/2, 1/2, z )
27  * where
28  *        z = nu/(nu + t<sup>2</sup>).
29  *
30  * For t < -1.6, this is the method of computation.  For higher t,
31  * a direct method is derived from integration by parts.
32  * Since the function is symmetric about t=0, the area under the
33  * right tail of the density is found by calling the function
34  * with -t instead of t.
35  */
36 real studentsDistribution(int nu, real t)
37 {
38   // Author: Don Clugston. Public domain.
39   /* Based on code from Cephes Math Library Release 2.3:  January, 1995
40      Copyright 1984, 1995 by Stephen L. Moshier
41  */
42
43
44     if ( nu <= 0 ) return real.nan; // domain error -- or should it return 0?
45     if ( t == 0.0 )  return 0.5;
46
47     real rk, z, p;
48    
49     if ( t < -1.6 ) {
50         rk = nu;
51         z = rk / (rk + t * t);
52         return 0.5L * betaDistribution( 0.5L*rk, 0.5L, z );
53     }
54
55     /*  compute integral from -t to + t */
56
57     rk = nu;    /* degrees of freedom */
58    
59     real x;
60     if (t < 0) x = -t; else x = t;
61     z = 1.0L + ( x * x )/rk;
62
63     real f, tz;
64     int j;
65
66     if ( nu & 1)    {
67         /*  computation for odd nu  */
68         real xsqk = x/sqrt(rk);
69         p = atan( xsqk );
70         if ( nu > 1 )   {
71             f = 1.0L;
72             tz = 1.0L;
73             j = 3;
74             while(  (j<=(nu-2)) && ( (tz/f) > real.epsilon )  ) {
75                 tz *= (j-1)/( z * j );
76                 f += tz;
77                 j += 2;
78             }
79             p += f * xsqk/z;
80             }
81         p *= 2.0L/PI;
82     } else {
83         /*  computation for even nu */
84         f = 1.0L;
85         tz = 1.0L;
86         j = 2;
87    
88         while ( ( j <= (nu-2) ) && ( (tz/f) > real.epsilon )  ) {
89             tz *= (j - 1)/( z * j );
90             f += tz;
91             j += 2;
92         }
93         p = f * x/sqrt(z*rk);
94     }
95     if ( t < 0.0L )
96         p = -p; /* note destruction of relative accuracy */
97
98     p = 0.5L + 0.5L * p;
99     return p;
100 }
101
102 /******************************************
103 *   Inverse of Student's t distribution
104 *
105 * Given probability p and degrees of freedom nu,
106 * finds the argument t such that the one-sided
107 * studentsDistribution(nu,t) is equal to p.
108 * Used to test whether two distributions have the same
109 * standard deviation.
110 *
111 * Params:
112 * nu = degrees of freedom. Must be >1
113 * p  = probability. 0 < p < 1
114 */
115 real studentsDistributionInv(int nu, real p )
116 // Author: Don Clugston. Public domain.
117 in {
118    assert(nu>0);
119    assert(p>=0.0L && p<=1.0L);
120 }
121 body
122 {
123     if (p==0) return -real.infinity;
124     if (p==1) return real.infinity;
125    
126     real rk, z;
127     rk =  nu;
128    
129     if ( p > 0.25L && p < 0.75L ) {
130         if ( p == 0.5L ) return 0;
131         z = 1.0L - 2.0L * p;
132         z = betaDistributionInv( 0.5L, 0.5L*rk, fabs(z) );
133         real t = sqrt( rk*z/(1.0L-z) );
134         if( p < 0.5L )
135             t = -t;
136         return t;
137     }
138     int rflg = -1; // sign of the result
139     if (p >= 0.5L) {
140         p = 1.0L - p;
141         rflg = 1;
142     }
143     z = betaDistributionInv( 0.5L*rk, 0.5L, 2.0L*p );
144    
145     if (z<0) return rflg * real.infinity;
146     return rflg * sqrt( rk/z - rk );
147 }
148
149 unittest {
150
151 // There are simple forms for nu = 1 and nu = 2.
152
153 // if (nu == 1), tDistribution(x) = 0.5 + atan(x)/PI
154 //              so tDistributionInv(p) = tan( PI * (p-0.5) );
155 // nu==2: tDistribution(x) = 0.5 * (1 + x/ sqrt(2+x*x) )
156
157 assert( studentsDistribution(1, -0.4)== 0.5 + atan(-0.4)/PI);
158 assert(studentsDistribution(2, 0.9) == 0.5L * (1 + 0.9L/sqrt(2.0L + 0.9*0.9)) );
159 assert(studentsDistribution(2, -5.4) == 0.5L * (1 - 5.4L/sqrt(2.0L + 5.4*5.4)) );
160
161 // Check a few spot values with statsoft.com (Mathworld values are wrong!!)
162 // According to statsoft.com, studentsDistributionInv(10, 0.995)= 3.16927.
163
164 // The remaining values listed here are from Excel, and are unlikely to be accurate
165 // in the last decimal places. However, they are helpful as a sanity check.
166
167 //  Microsoft Excel 2003 gives TINV(2*(1-0.995), 10) == 3.16927267160917
168 assert(isfeqabs(studentsDistributionInv(10, 0.995), 3.169_272_67L, 0.000_000_005L));
169
170  
171 assert(isfeqabs(studentsDistributionInv(8, 0.6), 0.261_921_096_769_043L,0.000_000_000_05L));
172 // -TINV(2*0.4, 18) ==  -0.257123042655869
173
174 assert(isfeqabs(studentsDistributionInv(18, 0.4), -0.257_123_042_655_869L, 0.000_000_000_05L));
175 assert( feqrel(studentsDistribution(18, studentsDistributionInv(18, 0.4L)),0.4L)
176  > real.mant_dig-2 );
177 assert( feqrel(studentsDistribution(11, studentsDistributionInv(11, 0.9L)),0.9L)
178   > real.mant_dig-2);
179 //writefln("%.20f", studentsDistribution(18, -0.257_123_042_655_869));
180
181 }
182
183 // return true if a==b to given number of places.
184 bool isfeqabs(real a, real b, real diff)
185 {
186   return fabs(a-b) < diff;
187 }
Note: See TracBrowser for help on using the browser.