Java tutorial
/* Copyright 2009-2015 David Hadka * * This file is part of the MOEA Framework. * * The MOEA Framework is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * The MOEA Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the MOEA Framework. If not, see <http://www.gnu.org/licenses/>. */ package iDynoOptimizer.MOEAFramework26.src.org.moeaframework.util.statistics; import java.io.Serializable; import java.util.Comparator; import org.apache.commons.math3.distribution.NormalDistribution; import iDynoOptimizer.MOEAFramework26.src.org.moeaframework.core.Settings; /** * The Wilcoxon Signed-Ranks test determines if the population median is equal * to a specified value. * <p> * <ul> * <li>Null Hypothesis: The population median equals X.</li> * <li>Alternative Hypothesis: The population median does not equal X.</li> * </ul> * <p> * Assumptions: * <ol> * <li>Samples are randomly selected from the population</li> * <li>The underlying population distribution is symmetrical</li> * </ol> * <p> * References: * <ol> * <li>Sheskin, D.J. "Handbook of Parametric and Nonparametric Statistical * Procedures, Third Edition." Chapman & Hall/CRC. 2004. * </ol> */ public class WilcoxonSignedRanksTest extends OrdinalStatisticalTest { /** * Compares observations based on their absolute value. */ private static class AbsoluteObservationComparator implements Comparator<RankedObservation>, Serializable { private static final long serialVersionUID = 7337112773629454794L; @Override public int compare(RankedObservation o1, RankedObservation o2) { double v1 = Math.abs(o1.getValue()); double v2 = Math.abs(o2.getValue()); if (v1 < v2) { return -1; } else if (v1 > v2) { return 1; } else { return 0; } } } /** * The value being tested against the population median. */ private final double median; /** * The value of {@code T} from the last invocation of {@link #test}. This * is package private and intended only for testing. */ double lastT; /** * Constructs a Wilcoxon signed ranks test with the specified median. * * @param median the value being tested against the population median */ public WilcoxonSignedRanksTest(double median) { super(1, new AbsoluteObservationComparator()); this.median = median; } /** * Returns the value being tested against the population median. * * @return the value being tested against the population median */ public double getMedian() { return median; } /** * Adds a new observation with the specified value. * * @param value the value of the new observation */ public void add(double value) { if (value - median != 0.0) { super.add(value - median, 0); } } /** * Adds several new observations with the specified values. * * @param values the values of the new observations */ public void add(double[] values) { for (double value : values) { add(value); } } /** * {@inheritDoc} * <p> * When the samples from both populations are less than 20, only alpha * values of 0.05 and 0.01 are valid. This is because a table is used to * accurately determine the critical values. When more than 20 samples are * available, the normal approximation is used allowing any value for alpha. * * @throws IllegalArgumentException if an insufficient sampling size is * provided, or if an invalid alpha value is provided */ @Override public boolean test(double alpha) { double Rpos = 0.0; double Rneg = 0.0; update(); for (RankedObservation observation : data) { if (observation.getValue() < 0.0) { Rneg += observation.getRank(); } else { Rpos += observation.getRank(); } } int n = data.size(); double T = Math.min(Rpos, Rneg); // expose T for testing lastT = T; if (n <= 50) { return T <= getCriticalTValueFromTable(n, alpha); } else { double z = 0.0; NormalDistribution dist = new NormalDistribution(); if (Settings.isContinuityCorrection()) { z = (Math.abs(T - n * (n + 1) / 4.0) - 0.5) / Math.sqrt(n * (n + 1) * (n + n + 1) / 24.0); } else { z = (T - n * (n + 1) / 4.0) / Math.sqrt(n * (n + 1) * (n + n + 1) / 24.0); } return Math.abs(z) >= Math.abs(dist.inverseCumulativeProbability(alpha)); } } /** * Returns the critical T value from the lookup tables. * * @param n1 the number of samples from the first group * @param n2 the number of samples from the second group * @param alpha the prespecified level of confidence; only values of 0.05 * and 0.01 are permitted * @return the critical U value from the lookup tables * @throws IllegalArgumentException if an insufficient sampling size is * provided, or if an invalid alpha value is provided */ private static int getCriticalTValueFromTable(int n, double alpha) { if ((n < 6) || (n > 50)) { throw new IllegalArgumentException("only valid for 6 <= n <= 50"); } int value = -1; if (alpha == 0.05) { value = TABLE_5[n - 6]; } else if (alpha == 0.01) { value = TABLE_1[n - 6]; } else { throw new IllegalArgumentException("only valid for 0.05 or 0.01"); } if (value == -1) { throw new IllegalArgumentException("insufficient sampling size"); } return value; } /** * Table of critical T values for alpha=0.05. Entries of -1 indicate an * insufficient sampling size. */ private static final int[] TABLE_5 = new int[] { 0, 2, 3, 5, 8, 10, 13, 17, 21, 25, 29, 34, 40, 46, 52, 58, 65, 73, 81, 89, 98, 107, 116, 126, 137, 147, 159, 170, 182, 195, 208, 221, 235, 249, 264, 279, 294, 310, 327, 343, 361, 378, 396, 415, 434 }; /** * Table of critical T values for alpha=0.01. Entries of -1 indicate an * insufficient sampling size. */ private static final int[] TABLE_1 = new int[] { -1, -1, 0, 1, 3, 5, 7, 9, 12, 15, 19, 23, 27, 32, 37, 42, 48, 54, 61, 68, 75, 83, 91, 100, 109, 118, 128, 138, 148, 159, 171, 182, 194, 207, 220, 233, 247, 261, 276, 291, 307, 322, 339, 355, 373 }; }