| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.statistics.distribution; |
| |
| import org.apache.commons.numbers.gamma.RegularizedBeta; |
| import org.apache.commons.numbers.gamma.Erf; |
| import org.apache.commons.numbers.gamma.LogGamma; |
| |
| /** |
| * Implementation of <a href='http://en.wikipedia.org/wiki/Student's_t-distribution'>Student's t-distribution</a>. |
| */ |
| public class TDistribution extends AbstractContinuousDistribution { |
| /** 2. */ |
| private static final double TWO = 2; |
| /** 1 / sqrt(2). */ |
| private static final double ONE_OVER_SQRT_TWO = 1 / Math.sqrt(2); |
| /** Number of degrees of freedom above which to use the normal distribution. */ |
| private static final double DOF_THRESHOLD_NORMAL = 2.99e6; |
| |
| /** The degrees of freedom. */ |
| private final double degreesOfFreedom; |
| /** degreesOfFreedom / 2. */ |
| private final double dofOver2; |
| /** Cached value. */ |
| private final double factor; |
| /** Cached value. */ |
| private final double mean; |
| /** Cached value. */ |
| private final double variance; |
| |
| /** |
| * Creates a distribution. |
| * |
| * @param degreesOfFreedom Degrees of freedom. |
| * @throws IllegalArgumentException if {@code degreesOfFreedom <= 0} |
| */ |
| public TDistribution(double degreesOfFreedom) { |
| if (degreesOfFreedom <= 0) { |
| throw new DistributionException(DistributionException.NOT_STRICTLY_POSITIVE, |
| degreesOfFreedom); |
| } |
| this.degreesOfFreedom = degreesOfFreedom; |
| |
| dofOver2 = 0.5 * degreesOfFreedom; |
| factor = LogGamma.value(dofOver2 + 0.5) - |
| 0.5 * (Math.log(Math.PI) + Math.log(degreesOfFreedom)) - |
| LogGamma.value(dofOver2); |
| if (degreesOfFreedom > TWO) { |
| mean = 0; |
| variance = degreesOfFreedom / (degreesOfFreedom - 2); |
| } else if (degreesOfFreedom > 1) { |
| mean = 0; |
| variance = Double.POSITIVE_INFINITY; |
| } else { |
| mean = Double.NaN; |
| variance = Double.NaN; |
| } |
| } |
| |
| /** |
| * Access the degrees of freedom. |
| * |
| * @return the degrees of freedom. |
| */ |
| public double getDegreesOfFreedom() { |
| return degreesOfFreedom; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public double density(double x) { |
| return Math.exp(logDensity(x)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public double logDensity(double x) { |
| final double nPlus1Over2 = dofOver2 + 0.5; |
| return factor - nPlus1Over2 * Math.log1p(x * x / degreesOfFreedom); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public double cumulativeProbability(double x) { |
| if (x == 0) { |
| return 0.5; |
| } else { |
| if (degreesOfFreedom > DOF_THRESHOLD_NORMAL) { |
| return 0.5 * (1 + Erf.value(x * ONE_OVER_SQRT_TWO)); |
| } else { |
| final double a = 1 / (1 + x * x / degreesOfFreedom); |
| final double t = RegularizedBeta.value(a, dofOver2, 0.5); |
| |
| return x < 0 ? |
| 0.5 * t : |
| 1 - 0.5 * t; |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * For degrees of freedom parameter {@code df}, the mean is |
| * <ul> |
| * <li>zero if {@code df > 1}, and</li> |
| * <li>undefined ({@code Double.NaN}) otherwise.</li> |
| * </ul> |
| */ |
| @Override |
| public double getMean() { |
| return mean; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * For degrees of freedom parameter {@code df}, the variance is |
| * <ul> |
| * <li>{@code df / (df - 2)} if {@code df > 2},</li> |
| * <li>infinite ({@code Double.POSITIVE_INFINITY}) if {@code 1 < df <= 2}, and</li> |
| * <li>undefined ({@code Double.NaN}) otherwise.</li> |
| * </ul> |
| */ |
| @Override |
| public double getVariance() { |
| return variance; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * The lower bound of the support is always negative infinity.. |
| * |
| * @return lower bound of the support (always |
| * {@code Double.NEGATIVE_INFINITY}) |
| */ |
| @Override |
| public double getSupportLowerBound() { |
| return Double.NEGATIVE_INFINITY; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * The upper bound of the support is always positive infinity. |
| * |
| * @return upper bound of the support (always |
| * {@code Double.POSITIVE_INFINITY}) |
| */ |
| @Override |
| public double getSupportUpperBound() { |
| return Double.POSITIVE_INFINITY; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * The support of this distribution is connected. |
| * |
| * @return {@code true} |
| */ |
| @Override |
| public boolean isSupportConnected() { |
| return true; |
| } |
| } |