package org.geogebra.common.gui.view.data; import java.util.ArrayList; import java.util.Collection; import org.apache.commons.math3.distribution.FDistribution; import org.apache.commons.math3.exception.MathRuntimeException; import org.apache.commons.math3.stat.descriptive.summary.Sum; import org.apache.commons.math3.stat.descriptive.summary.SumOfSquares; import org.geogebra.common.kernel.arithmetic.NumberValue; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoList; import org.geogebra.common.main.App; import org.geogebra.common.main.Localization; public class ANOVAStatTableModel extends StatTableModel { public static ArrayList<double[]> getCategoryData(GeoList geoList) { // create an array list of data arrays ArrayList<double[]> categoryData = new ArrayList<double[]>(); // load the data arrays from the input GeoList GeoList list; for (int index = 0; index < geoList.size(); index++) { list = (GeoList) geoList.get(index); double[] valueArray = new double[list.size()]; for (int i = 0; i < list.size(); i++) { GeoElement geo = list.get(i); if (geo instanceof NumberValue) { NumberValue num = (NumberValue) geo; valueArray[i] = num.getDouble(); } } categoryData.add(valueArray); // System.out.println(Arrays.toString(valueArray)); } return categoryData; } /** * Calculates ANOVA stats. (Modified form of method found in Apache Commons * OneWayAnovaImpl) * * @param categoryData * <code>Collection</code> of <code>double[]</code> arrays each * containing data for one category * @return computed AnovaStats * @throws IllegalArgumentException * if categoryData does not meet preconditions specified in the * interface definition * @throws MathException * if an error occurs computing the Anova stats */ public static AnovaStats anovaStats(Collection<double[]> categoryData) throws IllegalArgumentException, ArithmeticException { // check if we have enough categories if (categoryData.size() < 2) { throw MathRuntimeException.createIllegalArgumentException( // LocalizedFormats.TWO_OR_MORE_CATEGORIES_REQUIRED, "two or more categories required, got {0}", categoryData.size()); } // check if each category has enough data and all is double[] for (double[] array : categoryData) { if (array.length <= 1) { throw MathRuntimeException.createIllegalArgumentException( // LocalizedFormats.TWO_OR_MORE_VALUES_IN_CATEGORY_REQUIRED, "two or more values required in each category, one has {0}", array.length); } } int dfwg = 0; double sswg = 0; Sum totsum = new Sum(); SumOfSquares totsumsq = new SumOfSquares(); int totnum = 0; for (double[] data : categoryData) { Sum sum = new Sum(); SumOfSquares sumsq = new SumOfSquares(); int num = 0; for (int i = 0; i < data.length; i++) { double val = data[i]; // within category num++; sum.increment(val); sumsq.increment(val); // for all categories totnum++; totsum.increment(val); totsumsq.increment(val); } dfwg += num - 1; double ss = sumsq.getResult() - sum.getResult() * sum.getResult() / num; sswg += ss; } double sst = totsumsq.getResult() - totsum.getResult() * totsum.getResult() / totnum; double ssbg = sst - sswg; int dfbg = categoryData.size() - 1; double msbg = ssbg / dfbg; double mswg = sswg / dfwg; double F = msbg / mswg; FDistribution fdist = new FDistribution(dfbg, dfwg); double P = 1.0 - fdist.cumulativeProbability(F); return new AnovaStats(dfbg, dfwg, F, P, ssbg, sswg, sst, msbg, mswg); } /** * Calculates ANOVA stats. (Modified form of method found in Apache Commons * OneWayAnovaImpl) * * @param categoryData * <code>Collection</code> of <code>double[]</code> arrays each * containing data for one category * @return computed AnovaStats */ public static AnovaStats anovaStatsSilent( Collection<double[]> categoryData) { try { return anovaStats(categoryData); } catch (RuntimeException e) { // catches ArithmeticException, IllegalStateException and // ArithmeticException e.printStackTrace(); } return null; } /** * Convenience class to pass dfbg,dfwg,F values around within AnovaImpl. * (Modified form of class found in OneWayAnovaImpl) */ public static class AnovaStats { /** Degrees of freedom in numerator (between groups). */ private int dfbg; /** Degrees of freedom in denominator (within groups). */ private int dfwg; /** F test statistic. */ private double F; /** sum of squares */ private double ssbg, sswg, sst; /** mean squares */ private double msbg, mswg; /** P value */ private double P; /** * Constructor * * @param dfbg * degrees of freedom in numerator (between groups) * @param dfwg * degrees of freedom in denominator (within groups) * @param F * statistic */ public AnovaStats(int dfbg, int dfwg, double F, double P, double ssbg, double sswg, double sst, double msbg, double mswg) { this.setDfbg(dfbg); this.setDfwg(dfwg); this.setF(F); this.setP(P); this.setSsbg(ssbg); this.setSswg(sswg); this.setSst(sst); this.setMsbg(msbg); this.setMswg(mswg); } public int getDfbg() { return dfbg; } public void setDfbg(int dfbg) { this.dfbg = dfbg; } public int getDfwg() { return dfwg; } public void setDfwg(int dfwg) { this.dfwg = dfwg; } public double getSsbg() { return ssbg; } public void setSsbg(double ssbg) { this.ssbg = ssbg; } public double getSswg() { return sswg; } public void setSswg(double sswg) { this.sswg = sswg; } public double getSst() { return sst; } public void setSst(double sst) { this.sst = sst; } public double getMsbg() { return msbg; } public void setMsbg(double msbg) { this.msbg = msbg; } public double getMswg() { return mswg; } public void setMswg(double mswg) { this.mswg = mswg; } public double getF() { return F; } public void setF(double f) { F = f; } public double getP() { return P; } public void setP(double p) { P = p; } } public static AnovaStats getStatsSilent(GeoList dataList) { return anovaStatsSilent(getCategoryData(dataList)); } public ANOVAStatTableModel(App app, StatTableListener listener) { super(app, listener); } @Override public String[] getRowNames() { Localization loc = getApp().getLocalization(); String[] names = { loc.getMenu("BetweenGroups"), loc.getMenu("WithinGroups"), loc.getMenu("Total"), }; return names; } @Override public String[] getColumnNames() { Localization loc = getApp().getLocalization(); String[] names = { loc.getMenu("DegreesOfFreedom.short"), loc.getMenu("SumSquares.short"), loc.getMenu("MeanSquare.short"), loc.getMenu("FStatistic"), loc.getMenu("PValue"), }; return names; } @Override public int getRowCount() { return getRowNames().length; } @Override public int getColumnCount() { return getColumnNames().length; } }