/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation.
*/
package org.geogebra.common.kernel.statistics;
import org.apache.commons.math3.stat.descriptive.StatisticalSummaryValues;
import org.apache.commons.math3.stat.inference.TTest;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.arithmetic.NumberValue;
import org.geogebra.common.kernel.commands.Commands;
import org.geogebra.common.kernel.geos.GeoBoolean;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.util.StringUtil;
/**
* Performs a two sample t-test of the difference of means.
*
* @author G. Sturr
*/
public class AlgoTTest2 extends AlgoElement {
private GeoList geoList0, geoList1; // input
private GeoNumeric mean0, mean1, sd0, sd1, n0, n1; // input
private GeoText tail; // input
private GeoBoolean pooled; // input
private GeoList result; // output
private TTest tTestImpl;
private double[] val0, val1;
public AlgoTTest2(Construction cons, String label, GeoList geoList0,
GeoList geoList1, GeoText tail, GeoBoolean pooled) {
super(cons);
this.geoList0 = geoList0;
this.geoList1 = geoList1;
this.tail = tail;
this.pooled = pooled;
result = new GeoList(cons);
setInputOutput(); // for AlgoElement
compute();
result.setLabel(label);
}
public AlgoTTest2(Construction cons, String label, GeoNumeric mean0,
GeoNumeric sd0, GeoNumeric n0, GeoNumeric mean1, GeoNumeric sd1,
GeoNumeric n1, GeoText tail, GeoBoolean pooled) {
this(cons, mean0, sd0, n0, mean1, sd1, n1, tail, pooled);
result.setLabel(label);
}
public AlgoTTest2(Construction cons, GeoNumeric mean0, GeoNumeric sd0,
GeoNumeric n0, GeoNumeric mean1, GeoNumeric sd1, GeoNumeric n1,
GeoText tail, GeoBoolean pooled) {
super(cons);
this.mean0 = mean0;
this.mean1 = mean1;
this.sd0 = sd0;
this.sd1 = sd1;
this.n0 = n0;
this.n1 = n1;
this.tail = tail;
this.pooled = pooled;
result = new GeoList(cons);
setInputOutput(); // for AlgoElement
compute();
}
@Override
public Commands getClassName() {
return Commands.TTest2;
}
@Override
protected void setInputOutput() {
if (geoList0 != null) {
input = new GeoElement[4];
input[0] = geoList0;
input[1] = geoList1;
input[2] = tail;
input[3] = pooled;
} else {
input = new GeoElement[8];
input[0] = mean0;
input[1] = sd0;
input[2] = n0;
input[3] = mean1;
input[4] = sd1;
input[5] = n1;
input[6] = tail;
input[7] = pooled;
}
setOnlyOutput(result);
setDependencies(); // done by AlgoElement
}
public GeoList getResult() {
return result;
}
private double adjustedPValue(double p, double testStatistic) {
// two sided test
if (StringUtil.isNotEqual(tail.getTextString())) {
return p;
}
// one sided test
else if ((tail.getTextString().equals(">") && testStatistic > 0)
|| (tail.getTextString().equals("<") && testStatistic < 0)) {
return p / 2;
} else {
return 1 - p / 2;
}
}
@Override
public final void compute() {
if (!(StringUtil.isInequality(tail.getTextString()))) {
result.setUndefined();
return;
}
double p, testStat;
// sample data input
if (input.length == 4) {
int size0 = geoList0.size();
if (!geoList0.isDefined() || size0 < 2) {
result.setUndefined();
return;
}
int size1 = geoList1.size();
if (!geoList1.isDefined() || size1 < 2) {
result.setUndefined();
return;
}
val0 = new double[size0];
val1 = new double[size1];
// load array from first sample
for (int i = 0; i < size0; i++) {
GeoElement geo0 = geoList0.get(i);
if (geo0 instanceof NumberValue) {
NumberValue num = (NumberValue) geo0;
val0[i] = num.getDouble();
} else {
result.setUndefined();
return;
}
}
// load array from second sample
for (int i = 0; i < size1; i++) {
GeoElement geo1 = geoList1.get(i);
if (geo1 instanceof NumberValue) {
NumberValue num = (NumberValue) geo1;
val1[i] = num.getDouble();
} else {
result.setUndefined();
return;
}
}
try {
// get the test statistic and p
if (tTestImpl == null) {
tTestImpl = new TTest();
}
if (pooled.getBoolean()) {
testStat = tTestImpl.homoscedasticT(val0, val1);
p = tTestImpl.homoscedasticTTest(val0, val1);
p = adjustedPValue(p, testStat);
} else {
testStat = tTestImpl.t(val0, val1);
p = tTestImpl.tTest(val0, val1);
p = adjustedPValue(p, testStat);
}
// put these results into the output list
result.clear();
result.addNumber(p, null);
result.addNumber(testStat, null);
} catch (RuntimeException e) {
// catches ArithmeticException, IllegalStateException and
// ArithmeticException
e.printStackTrace();
}
// sample statistics input
} else {
// check for valid stand. deviation and sample size
if (sd0.getDouble() < 0 || sd1.getDouble() < 0 || n0.getDouble() < 2
|| n1.getDouble() < 2) {
result.setUndefined();
return;
}
StatisticalSummaryValues sumStats0 = new StatisticalSummaryValues(
mean0.getDouble(), sd0.getDouble() * sd0.getDouble(),
(long) n0.getDouble(), -1, -1, -1);
StatisticalSummaryValues sumStats1 = new StatisticalSummaryValues(
mean1.getDouble(), sd1.getDouble() * sd1.getDouble(),
(long) n1.getDouble(), -1, -1, -1);
try {
// get the test statistic and p
if (tTestImpl == null) {
tTestImpl = new TTest();
}
if (pooled.getBoolean()) {
testStat = tTestImpl.homoscedasticT(sumStats0, sumStats1);
p = tTestImpl.homoscedasticTTest(sumStats0, sumStats1);
p = adjustedPValue(p, testStat);
} else {
testStat = tTestImpl.t(sumStats0, sumStats1);
p = tTestImpl.tTest(sumStats0, sumStats1);
p = adjustedPValue(p, testStat);
}
// put these results into the output list
result.clear();
result.addNumber(p, null);
result.addNumber(testStat, null);
} catch (RuntimeException e) {
// catches ArithmeticException, IllegalStateException and
// ArithmeticException
e.printStackTrace();
}
}
}
}