1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.statistics.distribution;
19
20 import org.apache.commons.numbers.gamma.LogBeta;
21 import org.apache.commons.numbers.gamma.RegularizedBeta;
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public final class FDistribution extends AbstractContinuousDistribution {
39
40 private static final double SUPPORT_LO = 0;
41
42 private static final double SUPPORT_HI = Double.POSITIVE_INFINITY;
43
44 private static final double MIN_DENOMINATOR_DF_FOR_MEAN = 2.0;
45
46 private static final double MIN_DENOMINATOR_DF_FOR_VARIANCE = 4.0;
47
48
49 private final double numeratorDegreesOfFreedom;
50
51 private final double denominatorDegreesOfFreedom;
52
53 private final double nHalfLogNmHalfLogM;
54
55 private final double logBetaNhalfMhalf;
56
57 private final double mean;
58
59 private final double variance;
60
61
62
63
64
65 private FDistribution(double numeratorDegreesOfFreedom,
66 double denominatorDegreesOfFreedom) {
67 this.numeratorDegreesOfFreedom = numeratorDegreesOfFreedom;
68 this.denominatorDegreesOfFreedom = denominatorDegreesOfFreedom;
69 final double nhalf = numeratorDegreesOfFreedom / 2;
70 final double mhalf = denominatorDegreesOfFreedom / 2;
71 nHalfLogNmHalfLogM = nhalf * Math.log(numeratorDegreesOfFreedom) +
72 mhalf * Math.log(denominatorDegreesOfFreedom);
73 logBetaNhalfMhalf = LogBeta.value(nhalf, mhalf);
74
75 if (denominatorDegreesOfFreedom > MIN_DENOMINATOR_DF_FOR_MEAN) {
76 mean = denominatorDegreesOfFreedom / (denominatorDegreesOfFreedom - 2);
77 } else {
78 mean = Double.NaN;
79 }
80 if (denominatorDegreesOfFreedom > MIN_DENOMINATOR_DF_FOR_VARIANCE) {
81 final double denomDFMinusTwo = denominatorDegreesOfFreedom - 2;
82 variance = (2 * (denominatorDegreesOfFreedom * denominatorDegreesOfFreedom) *
83 (numeratorDegreesOfFreedom + denominatorDegreesOfFreedom - 2)) /
84 (numeratorDegreesOfFreedom * (denomDFMinusTwo * denomDFMinusTwo) *
85 (denominatorDegreesOfFreedom - 4));
86 } else {
87 variance = Double.NaN;
88 }
89 }
90
91
92
93
94
95
96
97
98
99
100 public static FDistribution of(double numeratorDegreesOfFreedom,
101 double denominatorDegreesOfFreedom) {
102 if (numeratorDegreesOfFreedom <= 0) {
103 throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE,
104 numeratorDegreesOfFreedom);
105 }
106 if (denominatorDegreesOfFreedom <= 0) {
107 throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE,
108 denominatorDegreesOfFreedom);
109 }
110 return new FDistribution(numeratorDegreesOfFreedom, denominatorDegreesOfFreedom);
111 }
112
113
114
115
116
117
118 public double getNumeratorDegreesOfFreedom() {
119 return numeratorDegreesOfFreedom;
120 }
121
122
123
124
125
126
127 public double getDenominatorDegreesOfFreedom() {
128 return denominatorDegreesOfFreedom;
129 }
130
131
132
133
134
135
136
137
138
139
140
141 @Override
142 public double density(double x) {
143 if (x <= SUPPORT_LO ||
144 x >= SUPPORT_HI) {
145
146
147 if (x == SUPPORT_LO && numeratorDegreesOfFreedom <= 2) {
148 return numeratorDegreesOfFreedom == 2 ?
149 1 :
150 Double.POSITIVE_INFINITY;
151 }
152 return 0;
153 }
154 return computeDensity(x, false);
155 }
156
157
158
159
160
161
162
163
164
165
166
167 @Override
168 public double logDensity(double x) {
169 if (x <= SUPPORT_LO ||
170 x >= SUPPORT_HI) {
171
172
173 if (x == SUPPORT_LO && numeratorDegreesOfFreedom <= 2) {
174 return numeratorDegreesOfFreedom == 2 ?
175 0 :
176 Double.POSITIVE_INFINITY;
177 }
178 return Double.NEGATIVE_INFINITY;
179 }
180 return computeDensity(x, true);
181 }
182
183
184
185
186
187
188
189
190 private double computeDensity(double x, boolean log) {
191
192
193
194
195
196
197 final double n = numeratorDegreesOfFreedom;
198 final double m = denominatorDegreesOfFreedom;
199 final double nx = n * x;
200 final double z = m + nx;
201 final double y = n * m / (z * z);
202 double p;
203 if (nx > m) {
204 p = y * RegularizedBeta.derivative(m / z,
205 m / 2, n / 2);
206 } else {
207 p = y * RegularizedBeta.derivative(nx / z,
208 n / 2, m / 2);
209 }
210
211
212 if (p <= Double.MAX_VALUE && p >= Double.MIN_NORMAL) {
213 return log ? Math.log(p) : p;
214 }
215
216 p = nHalfLogNmHalfLogM + (n / 2 - 1) * Math.log(x) - logBetaNhalfMhalf -
217 ((n + m) / 2) * Math.log(z);
218 return log ? p : Math.exp(p);
219 }
220
221
222 @Override
223 public double cumulativeProbability(double x) {
224 if (x <= SUPPORT_LO) {
225 return 0;
226 } else if (x >= SUPPORT_HI) {
227 return 1;
228 }
229
230 final double n = numeratorDegreesOfFreedom;
231 final double m = denominatorDegreesOfFreedom;
232
233
234
235
236
237
238 final double nx = n * x;
239 if (nx > m) {
240 return RegularizedBeta.complement(m / (m + nx),
241 m / 2, n / 2);
242 }
243 return RegularizedBeta.value(nx / (m + nx),
244 n / 2, m / 2);
245 }
246
247
248 @Override
249 public double survivalProbability(double x) {
250 if (x <= SUPPORT_LO) {
251 return 1;
252 } else if (x >= SUPPORT_HI) {
253 return 0;
254 }
255
256 final double n = numeratorDegreesOfFreedom;
257 final double m = denominatorDegreesOfFreedom;
258
259
260
261 final double nx = n * x;
262 if (nx > m) {
263 return RegularizedBeta.value(m / (m + nx),
264 m / 2, n / 2);
265 }
266 return RegularizedBeta.complement(nx / (m + nx),
267 n / 2, m / 2);
268 }
269
270
271
272
273
274
275
276
277
278
279
280
281
282 @Override
283 public double getMean() {
284 return mean;
285 }
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300 @Override
301 public double getVariance() {
302 return variance;
303 }
304
305
306
307
308
309
310
311
312 @Override
313 public double getSupportLowerBound() {
314 return SUPPORT_LO;
315 }
316
317
318
319
320
321
322
323
324 @Override
325 public double getSupportUpperBound() {
326 return SUPPORT_HI;
327 }
328 }