/*
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.distribution.TDistribution;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
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;
/**
* Calculates a t-confidence interval estimate of the difference of means.
*
*
* @author G. Sturr
*/
public class AlgoTMean2Estimate extends AlgoElement {
private GeoList geoList1, geoList2; // input
private GeoNumeric geoLevel, geoMean1, geoSD1, geoN1, geoMean2, geoSD2,
geoN2; // input
private GeoBoolean geoPooled; // input
private GeoList result; // output
private double[] val1, val2;
private int size1, size2;
private double level, mean1, var1, n1, mean2, var2, n2, me;
boolean pooled;
private SummaryStatistics stats;
private TDistribution tDist;
private double difference;
public AlgoTMean2Estimate(Construction cons, String label, GeoList geoList1,
GeoList geoList2, GeoNumeric geoLevel, GeoBoolean geoPooled) {
super(cons);
this.geoList1 = geoList1;
this.geoList2 = geoList2;
this.geoLevel = geoLevel;
this.geoPooled = geoPooled;
this.geoMean1 = null;
this.geoSD1 = null;
this.geoN1 = null;
this.geoMean2 = null;
this.geoSD2 = null;
this.geoN2 = null;
result = new GeoList(cons);
setInputOutput(); // for AlgoElement
compute();
result.setLabel(label);
}
public AlgoTMean2Estimate(Construction cons, String label,
GeoNumeric geoMean1, GeoNumeric geoSD1, GeoNumeric geoN1,
GeoNumeric geoMean2, GeoNumeric geoSD2, GeoNumeric geoN2,
GeoNumeric geoLevel, GeoBoolean geoPooled) {
this(cons, geoMean1, geoSD1, geoN1, geoMean2, geoSD2, geoN2, geoLevel,
geoPooled);
result.setLabel(label);
}
public AlgoTMean2Estimate(Construction cons, GeoNumeric geoMean1,
GeoNumeric geoSD1, GeoNumeric geoN1, GeoNumeric geoMean2,
GeoNumeric geoSD2, GeoNumeric geoN2, GeoNumeric geoLevel,
GeoBoolean geoPooled) {
super(cons);
this.geoList1 = null;
this.geoList2 = null;
this.geoLevel = geoLevel;
this.geoPooled = geoPooled;
this.geoMean1 = geoMean1;
this.geoSD1 = geoSD1;
this.geoN1 = geoN1;
this.geoMean2 = geoMean2;
this.geoSD2 = geoSD2;
this.geoN2 = geoN2;
result = new GeoList(cons);
setInputOutput(); // for AlgoElement
compute();
}
@Override
public Commands getClassName() {
return Commands.TMean2Estimate;
}
@Override
protected void setInputOutput() {
if (geoList1 != null) {
input = new GeoElement[4];
input[0] = geoList1;
input[1] = geoList2;
input[2] = geoLevel;
input[3] = geoPooled;
} else {
input = new GeoElement[8];
input[0] = geoMean1;
input[1] = geoSD1;
input[2] = geoN1;
input[3] = geoMean2;
input[4] = geoSD2;
input[5] = geoN2;
input[6] = geoLevel;
input[7] = geoPooled;
}
setOnlyOutput(result);
setDependencies(); // done by AlgoElement
}
public GeoList getResult() {
return result;
}
/**
* Computes approximate degrees of freedom for 2-sample t-estimate. (code
* from Apache commons, TTest class)
*
* @param v1
* first sample variance
* @param v2
* second sample variance
* @param n1
* first sample n
* @param n2
* second sample n
* @return approximate degrees of freedom
*/
private static double getDegreeOfFreedom(double v1, double v2, double n1,
double n2, boolean pooled) {
if (pooled) {
return n1 + n2 - 2;
}
return (((v1 / n1) + (v2 / n2)) * ((v1 / n1) + (v2 / n2)))
/ ((v1 * v1) / (n1 * n1 * (n1 - 1d))
+ (v2 * v2) / (n2 * n2 * (n2 - 1d)));
}
/**
* Computes margin of error for 2-sample t-estimate; this is the half-width
* of the confidence interval
*
* @param v1
* first sample variance
* @param v2
* second sample variance
* @param n1
* first sample n
* @param n2
* second sample n
* @param confLevel
* confidence level
* @return margin of error for 2 mean interval estimate
* @throws ArithmeticException
*/
private double getMarginOfError(double v1, double n1, double v2, double n2,
double confLevel, boolean pooled) throws ArithmeticException {
if (pooled) {
double pooledVariance = ((n1 - 1) * v1 + (n2 - 1) * v2)
/ (n1 + n2 - 2);
double se = Math.sqrt(pooledVariance * (1d / n1 + 1d / n2));
tDist = new TDistribution(
getDegreeOfFreedom(v1, v2, n1, n2, pooled));
double a = tDist.inverseCumulativeProbability((confLevel + 1d) / 2);
return a * se;
}
double se = Math.sqrt((v1 / n1) + (v2 / n2));
tDist = new TDistribution(
getDegreeOfFreedom(v1, v2, n1, n2, pooled));
double a = tDist.inverseCumulativeProbability((confLevel + 1d) / 2);
return a * se;
}
@Override
public final void compute() {
try {
// get statistics from sample data input
if (input.length == 4) {
size1 = geoList1.size();
if (!geoList1.isDefined() || size1 < 2) {
result.setUndefined();
return;
}
size2 = geoList2.size();
if (!geoList2.isDefined() || size2 < 2) {
result.setUndefined();
return;
}
val1 = new double[size1];
for (int i = 0; i < size1; i++) {
GeoElement geo = geoList1.get(i);
if (geo instanceof NumberValue) {
NumberValue num = (NumberValue) geo;
val1[i] = num.getDouble();
} else {
result.setUndefined();
return;
}
}
val2 = new double[size2];
for (int i = 0; i < size2; i++) {
GeoElement geo = geoList2.get(i);
if (geo instanceof NumberValue) {
NumberValue num = (NumberValue) geo;
val2[i] = num.getDouble();
} else {
result.setUndefined();
return;
}
}
stats = new SummaryStatistics();
for (int i = 0; i < val1.length; i++) {
stats.addValue(val1[i]);
}
n1 = stats.getN();
var1 = stats.getVariance();
mean1 = stats.getMean();
stats.clear();
for (int i = 0; i < val2.length; i++) {
stats.addValue(val2[i]);
}
n2 = stats.getN();
var2 = stats.getVariance();
mean2 = stats.getMean();
} else {
mean1 = geoMean1.getDouble();
var1 = geoSD1.getDouble() * geoSD1.getDouble();
n1 = geoN1.getDouble();
mean2 = geoMean2.getDouble();
var2 = geoSD2.getDouble() * geoSD2.getDouble();
n2 = geoN2.getDouble();
}
level = geoLevel.getDouble();
pooled = geoPooled.getBoolean();
// validate statistics
if (level < 0 || level > 1 || var1 < 0 || n1 < 1 || var2 < 0
|| n2 < 1) {
result.setUndefined();
return;
}
// get interval estimate
me = getMarginOfError(var1, n1, var2, n2, level, pooled);
// return list = {low limit, high limit, difference, margin of
// error, df }
difference = mean1 - mean2;
result.clear();
result.addNumber(difference - me, null);
result.addNumber(difference + me, null);
// result.add(new GeoNumeric(cons, difference));
// result.add(new GeoNumeric(cons, me));
// result.add(new GeoNumeric(cons, getDegreeOfFreedom(var1, var2,
// n1, n2, pooled)));
} catch (RuntimeException e) {
// catches ArithmeticException, IllegalStateException and
// ArithmeticException
e.printStackTrace();
}
}
}