package org.geogebra.common.gui.view.data;
import java.util.ArrayList;
import org.geogebra.common.gui.view.data.DataAnalysisModel.ICreateColor;
import org.geogebra.common.gui.view.data.DataAnalysisModel.Regression;
import org.geogebra.common.gui.view.data.DataDisplayModel.PlotType;
import org.geogebra.common.gui.view.data.DataVariable.GroupType;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.advanced.AlgoUnique;
import org.geogebra.common.kernel.algos.AlgoBarChart;
import org.geogebra.common.kernel.algos.AlgoBoxPlot;
import org.geogebra.common.kernel.algos.AlgoDependentList;
import org.geogebra.common.kernel.algos.AlgoDependentListExpression;
import org.geogebra.common.kernel.algos.AlgoElement;
import org.geogebra.common.kernel.algos.AlgoFunctionAreaSums;
import org.geogebra.common.kernel.algos.AlgoListElement;
import org.geogebra.common.kernel.algos.AlgoListMax;
import org.geogebra.common.kernel.algos.AlgoListMin;
import org.geogebra.common.kernel.algos.AlgoPolyLine;
import org.geogebra.common.kernel.algos.AlgoText;
import org.geogebra.common.kernel.algos.ConstructionElement;
import org.geogebra.common.kernel.arithmetic.ExpressionNode;
import org.geogebra.common.kernel.arithmetic.FunctionVariable;
import org.geogebra.common.kernel.arithmetic.MyDouble;
import org.geogebra.common.kernel.geos.GeoBoolean;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.kernel.geos.GeoFunctionable;
import org.geogebra.common.kernel.geos.GeoList;
import org.geogebra.common.kernel.geos.GeoNumeric;
import org.geogebra.common.kernel.geos.GeoPoint;
import org.geogebra.common.kernel.geos.GeoText;
import org.geogebra.common.kernel.kernelND.GeoElementND;
import org.geogebra.common.kernel.kernelND.GeoPointND;
import org.geogebra.common.kernel.statistics.AlgoClasses;
import org.geogebra.common.kernel.statistics.AlgoDotPlot;
import org.geogebra.common.kernel.statistics.AlgoFitExp;
import org.geogebra.common.kernel.statistics.AlgoFitGrowth;
import org.geogebra.common.kernel.statistics.AlgoFitLog;
import org.geogebra.common.kernel.statistics.AlgoFitLogistic;
import org.geogebra.common.kernel.statistics.AlgoFitPoly;
import org.geogebra.common.kernel.statistics.AlgoFitPow;
import org.geogebra.common.kernel.statistics.AlgoFitSin;
import org.geogebra.common.kernel.statistics.AlgoFrequencyTable;
import org.geogebra.common.kernel.statistics.AlgoHistogram;
import org.geogebra.common.kernel.statistics.AlgoMean;
import org.geogebra.common.kernel.statistics.AlgoNormalQuantilePlot;
import org.geogebra.common.kernel.statistics.AlgoResidualPlot;
import org.geogebra.common.kernel.statistics.AlgoStandardDeviation;
import org.geogebra.common.kernel.statistics.AlgoStemPlot;
import org.geogebra.common.main.App;
import org.geogebra.common.plugin.Operation;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
*
* Creates geos for use in plot panels and provides updates to plot panel
* settings based on these geos.
*
*/
public class StatGeo {
private static final double DEFAULT_BUFFER = 0.01;
private Kernel kernel;
private Construction cons;
private double xMinData, xMaxData, yMinData, yMaxData;
private boolean histogramRight;
private boolean removeFromConstruction = true;
private ICreateColor listener;
/*************************************************
* Constructs a GeoPlot instance
*/
public StatGeo(App app, ICreateColor listener) {
kernel = app.getKernel();
cons = kernel.getConstruction();
this.listener = listener;
}
// =================================================
// Plots and Updates
// =================================================
// Note:
// All GeoElements are constructed without labels. If the
// removeFromConstruction flag is set to true, all AlgoElements are removed
// from the construction list.
//
// Whenever a GeoElement is not labeled and serves as input for some
// AlgoElement, then it may be removed when that AlgoElement is removed (see
// AlgoElement.remove()). For this reason all AlgoElements that use
// the dataList as input call algo.setProtectedInput(true) to prevent
// the problem of all GeoElements depending on the dataList from being
// removed when just one such geo is removed.
private void getDataBounds(GeoList dataList) {
getDataBounds(dataList, false, false);
}
private void getDataBoundsForPointList(GeoList dataList) {
getDataBounds(dataList, true, false);
}
private void getDataBounds(GeoList dataList, boolean isPointList,
boolean isMatrix) {
if (dataList == null) {
return;
}
// construction elements created by this method should always be
// removed from the construction
boolean currentRemoveFromConstructionStatus = removeFromConstruction;
removeFromConstruction = true;
// String label = dataList.getLabel();
double[] dataBounds = new double[4];
if (isMatrix) {
// create index for iterating through the lists in the matrix
GeoNumeric index = new GeoNumeric(cons, 1);
// create algo for extracting a list from the matrix that depends on
// index
AlgoListElement le = new AlgoListElement(cons, dataList, index);
removeFromConstructionList(le);
// create list from this algo
GeoList list = (GeoList) le.getOutput(0);
// create algos to find the max and min values of the list
AlgoListMax maxAlgo = new AlgoListMax(cons, list);
AlgoListMin minAlgo = new AlgoListMin(cons, list);
removeFromConstructionList(minAlgo);
removeFromConstructionList(maxAlgo);
// create max/min geos from the max/min algos
GeoNumeric maxGeo = (GeoNumeric) maxAlgo.getOutput(0);
GeoNumeric minGeo = (GeoNumeric) minAlgo.getOutput(0);
// initialize the data bounds with max/min from the first list
dataBounds[0] = ((GeoNumeric) minAlgo.getOutput(0)).getDouble();
dataBounds[1] = ((GeoNumeric) maxAlgo.getOutput(0)).getDouble();
// iterate through the remaining lists to find the max/min for the
// matrix
double min, max;
for (int i = 1; i < dataList.size(); i++) {
index.setValue(i + 1); // use i+1 because Element[] uses 1-based
// counting
index.updateCascade();
min = minGeo.getDouble();
max = maxGeo.getDouble();
dataBounds[0] = Math.min(dataBounds[0], min);
dataBounds[1] = Math.max(dataBounds[1], max);
}
}
else if (isPointList) {
ExpressionNode enX = new ExpressionNode(kernel, dataList,
Operation.XCOORD, null);
ExpressionNode enY = new ExpressionNode(kernel, dataList,
Operation.YCOORD, null);
AlgoDependentListExpression listX = new AlgoDependentListExpression(
cons, enX);
AlgoDependentListExpression listY = new AlgoDependentListExpression(
cons, enY);
AlgoListMax maxX = new AlgoListMax(cons,
(GeoList) listX.getOutput(0));
AlgoListMax maxY = new AlgoListMax(cons,
(GeoList) listY.getOutput(0));
AlgoListMin minX = new AlgoListMin(cons,
(GeoList) listX.getOutput(0));
AlgoListMin minY = new AlgoListMin(cons,
(GeoList) listY.getOutput(0));
listX.getOutput()[0].setSelectionAllowed(false);
listY.getOutput()[0].setSelectionAllowed(false);
removeFromConstructionList(listX);
removeFromConstructionList(listY);
removeFromConstructionList(maxX);
removeFromConstructionList(maxY);
removeFromConstructionList(minX);
removeFromConstructionList(minY);
dataBounds[0] = ((GeoNumeric) minX.getOutput(0)).getDouble();
dataBounds[1] = ((GeoNumeric) maxX.getOutput(0)).getDouble();
dataBounds[2] = ((GeoNumeric) minY.getOutput(0)).getDouble();
dataBounds[3] = ((GeoNumeric) maxY.getOutput(0)).getDouble();
} else {
AlgoListMax max = new AlgoListMax(cons, dataList);
AlgoListMin min = new AlgoListMin(cons, dataList);
removeFromConstructionList(min);
removeFromConstructionList(max);
dataBounds[0] = ((GeoNumeric) min.getOutput(0)).getDouble();
dataBounds[1] = ((GeoNumeric) max.getOutput(0)).getDouble();
}
xMinData = dataBounds[0];
xMaxData = dataBounds[1];
yMinData = dataBounds[2];
yMaxData = dataBounds[3];
// restore the removeFromConstruction flag
removeFromConstruction = currentRemoveFromConstructionStatus;
}
public GeoElementND createHistogram(GeoList dataList,
StatPanelSettings settings, boolean isFrequencyPolygon)
throws Exception {
AlgoElement al = null, algoHistogram = null;
histogramRight = !settings.isLeftRule();
GeoElementND geo;
// determine min/max X values
if (settings.groupType() == GroupType.RAWDATA) {
getDataBounds(dataList);
} else if (settings.groupType() == GroupType.FREQUENCY) {
getDataBounds((GeoList) dataList.get(0));
} else if (settings.groupType() == GroupType.CLASS) {
// settings.numClasses = ((GeoList) dataList.get(0)).size();
}
// determine class borders
if (settings.isUseManualClasses()
|| settings.groupType() == GroupType.CLASS) {
// generate class borders using given start and width
al = new AlgoClasses(cons, dataList,
new GeoNumeric(cons, settings.getClassStart()),
new GeoNumeric(cons, settings.getClassWidth()), null);
} else {
// generate class borders from data using given number of classes
settings.setClassWidth(
(xMaxData - xMinData) / (settings.getNumClasses()));
if (settings.groupType() == GroupType.RAWDATA) {
al = new AlgoClasses(cons, dataList, null, null,
new GeoNumeric(cons, settings.getNumClasses()));
} else if (settings.groupType() == GroupType.FREQUENCY) {
al = new AlgoClasses(cons, (GeoList) dataList.get(0), null,
null, new GeoNumeric(cons, settings.getNumClasses()));
}
}
removeFromConstructionList(al);
// set the density
double density = -1;
if (settings.getFrequencyType() == StatPanelSettings.TYPE_RELATIVE) {
density = 1.0 * settings.getClassWidth() / dataList.size();
} else if (settings
.getFrequencyType() == StatPanelSettings.TYPE_NORMALIZED) {
density = 1.0 / dataList.size();
}
// ==================
// create a histogram and (possibly) a frequency polygon
if (settings.groupType() == GroupType.RAWDATA) {
// histogram constructed from data values
algoHistogram = new AlgoHistogram(cons,
new GeoBoolean(cons, settings.isCumulative()),
(GeoList) al.getOutput(0), dataList, null,
new GeoBoolean(cons, true), new GeoNumeric(cons, density),
histogramRight);
} else if (settings.groupType() == GroupType.FREQUENCY) {
// histogram constructed from frequencies
algoHistogram = new AlgoHistogram(cons,
new GeoBoolean(cons, settings.isCumulative()),
(GeoList) al.getOutput(0), (GeoList) dataList.get(0),
(GeoList) dataList.get(1), new GeoBoolean(cons, true),
new GeoNumeric(cons, density), histogramRight);
} else if (settings.groupType() == GroupType.CLASS) {
// histogram constructed from classes and frequencies
algoHistogram = new AlgoHistogram(cons, (GeoList) dataList.get(0),
(GeoList) dataList.get(1), histogramRight);
} else {
throw new Exception(
"unexpected groupType: " + settings.groupType());
}
if (isFrequencyPolygon) {
AlgoPolyLine al3 = createFrequencyPolygon(
(AlgoHistogram) algoHistogram, settings.isCumulative());
geo = al3.getOutput(0);
geo.setObjColor(
listener.createColor(DataAnalysisModel.OVERLAY_COLOR_IDX));
geo.setLineThickness(DataAnalysisModel.thicknessCurve);
removeFromConstructionList(algoHistogram);
removeFromConstructionList(al3);
} else {
geo = algoHistogram.getOutput(0);
geo.setObjColor(listener
.createColor(DataAnalysisModel.HISTOGRAM_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
geo.setLineThickness(DataAnalysisModel.thicknessBarChart);
removeFromConstructionList(algoHistogram);
}
algoHistogram.getOutput(0).setEuclidianVisible(false);
algoHistogram.setProtectedInput(true);
return geo;
}
/**
* Creates a FrequencyPolygon algo using AlgoPolyLine instead of
* AlgoFrequencyPolygon This is needed until FrequencyPolygonRight is
* implemented
*
* @param histogram
* @param doCumulative
* @return
*/
private AlgoPolyLine createFrequencyPolygon(AlgoHistogram histogram,
boolean doCumulative) {
double[] leftBorder = histogram.getLeftBorder();
double yValue[] = histogram.getValues();
int size = doCumulative ? yValue.length : yValue.length + 1;
GeoPointND[] points = new GeoPoint[size];
boolean suppressLabelCreation = cons.isSuppressLabelsActive();
cons.setSuppressLabelCreation(true);
if (doCumulative) {
points[0] = new GeoPoint(cons, null, leftBorder[0], 0.0, 1.0);
for (int i = 0; i < yValue.length - 1; i++) {
points[i + 1] = new GeoPoint(cons, null, leftBorder[i + 1],
yValue[i], 1.0);
}
} else {
double midpoint = leftBorder[0]
- 0.5 * (leftBorder[1] - leftBorder[0]);
points[0] = new GeoPoint(cons, null, midpoint, 0.0, 1.0);
for (int i = 0; i < yValue.length - 1; i++) {
midpoint = 0.5 * (leftBorder[i + 1] + leftBorder[i]);
points[i + 1] = new GeoPoint(cons, null, midpoint, yValue[i],
1.0);
}
midpoint = 1.5 * leftBorder[yValue.length - 1]
- .5 * (leftBorder[yValue.length - 2]);
points[yValue.length] = new GeoPoint(cons, null, midpoint, 0.0,
1.0);
}
cons.setSuppressLabelCreation(suppressLabelCreation);
AlgoPolyLine polyLine = new AlgoPolyLine(cons, points);
return polyLine;
}
public GeoElement createNormalCurveOverlay(GeoList dataList) {
GeoElement geo;
AlgoMean mean = new AlgoMean(cons, dataList);
AlgoStandardDeviation sd = new AlgoStandardDeviation(cons, dataList);
removeFromConstructionList(mean);
removeFromConstructionList(sd);
GeoElementND meanGeo = mean.getOutput(0);
GeoElementND sdGeo = sd.getOutput(0);
FunctionVariable x = new FunctionVariable(kernel);
ExpressionNode normal = new ExpressionNode(kernel, x, Operation.MINUS,
meanGeo);
normal = new ExpressionNode(kernel, normal, Operation.DIVIDE, sdGeo);
normal = new ExpressionNode(kernel, normal, Operation.POWER,
new MyDouble(kernel, 2.0));
normal = new ExpressionNode(kernel, normal, Operation.DIVIDE,
new MyDouble(kernel, -2.0));
normal = new ExpressionNode(kernel, normal, Operation.EXP, null);
normal = new ExpressionNode(kernel, normal, Operation.DIVIDE,
new MyDouble(kernel, Math.sqrt(2 * Math.PI)));
normal = new ExpressionNode(kernel, normal, Operation.DIVIDE, sdGeo);
geo = normal.buildFunction(x);
geo.setObjColor(
listener.createColor(DataAnalysisModel.OVERLAY_COLOR_IDX));
geo.setLineThickness(DataAnalysisModel.thicknessCurve);
return geo;
}
public void getHistogramSettings(GeoList dataList, GeoElementND histogram,
StatPanelSettings settings) {
// get the data bounds
if (settings.groupType() == GroupType.RAWDATA) {
getDataBounds(dataList);
} else if (settings.groupType() == GroupType.FREQUENCY) {
getDataBounds((GeoList) dataList.get(0));
} else if (settings.groupType() == GroupType.CLASS) {
getDataBounds((GeoList) dataList.get(0));
}
double freqMax = ((AlgoFunctionAreaSums) histogram.getParentAlgorithm())
.getFreqMax();
if (settings.isUseManualClasses()) {
double[] leftBorder = ((AlgoFunctionAreaSums) histogram
.getParentAlgorithm()).getLeftBorder();
xMinData = leftBorder[0];
xMaxData = leftBorder[leftBorder.length - 1];
}
yMinData = 0.0;
yMaxData = freqMax;
setXYBounds(settings, .2, .1);
settings.showYAxis = true;
settings.isEdgeAxis[0] = false;
settings.isEdgeAxis[1] = true;
settings.isPositiveOnly[1] = true;
settings.forceXAxisBuffer = true;
}
/**
* Creates a bar chart from a list of GeoText values
*
* @param dataList
* @param settings
* @return
* @throws Exception
*/
public GeoElementND createBarChartText(GeoList dataList,
StatPanelSettings settings) throws Exception {
GeoElementND geo = null;
AlgoBarChart algoBarChart = null;
if (settings.isAutomaticBarWidth()) {
settings.setBarWidth(0.5);
}
if (settings.groupType() == GroupType.RAWDATA) {
algoBarChart = new AlgoBarChart(cons, dataList,
new GeoNumeric(cons, settings.getBarWidth()));
} else if (settings.groupType() == GroupType.FREQUENCY) {
algoBarChart = new AlgoBarChart(cons, (GeoList) dataList.get(0),
(GeoList) dataList.get(1),
new GeoNumeric(cons, settings.getBarWidth()));
} else {
throw new Exception(
"unexpected groupType: " + settings.groupType());
}
removeFromConstructionList(algoBarChart);
geo = algoBarChart.getOutput(0);
geo.setObjColor(
listener.createColor(DataAnalysisModel.BARCHART_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
algoBarChart.setProtectedInput(true);
return geo;
}
/**
* Creates a bar chart from a list of GeoNumeric values
*
* @param dataList
* @param settings
* @return
* @throws Exception
*/
public GeoElement createBarChartNumeric(GeoList dataList,
StatPanelSettings settings) throws Exception {
GeoElement geo = null;
AlgoBarChart algoBarChart = null;
if (settings.groupType() == GroupType.RAWDATA) {
if (settings.isAutomaticBarWidth()) {
AlgoUnique algo = new AlgoUnique(cons, dataList);
cons.removeFromConstructionList(algo);
settings.setBarWidth(getPreferredBarWidth(algo.getResult()));
}
algoBarChart = new AlgoBarChart(cons, dataList,
new GeoNumeric(cons, settings.getBarWidth()));
removeFromConstructionList(algoBarChart);
geo = algoBarChart.getOutput(0);
} else if (settings.groupType() == GroupType.FREQUENCY) {
if (settings.isAutomaticBarWidth()) {
settings.setBarWidth(
getPreferredBarWidth((GeoList) dataList.get(0)));
}
algoBarChart = new AlgoBarChart(cons, (GeoList) dataList.get(0),
(GeoList) dataList.get(1),
new GeoNumeric(cons, settings.getBarWidth()));
removeFromConstructionList(algoBarChart);
geo = algoBarChart.getOutput(0);
} else {
throw new Exception(
"unexpected groupType: " + settings.groupType());
}
geo.setObjColor(
listener.createColor(DataAnalysisModel.BARCHART_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
algoBarChart.setProtectedInput(true);
return geo;
}
/**
* @param list
* @return Preferred bar width = half of the minimum width between
* consecutive values in the given list.
*/
private static double getPreferredBarWidth(GeoList list) {
double w = 1;
for (int i = 0; i < list.size() - 1; i++) {
if (list.get(i).isDefined() && list.get(i + 1).isDefined()) {
w = Math.min(Math.abs(((GeoNumeric) list.get(i + 1)).getDouble()
- ((GeoNumeric) list.get(i)).getDouble()), w);
}
}
return w / 2;
}
public GeoElement createFrequencyTableGeo(GeoNumeric chart,
PlotType plotType) throws Exception {
AlgoFrequencyTable al = null;
switch (plotType) {
case HISTOGRAM:
al = new AlgoFrequencyTable(cons, chart);
break;
case BARCHART:
al = new AlgoFrequencyTable(cons, chart);
break;
default:
throw new Exception("unexpected plotType: " + plotType);
}
removeFromConstructionList(al);
return al.getOutput(0);
}
public void getBarChartSettings(GeoList dataList,
StatPanelSettings settings, GeoElementND barChart) {
double[] leftBorder = ((AlgoBarChart) barChart.getParentAlgorithm())
.getLeftBorder();
xMinData = leftBorder[0] - settings.getBarWidth() / 2;
xMaxData = leftBorder[leftBorder.length - 1] + settings.getBarWidth();
double freqMax = ((AlgoBarChart) barChart.getParentAlgorithm())
.getFreqMax();
yMinData = 0.0;
yMaxData = freqMax;
setXYBounds(settings, .2, .1);
if (settings.isAutomaticWindow() && !settings.isNumericData()) {
settings.xAxesIntervalAuto = false;
settings.xAxesInterval = 1;
}
settings.isEdgeAxis[0] = false;
settings.isEdgeAxis[1] = true;
// settings.isPositiveOnly[1] = true;
settings.showYAxis = true;
settings.forceXAxisBuffer = true;
// settings.debug();
}
public GeoElement createBoxPlot(GeoList dataList,
StatPanelSettings settings) throws Exception {
GeoElement geo = null;
AlgoBoxPlot algoBoxPlot = null;
if (settings.groupType() == GroupType.RAWDATA) {
algoBoxPlot = new AlgoBoxPlot(cons, new GeoNumeric(cons, 1d),
new GeoNumeric(cons, 0.5), dataList,
new GeoBoolean(cons, settings.isShowOutliers()));
removeFromConstructionList(algoBoxPlot);
geo = algoBoxPlot.getOutput(0);
} else if (settings.groupType() == GroupType.FREQUENCY) {
algoBoxPlot = new AlgoBoxPlot(cons, new GeoNumeric(cons, 1d),
new GeoNumeric(cons, 0.5), (GeoList) dataList.get(0),
(GeoList) dataList.get(1),
new GeoBoolean(cons, settings.isShowOutliers()));
removeFromConstructionList(algoBoxPlot);
geo = algoBoxPlot.getOutput(0);
} else {
throw new Exception(
"unexpected groupType: " + settings.groupType());
}
geo.setObjColor(
listener.createColor(DataAnalysisModel.BOXPLOT_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
algoBoxPlot.setProtectedInput(true);
return geo;
}
public void getBoxPlotSettings(GeoList dataList, StatPanelSettings settings)
throws Exception {
if (settings.groupType() == GroupType.RAWDATA) {
getDataBounds(dataList);
} else if (settings.groupType() == GroupType.FREQUENCY) {
getDataBounds((GeoList) dataList.get(0));
} else {
throw new Exception(
"unexpected groupType: " + settings.groupType());
}
if (settings.isAutomaticWindow()) {
double buffer = .25 * (xMaxData - xMinData);
settings.xMin = xMinData - buffer;
settings.xMax = xMaxData + buffer;
settings.yMin = -1.0;
settings.yMax = 2;
}
settings.showYAxis = false;
settings.forceXAxisBuffer = true;
}
public GeoElement[] createMultipleBoxPlot(GeoList dataList,
StatPanelSettings settings) {
int length = dataList.size();
GeoElement[] ret = new GeoElement[length];
for (int i = 0; i < length; i++) {
AlgoBoxPlot bp = new AlgoBoxPlot(cons, new GeoNumeric(cons, i + 1),
new GeoNumeric(cons, 1d / 3d),
(GeoList) dataList.get((length - 1) - i),
new GeoBoolean(cons, settings.isShowOutliers()));
cons.removeFromAlgorithmList(bp);
ret[i] = bp.getOutput(0);
ret[i].setObjColor(
listener.createColor(DataAnalysisModel.BOXPLOT_COLOR_IDX));
ret[i].setAlphaValue(DataAnalysisModel.opacityBarChart);
}
return ret;
}
public void getMultipleBoxPlotSettings(GeoList dataList,
StatPanelSettings settings) {
if (settings.isAutomaticWindow()) {
getDataBounds(dataList, false, true);
double buffer = .25 * (xMaxData - xMinData);
settings.xMin = xMinData - buffer;
settings.xMax = xMaxData + buffer;
settings.yMin = -1.0;
settings.yMax = dataList.size() + 1;
}
settings.showYAxis = false;
settings.forceXAxisBuffer = true;
}
public GeoElement[] createBoxPlotTitles(DataAnalysisModel statDialog,
StatPanelSettings settings) {
String[] dataTitles = statDialog.getDataTitles();
int length = dataTitles.length;
GeoElement[] ret = new GeoElement[length];
for (int i = 0; i < dataTitles.length; i++) {
GeoPoint p = new GeoPoint(cons, settings.xMin, i + 1d, 1d);
GeoText t = new GeoText(cons,
" " + dataTitles[dataTitles.length - i - 1]);
AlgoText text = new AlgoText(cons, t, p, null, null);
cons.removeFromAlgorithmList(text);
ret[i] = text.getOutput(0);
ret[i].setBackgroundColor(
listener.createColor(DataAnalysisModel.WHITE_COLOR_IDX));
ret[i].setObjColor(
listener.createColor(DataAnalysisModel.BLACK_COLOR_IDX));
}
return ret;
}
public GeoElement createDotPlot(GeoList dataList) {
// String label = dataList.getLabel();
// GeoElement geo;
// String text = "DotPlot[" + label + "]";
// geo = createGeoFromString(text);
AlgoDotPlot algoDotPlot = new AlgoDotPlot(cons, dataList);
removeFromConstructionList(algoDotPlot);
GeoElement geo = algoDotPlot.getOutput(0);
geo.setObjColor(
listener.createColor(DataAnalysisModel.DOTPLOT_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
algoDotPlot.setProtectedInput(true);
return geo;
}
public void updateDotPlot(GeoList dataList, GeoElement dotPlot,
StatPanelSettings settings) {
getDataBounds(dataList);
if (settings.isAutomaticWindow()) {
double buffer = .25 * (xMaxData - xMinData);
settings.xMin = xMinData - buffer;
settings.xMax = xMaxData + buffer;
settings.yMin = -1.0;
ExpressionNode en = new ExpressionNode(kernel, dotPlot,
Operation.YCOORD, null);
AlgoDependentListExpression list = new AlgoDependentListExpression(
cons, en);
AlgoListMax max = new AlgoListMax(cons,
(GeoList) list.getOutput(0));
removeFromConstructionList(list);
removeFromConstructionList(max);
settings.yMax = ((GeoNumeric) max.getOutput(0)).getDouble() + 1;
}
settings.showYAxis = false;
settings.forceXAxisBuffer = true;
}
public GeoElement createNormalQuantilePlot(GeoList dataList) {
// String label = dataList.getLabel();
// GeoElement geo;
// String text = "NormalQuantilePlot[" + label + "]";
// geo = createGeoFromString(text);
AlgoNormalQuantilePlot algoNormalQPlot = new AlgoNormalQuantilePlot(
cons, dataList);
removeFromConstructionList(algoNormalQPlot);
GeoElement geo = algoNormalQPlot.getOutput(0);
geo.setObjColor(
listener.createColor(DataAnalysisModel.NQPLOT_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
geo.setLineThickness(DataAnalysisModel.thicknessCurve);
algoNormalQPlot.setProtectedInput(true);
return geo;
}
public void updateNormalQuantilePlot(GeoList dataList,
StatPanelSettings settings) {
getDataBounds(dataList);
if (settings.isAutomaticWindow()) {
double buffer = .25 * (xMaxData - xMinData);
settings.xMin = xMinData - buffer;
settings.xMax = xMaxData + buffer;
settings.yMin = -4.0;
settings.yMax = 4.0;
settings.showYAxis = true;
}
settings.isEdgeAxis[1] = true;
settings.forceXAxisBuffer = false;
settings.isPositiveOnly[0] = false;
settings.isPositiveOnly[1] = false;
}
public GeoElementND createScatterPlotLine(GeoList points) {
AlgoPolyLine polyLine = new AlgoPolyLine(cons, points);
removeFromConstructionList(polyLine);
GeoElementND geo = polyLine.getOutput(0);
// set visibility
geo.setEuclidianVisible(true);
geo.setAuxiliaryObject(true);
geo.setLabelVisible(false);
geo.setObjColor(
listener.createColor(DataAnalysisModel.DOTPLOT_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
return geo;
}
public GeoElement createScatterPlot(GeoList dataList) {
// copy the dataList geo
ArrayList<GeoElement> list = new ArrayList<GeoElement>();
for (int i = 0; i < dataList.size(); ++i) {
list.add(dataList.get(i));
}
AlgoDependentList dl = new AlgoDependentList(cons, list, false);
removeFromConstructionList(dl);
GeoList geo = dl.getGeoList();
// set visibility
geo.setEuclidianVisible(true);
geo.setAuxiliaryObject(true);
geo.setLabelVisible(false);
geo.setSelectionAllowed(false);
geo.setObjColor(
listener.createColor(DataAnalysisModel.DOTPLOT_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
return geo;
}
public void getScatterPlotSettings(GeoList dataList,
StatPanelSettings settings) {
getDataBoundsForPointList(dataList);
setXYBounds(settings);
settings.showYAxis = true;
settings.forceXAxisBuffer = false;
settings.isEdgeAxis[0] = true;
settings.isEdgeAxis[1] = true;
settings.isPositiveOnly[0] = true;
settings.isPositiveOnly[1] = true;
}
@SuppressFBWarnings({ "SF_SWITCH_FALLTHROUGH",
"missing break is deliberate" })
public GeoElement createRegressionPlot(GeoList dataList, Regression reg,
int order, boolean residual) {
boolean regNone = false;
AlgoElement algo;
switch (reg) {
case LOG:
algo = new AlgoFitLog(cons, dataList);
break;
case POLY:
algo = new AlgoFitPoly(cons, dataList, new GeoNumeric(cons, order));
break;
case POW:
algo = new AlgoFitPow(cons, dataList);
break;
case EXP:
algo = new AlgoFitExp(cons, dataList);
break;
case GROWTH:
algo = new AlgoFitGrowth(cons, dataList);
break;
case SIN:
algo = new AlgoFitSin(cons, dataList);
break;
case LOGISTIC:
algo = new AlgoFitLogistic(cons, dataList);
break;
case NONE:
regNone = true;
// fall through to linear
case LINEAR:
default:
algo = new AlgoFitPoly(cons, dataList, new GeoNumeric(cons, 1));
break;
}
removeFromConstructionList(algo);
GeoElement geo = algo.getOutput(0);
if (residual) {
AlgoResidualPlot algoRP = new AlgoResidualPlot(cons, dataList,
(GeoFunctionable) geo);
geo = algoRP.getOutput(0);
geo.setObjColor(
listener.createColor(DataAnalysisModel.DOTPLOT_COLOR_IDX));
geo.setAlphaValue(DataAnalysisModel.opacityBarChart);
geo.setLineThickness(DataAnalysisModel.thicknessCurve);
} else {
// set geo options
geo.setObjColor(listener
.createColor(DataAnalysisModel.REGRESSION_COLOR_IDX));
// hide the dummy geo
if (regNone) {
geo.setEuclidianVisible(false);
}
}
return geo;
}
public void updateRegressionPlot(GeoList dataList,
StatPanelSettings settings) {
if (settings.isAutomaticWindow()) {
getDataBoundsForPointList(dataList);
setXYBounds(settings, .25, .25);
}
settings.showYAxis = true;
settings.forceXAxisBuffer = false;
}
/*
* public GeoElement createResidualPlot(GeoList dataList, int regType, int
* order){
*
* GeoElement geo = null;
*
* if (regType == StatDialog.REG_NONE){ return new GeoList(cons); }
*
* String label = dataList.getLabel();
*
* String regFcn = regCmd[regType] + "[" + label + "]"; if(regType ==
* StatDialog.REG_POLY) regFcn = regCmd[regType] + "[" + label + "," + order
* + "]";
*
* String text = "ResidualPlot[" + label + "," + regFcn + "]"; geo =
* createGeoFromString(text); geo.setObjColor(StatDialog.DOTPLOT_COLOR_IDX);
* geo.setAlphaValue(0.25f);
*
* return geo;
*
* }
*/
public void getResidualPlotSettings(GeoList dataList,
GeoElement residualPlot, StatPanelSettings settings) {
getDataBoundsForPointList(dataList);
double[] residualBounds = ((AlgoResidualPlot) residualPlot
.getParentAlgorithm()).getResidualBounds();
yMaxData = Math.max(Math.abs(residualBounds[0]),
Math.abs(residualBounds[1]));
yMinData = -yMaxData;
setXYBounds(settings);
settings.showYAxis = true;
settings.forceXAxisBuffer = false;
settings.isEdgeAxis[0] = false;
settings.isEdgeAxis[1] = true;
settings.isPositiveOnly[0] = true;
settings.isPositiveOnly[1] = false;
}
private void setXYBounds(StatPanelSettings settings) {
setXYBounds(settings, .2, .2);
}
/**
* Sets the automatic window dimensions for the plot panel.
*
* @param settings
* @param xBufferScale
* proportion of the x range to use for a left/right buffer
* @param yBufferScale
* proportion of the y range to use for a top/bottom buffer
*/
private void setXYBounds(StatPanelSettings settings, double xBufferScale,
double yBufferScale) {
if (settings.isAutomaticWindow()) {
double xMin = xMinData, yMin = yMinData, xMax = xMaxData,
yMax = yMaxData;
// TODO #4952 following settings make the scaling right for points,
// but a wrong part of a curve is used
// if (settings.logXAxis) {
// xMin = xMin < 0 ? 0 : Math.log10(xMin);
// xMax = xMax < 0 ? xMin : Math.log10(xMax);
// }
// if (settings.logYAxis) {
// yMin = yMin < 0 ? 0 : Math.log10(yMin);
// yMax = yMax < 0 ? yMin : Math.log10(yMax);
// }
double xBuffer = Kernel.isEqual(xMax, xMin) ? DEFAULT_BUFFER
: xBufferScale * (xMax - xMin);
settings.xMin = xMin - xBuffer;
settings.xMax = xMax + xBuffer;
double yBuffer = Kernel.isEqual(yMax, yMin) ? DEFAULT_BUFFER
: yBufferScale * (yMax - yMin);
settings.yMin = yMin - yBuffer;
settings.yMax = yMax + yBuffer;
}
}
public String getStemPlotLatex(GeoList dataList, int adjustment) {
// String label = dataList.getLabel();
// String text = "StemPlot[" + label + "," + adjustment + "]";
// tempGeo = createGeoFromString(text);
AlgoStemPlot algoStemPlot = new AlgoStemPlot(cons, dataList,
new GeoNumeric(cons, adjustment));
GeoElement tempGeo = algoStemPlot.getOutput(0);
removeFromConstructionList(algoStemPlot);
algoStemPlot.setProtectedInput(true);
String latex = tempGeo.getLaTeXdescription();
tempGeo.remove();
return latex;
}
public boolean removeFromConstruction() {
return removeFromConstruction;
}
public void setRemoveFromConstruction(boolean removeFromConstruction) {
this.removeFromConstruction = removeFromConstruction;
}
private void removeFromConstructionList(ConstructionElement ce) {
// System.out.println("remove from cons:" + removeFromConstruction);
if (removeFromConstruction) {
cons.removeFromConstructionList(ce);
}
}
}