Author: psteitz Date: Mon Sep 30 20:54:28 2013 New Revision: 1527777 URL: http://svn.apache.org/r1527777 Log: Added exact binomial test implementation. Contributed by Thorsten Schaefer JIRA: MATH-1034
Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java commons/proper/math/trunk/src/test/java/org/apache/commons/math3/stat/inference/BinomialTestTest.java Modified: commons/proper/math/trunk/src/changes/changes.xml Modified: commons/proper/math/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/changes/changes.xml?rev=1527777&r1=1527776&r2=1527777&view=diff ============================================================================== --- commons/proper/math/trunk/src/changes/changes.xml (original) +++ commons/proper/math/trunk/src/changes/changes.xml Mon Sep 30 20:54:28 2013 @@ -51,6 +51,9 @@ If the output is not quite correct, chec </properties> <body> <release version="x.y" date="TBD" description="TBD"> + <action dev="psteitz" type="add" issue="MATH-1034" due-to="Thorsten Schaefer"> + Added exact binomial test implementation. + </action> <action dev="tn" type="add" issue="MATH-1018" due-to="Ajo Fod"> Added overloaded constructors for subclasses of "RealDistribution" implementations which do not require an explicit "inverseCumulativeAccuracy". The default accuracy will Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java?rev=1527777&view=auto ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java (added) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/AlternativeHypothesis.java Mon Sep 30 20:54:28 2013 @@ -0,0 +1,41 @@ +/* + * 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.math3.stat.inference; + +/** + * Represents an alternative hypothesis for a hypothesis test. + * + * @version $Id$ + * @since 3.3 + */ +public enum AlternativeHypothesis { + + /** + * Represents a two-sided test. H0: p=p0, H1: p ≠ p0 + */ + TWO_SIDED, + + /** + * Represents a right-sided test. H0: p ≤ p0, H1: p > p0. + */ + GREATER_THAN, + + /** + * Represents a left-sided test. H0: p ≥ p0, H1: p < p0. + */ + LESS_THAN +} Added: commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java?rev=1527777&view=auto ============================================================================== --- commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java (added) +++ commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/inference/BinomialTest.java Mon Sep 30 20:54:28 2013 @@ -0,0 +1,165 @@ +/* + * 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.math3.stat.inference; + +import org.apache.commons.math3.distribution.BinomialDistribution; +import org.apache.commons.math3.exception.MathIllegalArgumentException; +import org.apache.commons.math3.exception.MathInternalError; +import org.apache.commons.math3.exception.NotPositiveException; +import org.apache.commons.math3.exception.NullArgumentException; +import org.apache.commons.math3.exception.OutOfRangeException; +import org.apache.commons.math3.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. + * </p> + * + * @see <a href="http://en.wikipedia.org/wiki/Binomial_test">Binomial test (Wikipedia)</a> + * @version $Id$ + * @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. + * </ul> + * </p> + * + * @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 confidenceLevel confidence level of the test + * @return true if the null hypothesis can be rejected with confidence {@code confidenceLevel} + * @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 confidenceLevel) { + double pValue = binomialTest(numberOfTrials, numberOfSuccesses, probability, alternativeHypothesis); + return pValue < 1 - confidenceLevel; + } + + /** + * 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> + * <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. + * </ul> + * </p> + * + * @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 = new BinomialDistribution(numberOfTrials, probability); + switch (alternativeHypothesis) { + case GREATER_THAN: + return 1 - distribution.cumulativeProbability(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) { + pTotal += 2 * 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); + } + } +} Added: commons/proper/math/trunk/src/test/java/org/apache/commons/math3/stat/inference/BinomialTestTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/stat/inference/BinomialTestTest.java?rev=1527777&view=auto ============================================================================== --- commons/proper/math/trunk/src/test/java/org/apache/commons/math3/stat/inference/BinomialTestTest.java (added) +++ commons/proper/math/trunk/src/test/java/org/apache/commons/math3/stat/inference/BinomialTestTest.java Mon Sep 30 20:54:28 2013 @@ -0,0 +1,84 @@ +/* + * 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.math3.stat.inference; + +import org.apache.commons.math3.exception.MathIllegalArgumentException; +import org.apache.commons.math3.exception.NotPositiveException; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test cases for the BinomialTest class. + */ + +public class BinomialTestTest { + + protected BinomialTest testStatistic = new BinomialTest(); + + private static int successes = 51; + private static int trials = 235; + private static double probability = 1.0 / 6.0; + + @Test + public void testBinomialTestPValues() { + Assert.assertEquals(0.04375, testStatistic.binomialTest( + trials, successes, probability, AlternativeHypothesis.TWO_SIDED), 1E-4); + Assert.assertEquals(0.02654, testStatistic.binomialTest( + trials, successes, probability, AlternativeHypothesis.GREATER_THAN), 1E-4); + Assert.assertEquals(0.982, testStatistic.binomialTest( + trials, successes, probability, AlternativeHypothesis.LESS_THAN), 1E-4); + } + + @Test + public void testBinomialTestExceptions() { + try { + testStatistic.binomialTest(10, -1, 0.5, AlternativeHypothesis.TWO_SIDED); + Assert.fail("Expected not positive exception"); + } catch (NotPositiveException e) { + // expected exception; + } + + try { + testStatistic.binomialTest(10, 11, 0.5, AlternativeHypothesis.TWO_SIDED); + Assert.fail("Expected illegal argument exception"); + } catch (MathIllegalArgumentException e) { + // expected exception; + } + try { + testStatistic.binomialTest(10, 11, 0.5, null); + Assert.fail("Expected illegal argument exception"); + } catch (MathIllegalArgumentException e) { + // expected exception; + } + + } + + @Test + public void testBinomialTestAcceptReject() { + double confidenceLevel95 = 0.95; + double confidenceLevel99 = 0.99; + + Assert.assertTrue(testStatistic.binomialTest(trials, successes, probability, AlternativeHypothesis.TWO_SIDED, confidenceLevel95)); + Assert.assertTrue(testStatistic.binomialTest(trials, successes, probability, AlternativeHypothesis.GREATER_THAN, confidenceLevel95)); + Assert.assertFalse(testStatistic.binomialTest(trials, successes, probability, AlternativeHypothesis.LESS_THAN, confidenceLevel95)); + + Assert.assertFalse(testStatistic.binomialTest(trials, successes, probability, AlternativeHypothesis.TWO_SIDED, confidenceLevel99)); + Assert.assertFalse(testStatistic.binomialTest(trials, successes, probability, AlternativeHypothesis.GREATER_THAN, confidenceLevel99)); + Assert.assertFalse(testStatistic.binomialTest(trials, successes, probability, AlternativeHypothesis.LESS_THAN, confidenceLevel95)); + + } +}