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 }