View Javadoc
1   /*
2    * Copyright (C) 2015 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.statistics;
17  
18  /**
19   * Contains methods to work with Chi squared distributions.
20   * Methods of this class use methods of the Gamma class.
21   * This class is based in code of Numerical Recipes 3rd ed. section 6.14.8.
22   */
23  public class ChiSqDist {
24  
25      /**
26       * Nu parameter of chi square distribution.
27       * Typically, this parameter is provided as an integer value indicating the
28       * number of degrees of freedom.
29       */
30      private double nu;
31  
32      /**
33       * Constant factor to multiply p.d.f of chi squared by. This factor depends
34       * on nu parameter.
35       */
36      private double fac;
37  
38      /**
39       * A gamma function instance to be reused for memory efficiency.
40       */
41      private final Gamma gamma;
42  
43      /**
44       * Constructor.
45       *
46       * @param nu parameter of chi square distribution.
47       * @throws IllegalArgumentException if provided nu parameter is negative or
48       *                                  zero.
49       */
50      public ChiSqDist(final double nu) {
51          gamma = new Gamma();
52          setNu(nu);
53      }
54  
55      /**
56       * Returns nu parameter of chi square distribution.
57       * Typically, this parameter is an integer value indicating the number of
58       * degrees of freedom.
59       *
60       * @return nu parameter of chi square distribution.
61       */
62      public double getNu() {
63          return nu;
64      }
65  
66      /**
67       * Sets nu parameter of chi square distribution.
68       * Typically, this parameter is an integer value indicating the number of
69       * degrees of freedom.
70       *
71       * @param nu nu parameter of chi square distribution.
72       * @throws IllegalArgumentException if provided nu parameter is negative or
73       *                                  zero.
74       */
75      public final void setNu(final double nu) {
76          if (nu <= 0.0) {
77              throw new IllegalArgumentException();
78          }
79  
80          this.nu = nu;
81          fac = fac(nu);
82      }
83  
84      /**
85       * Evaluates the probability density function (p.d.f.) of a Chi square
86       * distribution.
87       *
88       * @param x2 chi square value where p.d.f. is evaluated. Must be greater
89       *           than 0.0.
90       * @param nu nu parameter of chi square distribution. Typically, this is an
91       *           integer value indicating the number of degrees of freedom. Must be
92       *           greater than 0.0.
93       * @return evaluation of p.d.f.
94       * @throws IllegalArgumentException if either x2 or nu are negative or zero.
95       */
96      public static double p(final double x2, final double nu) {
97          if (nu <= 0.0) {
98              throw new IllegalArgumentException();
99          }
100 
101         return internalP(x2, nu, fac(nu));
102     }
103 
104     /**
105      * Evaluates the probability density function (p.d.f.) of this Chi square
106      * distribution.
107      *
108      * @param x2 chi square value where p.d.f. is evaluated. Must be greater
109      *           than 0.0.
110      * @return evaluation of p.d.f.
111      * @throws IllegalArgumentException if x2 is negative or zero.
112      */
113     public double p(final double x2) {
114         return internalP(x2, nu, fac);
115     }
116 
117     /**
118      * Evaluates the cumulative distribution function (c.d.f.) of a Chi-squared
119      * distribution having parameter nu.
120      *
121      * @param x2 chi square value where c.d.f. is evaluated. Must be positive or
122      *           zero.
123      * @param nu nu parameter of chi square distribution. Typically, this is an
124      *           integer value indicating the number of degrees of freedom. Must be
125      *           greater than 0.0.
126      * @return evaluation of c.d.f.
127      * @throws IllegalArgumentException       if provided chi square value is negative
128      *                                        or if provided nu parameter is negative or zero.
129      * @throws MaxIterationsExceededException if convergence of incomplete
130      *                                        gamma function cannot be reached. This is rarely thrown and happens
131      *                                        usually for numerically unstable input values.
132      */
133     public static double cdf(final double x2, final double nu)
134             throws MaxIterationsExceededException {
135         if (nu <= 0.0) {
136             throw new IllegalArgumentException("nu must be greater than 0.0");
137         }
138 
139         return internalCdf(x2, nu, new Gamma());
140     }
141 
142     /**
143      * Evaluates the cumulative distribution function (c.d.f.) of this Chi-square
144      * distribution.
145      *
146      * @param x2 chi square value where c.d.f. is evaluated. Must be positive or
147      *           zero.
148      * @return evaluation of c.d.f.
149      * @throws IllegalArgumentException       if provided chi square value is
150      *                                        negative.
151      * @throws MaxIterationsExceededException if convergence of incomplete gamma
152      *                                        function cannot be reached. This is rarely thrown and happens usually for
153      *                                        numerically unstable input values.
154      */
155     public double cdf(final double x2) throws MaxIterationsExceededException {
156         return internalCdf(x2, nu, gamma);
157     }
158 
159     /**
160      * Evaluates the inverse cumulative distribution function of a Chi squared
161      * distribution having parameter nu.
162      * Because the c.d.f is a monotonically increasing function with values
163      * between 0.0 and 1.0, its inverse is uniquely defined between such range
164      * of values.
165      *
166      * @param p  value to evaluate the inverse c.d.f. at. This value is
167      *           equivalent to a probability and must be between 0.0 and 1.0.
168      * @param nu nu parameter of chi square distribution. Typically, this is an
169      *           integer value indicating the number of degrees of freedom. Must be
170      *           greater than 0.0.
171      * @return the value x2 for which the c.d.f. has value p.
172      * @throws IllegalArgumentException       if provided probability value is not
173      *                                        between 0.0 and 1.0 of if provided nu parameter is negative or zero.
174      * @throws MaxIterationsExceededException if convergence of inverse
175      *                                        incomplete gamma function cannot be reached. This is rarely thrown and
176      *                                        happens usually for numerically unstable values.
177      */
178     public static double invcdf(final double p, final double nu) throws MaxIterationsExceededException {
179         if (nu <= 0.0) {
180             throw new IllegalArgumentException("nu must be greater than 0.0");
181         }
182 
183         return internalInvcdf(p, nu, new Gamma());
184     }
185 
186     /**
187      * Evaluates the inverse cumulative distribution function of this Chi squared
188      * distribution.
189      * Because the c.d.f is a monotonically increasing function with values
190      * between 0.0 and 1.0, its inverse is uniquely defined between such range
191      * of values.
192      *
193      * @param p value to evaluate the inverse c.d.f. at. This value is
194      *          equivalent to a probability and must be between 0.0 and 1.0.
195      * @return the value x2 for which the c.d.f. has value p.
196      * @throws IllegalArgumentException       if provided probability value is not
197      *                                        between 0.0 and 1.0.
198      * @throws MaxIterationsExceededException if convergence of inverse
199      *                                        incomplete gamma function cannot be reached. This is rarely thrown and
200      *                                        happens usually for numerically unstable values.
201      */
202     public double invcdf(final double p) throws MaxIterationsExceededException {
203         return internalInvcdf(p, nu, gamma);
204     }
205 
206     /**
207      * Evaluates the probability density function (p.d.f.) of a Chi square
208      * distribution.
209      * This method is used internally.
210      *
211      * @param x2  chi square value where p.d.f. is evaluated. Must be greater
212      *            than 0.0.
213      * @param nu  nu parameter of chi square distribution. Typically, this is an
214      *            integer value indicating the number of degrees of freedom. Must be
215      *            greater than 0.0.
216      * @param fac actor to multiply p.d.f of chi squared by.
217      * @return evaluation of p.d.f.
218      * @throws IllegalArgumentException if x2 is negative or zero.
219      */
220     private static double internalP(final double x2, final double nu, final double fac) {
221         if (x2 <= 0.0) {
222             throw new IllegalArgumentException(
223                     "chi square must be greater than zero");
224         }
225 
226         return Math.exp(-0.5 * (x2 - (nu - 2.0) * Math.log(x2)) - fac);
227     }
228 
229     /**
230      * Evaluates the cumulative distribution function (c.d.f.) of a Chi-squared
231      * distribution having parameter nu.
232      * This method is used internally.
233      *
234      * @param x2    chi square value where c.d.f. is evaluated. Must be positive
235      *              or zero.
236      * @param nu    nu parameter of chi square distribution. Typically, this is an
237      *              integer value indicating the number of degrees of freedom. Must be
238      *              greater than 0.0.
239      * @param gamma a gamma instance to evaluate the incomplete gamma function.
240      * @return evaluation of c.d.f.
241      * @throws IllegalArgumentException       if provided chi square value is
242      *                                        negative.
243      * @throws MaxIterationsExceededException if convergence of incomplete gamma
244      *                                        function cannot be reached. This is rarely thrown and happens usually for
245      *                                        numerically unstable values.
246      */
247     private static double internalCdf(final double x2, final double nu, final Gamma gamma)
248             throws MaxIterationsExceededException {
249         if (x2 < 0.0) {
250             throw new IllegalArgumentException("chi square must be positive or zero");
251         }
252 
253         return gamma.gammp(0.5 * nu, 0.5 * x2);
254     }
255 
256     /**
257      * Evaluates the inverse cumulative distribution function of a Chi squared
258      * distribution having parameter nu.
259      * Because the c.d.f is a monotonically increasing function with values
260      * between 0.0 and 1.0, its inverse is uniquely defined between such range
261      * of values.
262      * This method is used internally.
263      *
264      * @param p     value to evaluate the inverse c.d.f. at. This value is
265      *              equivalent to a probability and must be between 0.0 and 1.0.
266      * @param nu    nu parameter of chi square distribution. Typically, this is an
267      *              integer value indicating the number of degrees of freedom. Must be
268      *              greater than 0.0.
269      * @param gamma a gamma instance to evaluate the inverse incomplete gamma
270      *              function.
271      * @return the value x2 for which the c.d.f. has value p.
272      * @throws IllegalArgumentException       if provided probability value is not
273      *                                        between 0.0 and 1.0.
274      * @throws MaxIterationsExceededException if convergence of inverse
275      *                                        incomplete gamma function cannot be reached. This is rarely thrown and
276      *                                        happens usually for numerically unstable values.
277      */
278     private static double internalInvcdf(final double p, final double nu, final Gamma gamma)
279             throws MaxIterationsExceededException {
280 
281         if (p < 0.0 || p >= 1.0) {
282             throw new IllegalArgumentException("probability value must be between 0.0 and 1.0");
283         }
284 
285         return 2.0 * gamma.invgammp(p, 0.5 * nu);
286     }
287 
288     /**
289      * Computes constant factor to multiply p.d.f. of chi squared by.
290      *
291      * @param nu nu parameter of chi square distribution. Typically, this
292      *           parameter is provided as an integer value indicating the number of
293      *           degrees of freedom.
294      * @return constant factor to multiply p.d.f. of chi squared.
295      */
296     private static double fac(final double nu) {
297         return 0.693147180559945309 * (0.5 * nu) + Gamma.gammln(0.5 * nu);
298     }
299 }