| /* |
| * 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.math4.legacy.stat.inference; |
| |
| import org.apache.commons.statistics.distribution.BinomialDistribution; |
| import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException; |
| import org.apache.commons.math4.legacy.exception.MathInternalError; |
| import org.apache.commons.math4.legacy.exception.NotPositiveException; |
| import org.apache.commons.math4.legacy.exception.NullArgumentException; |
| import org.apache.commons.math4.legacy.exception.OutOfRangeException; |
| import org.apache.commons.math4.legacy.exception.util.LocalizedFormats; |
| |
| /** |
| * Implements binomial test statistics. |
| * <p> |
| * Exact test for the statistical significance of deviations from a |
| * theoretically expected distribution of observations into two categories. |
| * |
| * @see <a href="http://en.wikipedia.org/wiki/Binomial_test">Binomial test (Wikipedia)</a> |
| * @since 3.3 |
| */ |
| public class BinomialTest { |
| |
| /** |
| * Returns whether the null hypothesis can be rejected with the given confidence level. |
| * <p> |
| * <strong>Preconditions</strong>: |
| * <ul> |
| * <li>Number of trials must be ≥ 0.</li> |
| * <li>Number of successes must be ≥ 0.</li> |
| * <li>Number of successes must be ≤ number of trials.</li> |
| * <li>Probability must be ≥ 0 and ≤ 1.</li> |
| * </ul> |
| * |
| * @param numberOfTrials number of trials performed |
| * @param numberOfSuccesses number of successes observed |
| * @param probability assumed probability of a single trial under the null hypothesis |
| * @param alternativeHypothesis type of hypothesis being evaluated (one- or two-sided) |
| * @param alpha significance level of the test |
| * @return true if the null hypothesis can be rejected with confidence {@code 1 - alpha} |
| * @throws NotPositiveException if {@code numberOfTrials} or {@code numberOfSuccesses} is negative |
| * @throws OutOfRangeException if {@code probability} is not between 0 and 1 |
| * @throws MathIllegalArgumentException if {@code numberOfTrials} < {@code numberOfSuccesses} or |
| * if {@code alternateHypothesis} is null. |
| * @see AlternativeHypothesis |
| */ |
| public boolean binomialTest(int numberOfTrials, int numberOfSuccesses, double probability, |
| AlternativeHypothesis alternativeHypothesis, double alpha) { |
| double pValue = binomialTest(numberOfTrials, numberOfSuccesses, probability, alternativeHypothesis); |
| return pValue < alpha; |
| } |
| |
| /** |
| * Returns the <i>observed significance level</i>, or |
| * <a href="http://www.cas.lancs.ac.uk/glossary_v1.1/hyptest.html#pvalue">p-value</a>, |
| * associated with a <a href="http://en.wikipedia.org/wiki/Binomial_test"> Binomial test</a>. |
| * <p> |
| * The number returned is the smallest significance level at which one can reject the null hypothesis. |
| * The form of the hypothesis depends on {@code alternativeHypothesis}.</p> |
| * <p> |
| * The p-Value represents the likelihood of getting a result at least as extreme as the sample, |
| * given the provided {@code probability} of success on a single trial. For single-sided tests, |
| * this value can be directly derived from the Binomial distribution. For the two-sided test, |
| * the implementation works as follows: we start by looking at the most extreme cases |
| * (0 success and n success where n is the number of trials from the sample) and determine their likelihood. |
| * The lower value is added to the p-Value (if both values are equal, both are added). Then we continue with |
| * the next extreme value, until we added the value for the actual observed sample.</p> |
| * <p> |
| * <strong>Preconditions</strong>: |
| * <ul> |
| * <li>Number of trials must be ≥ 0.</li> |
| * <li>Number of successes must be ≥ 0.</li> |
| * <li>Number of successes must be ≤ number of trials.</li> |
| * <li>Probability must be ≥ 0 and ≤ 1.</li> |
| * </ul> |
| * |
| * @param numberOfTrials number of trials performed |
| * @param numberOfSuccesses number of successes observed |
| * @param probability assumed probability of a single trial under the null hypothesis |
| * @param alternativeHypothesis type of hypothesis being evaluated (one- or two-sided) |
| * @return p-value |
| * @throws NotPositiveException if {@code numberOfTrials} or {@code numberOfSuccesses} is negative |
| * @throws OutOfRangeException if {@code probability} is not between 0 and 1 |
| * @throws MathIllegalArgumentException if {@code numberOfTrials} < {@code numberOfSuccesses} or |
| * if {@code alternateHypothesis} is null. |
| * @see AlternativeHypothesis |
| */ |
| public double binomialTest(int numberOfTrials, int numberOfSuccesses, double probability, |
| AlternativeHypothesis alternativeHypothesis) { |
| if (numberOfTrials < 0) { |
| throw new NotPositiveException(numberOfTrials); |
| } |
| if (numberOfSuccesses < 0) { |
| throw new NotPositiveException(numberOfSuccesses); |
| } |
| if (probability < 0 || probability > 1) { |
| throw new OutOfRangeException(probability, 0, 1); |
| } |
| if (numberOfTrials < numberOfSuccesses) { |
| throw new MathIllegalArgumentException( |
| LocalizedFormats.BINOMIAL_INVALID_PARAMETERS_ORDER, |
| numberOfTrials, numberOfSuccesses); |
| } |
| if (alternativeHypothesis == null) { |
| throw new NullArgumentException(); |
| } |
| |
| final BinomialDistribution distribution = BinomialDistribution.of(numberOfTrials, probability); |
| switch (alternativeHypothesis) { |
| case GREATER_THAN: |
| return distribution.survivalProbability(numberOfSuccesses - 1); |
| case LESS_THAN: |
| return distribution.cumulativeProbability(numberOfSuccesses); |
| case TWO_SIDED: |
| int criticalValueLow = 0; |
| int criticalValueHigh = numberOfTrials; |
| double pTotal = 0; |
| |
| while (true) { |
| double pLow = distribution.probability(criticalValueLow); |
| double pHigh = distribution.probability(criticalValueHigh); |
| |
| if (pLow == pHigh) { |
| if (criticalValueLow == criticalValueHigh) { |
| pTotal += pLow; |
| } else { |
| pTotal += 2 * Math.nextDown(pLow); |
| } |
| criticalValueLow++; |
| criticalValueHigh--; |
| } else if (pLow < pHigh) { |
| pTotal += pLow; |
| criticalValueLow++; |
| } else { |
| pTotal += pHigh; |
| criticalValueHigh--; |
| } |
| |
| if (criticalValueLow > numberOfSuccesses || criticalValueHigh < numberOfSuccesses) { |
| break; |
| } |
| } |
| return pTotal; |
| default: |
| throw new MathInternalError(LocalizedFormats. OUT_OF_RANGE_SIMPLE, alternativeHypothesis, |
| AlternativeHypothesis.TWO_SIDED, AlternativeHypothesis.LESS_THAN); |
| } |
| } |
| } |