edu.cmu.tetradapp.editor.datamanip.ContinuousDiscretizationEditor Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below. //
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, //
// 2007, 2008, 2009, 2010, 2014, 2015, 2022 by Peter Spirtes, Richard //
// Scheines, Joseph Ramsey, and Clark Glymour. //
// //
// 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; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program 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 General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program; if not, write to the Free Software //
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //
///////////////////////////////////////////////////////////////////////////////
package edu.cmu.tetradapp.editor.datamanip;
import cern.colt.list.DoubleArrayList;
import cern.jet.stat.Descriptive;
import edu.cmu.tetrad.data.*;
import edu.cmu.tetrad.util.NumberFormatUtil;
import edu.cmu.tetradapp.util.IntSpinner;
import javax.swing.*;
import java.awt.*;
import java.text.NumberFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.prefs.Preferences;
/**
* Edits discretization parameters for a continuous variable.
*
* @author josephramsey
* @author Tyler Gibson
*/
class ContinuousDiscretizationEditor extends JPanel implements DiscretizationEditor {
/**
* The min value of the data.
*/
private final double min;
/**
* The max value of the data.
*/
private final double max;
/**
* The panel that contains the range editor.
*/
private final JPanel rangeEditorPanel;
/**
* The selection buttons box.
*/
private final Box selectionButtonsBox;
/**
* A spinner used to select the number of categories.
*/
private final IntSpinner categorySpinner;
/**
* The data that is being discretized.
*/
private final double[] data;
/**
* An editor that allows a user to edit the ranges of each category
*/
private RangeEditor rangeEditor;
/**
* The default number of categories to use.
*/
private int numberOfCategories;
/**
* The method being used to discretize the data.
*/
private Method method;
/**
* Constructs an editor that allows the user to discretize continuous variables. There are two options for the
* default discretization Method.EQUAL_SIZE_BUCKETS
which will discretize the continuous data into
* categories by trying to fit an equal number of values in each category. On the other hand
* Method.EVENLY_DIVIDED_INTERNVALS
will just spent the interval up in equal segments which may or may
* not include an even distribution of values.
*
* @param dataSet The dataset containing the data for the variable.
* @param variable The variable to be edited.
*/
public ContinuousDiscretizationEditor(DataSet dataSet,
ContinuousVariable variable) {
if (variable == null) {
throw new NullPointerException();
}
this.method = Method.NONE;
this.data = new double[dataSet.getNumRows()];
int col = dataSet.getColumn(variable);
for (int i = 0; i < dataSet.getNumRows(); i++) {
this.data[i] = dataSet.getDouble(i, col);
}
this.min = Descriptive.min(new DoubleArrayList(this.data));
this.max = Descriptive.max(new DoubleArrayList(this.data));
this.numberOfCategories = 2;
this.categorySpinner = new IntSpinner(2, 1, 2);
this.categorySpinner.setMin(2);
this.categorySpinner.setMaximumSize(this.categorySpinner.getPreferredSize());
this.categorySpinner.setSize(this.categorySpinner.getPreferredSize());
this.categorySpinner.setMinimumSize(this.categorySpinner.getPreferredSize());
this.categorySpinner.setFilter(new MyFilter());
this.categorySpinner.addChangeListener(e -> {
JSpinner spinner = (JSpinner) e.getSource();
if (!spinner.getValue().equals(ContinuousDiscretizationEditor.this.numberOfCategories)) {
setNumCategories((Integer) spinner.getValue());
}
});
this.rangeEditorPanel = new JPanel();
this.rangeEditorPanel.setLayout(new BorderLayout());
setNumCategories(2);
Box b1 = Box.createVerticalBox();
b1.add(Box.createVerticalStrut(10));
Box b3 = Box.createHorizontalBox();
b3.add(Box.createRigidArea(new Dimension(10, 0)));
/*
The decimal format to use.
*/
NumberFormat nf = NumberFormatUtil.getInstance().getNumberFormat();
String label = "Min = " + nf.format(this.min) + " , Max = " + nf.format(this.max);
b3.add(new JLabel(label));
b3.add(Box.createHorizontalGlue());
b1.add(b3);
b1.add(Box.createVerticalStrut(5));
this.selectionButtonsBox = Box.createHorizontalBox();
this.buildSelectionBox();
b1.add(this.selectionButtonsBox);
b1.add(Box.createVerticalStrut(5));
Box b5 = Box.createHorizontalBox();
b5.add(Box.createRigidArea(new Dimension(10, 0)));
b5.add(new JLabel("Use "));
b5.add(this.categorySpinner);
b5.add(new JLabel(" categories to discretize."));
b5.add(Box.createHorizontalGlue());
b1.add(b5);
b1.add(Box.createVerticalStrut(10));
Box b6 = Box.createHorizontalBox();
b6.add(Box.createRigidArea(new Dimension(10, 0)));
b6.add(new JLabel("Edit category names and breakpoints:"));
b6.add(Box.createHorizontalGlue());
b1.add(b6);
b1.add(Box.createVerticalStrut(10));
b1.add(this.rangeEditorPanel);
b1.add(Box.createVerticalGlue());
setLayout(new BorderLayout());
add(b1, BorderLayout.CENTER);
}
/**
* Calcultes the default break points.
*/
private static double[] defaultBreakpoints(double max, double min,
int numCategories) {
double interval = (max - min) / numCategories;
double[] breakpoints = new double[numCategories - 1];
for (int i = 0; i < breakpoints.length; i++) {
breakpoints[i] = min + (i + 1) * interval;
}
return breakpoints;
}
//================================PUBLIC METHODS=======================//
private static List defaultCategories(int numCategories) {
List categories = new LinkedList<>();
for (int i = 0; i < numCategories; i++) {
categories.add(DataUtils.defaultCategory(i));
}
return categories;
}
/**
* @return the number of categories.
*/
public int getNumCategories() {
return this.numberOfCategories;
}
/**
* Sets the number of categories to use.
*/
public void setNumCategories(int numCategories) {
if (numCategories < 2) {
throw new IllegalArgumentException();
}
this.numberOfCategories = numCategories;
this.rangeEditorPanel.removeAll();
ContinuousDiscretizationSpec defaultDiscretizationSpec;
if (this.method == Method.EVENLY_DIVIDED_INTERNVALS) {
defaultDiscretizationSpec = getEvenlyDividedDiscretizationSpec(numCategories);
} else if (this.method == Method.EQUAL_SIZE_BUCKETS) {
defaultDiscretizationSpec = getEqualFreqDiscretizationSpec(numCategories);
} else if (this.method == Method.NONE) {
defaultDiscretizationSpec = getDontDiscretizeSpec(numCategories);
} else {
throw new IllegalStateException("Unknown method " + this.method);
}
this.rangeEditor = createRangeEditor(defaultDiscretizationSpec);
this.rangeEditorPanel.add(this.rangeEditor, BorderLayout.CENTER);
this.rangeEditorPanel.revalidate();
this.rangeEditorPanel.repaint();
this.categorySpinner.setValue(numCategories);
Preferences.userRoot().putInt("latestNumCategories", numCategories);
}
/**
* @return the discretization spec created by the user.
*/
public ContinuousDiscretizationSpec getDiscretizationSpec() {
ContinuousDiscretizationSpec spec = this.rangeEditor.getDiscretizationSpec();
if (this.method == Method.EQUAL_SIZE_BUCKETS) {
spec.setMethod(ContinuousDiscretizationSpec.EVENLY_DISTRIBUTED_VALUES);
} else if (this.method == Method.EVENLY_DIVIDED_INTERNVALS) {
spec.setMethod(ContinuousDiscretizationSpec.EVENLY_DISTRIBUTED_INTERVALS);
} else if (this.method == Method.NONE) {
spec.setMethod(ContinuousDiscretizationSpec.NONE);
}
return spec;
}
/**
* Sets the discretization spec that should be used by the editor.
*/
public void setDiscretizationSpec(DiscretizationSpec _spec) {
ContinuousDiscretizationSpec spec = (ContinuousDiscretizationSpec) _spec;
this.rangeEditorPanel.removeAll();
if (spec.getMethod() == ContinuousDiscretizationSpec.EVENLY_DISTRIBUTED_INTERVALS) {
this.method = Method.EVENLY_DIVIDED_INTERNVALS;
} else if (spec.getMethod() == ContinuousDiscretizationSpec.EVENLY_DISTRIBUTED_VALUES) {
this.method = Method.EQUAL_SIZE_BUCKETS;
}
this.buildSelectionBox();
this.rangeEditor = createRangeEditor(spec);
this.numberOfCategories = spec.getCategories().size();
this.categorySpinner.setValue(this.numberOfCategories);
this.rangeEditorPanel.add(this.rangeEditor, BorderLayout.CENTER);
this.rangeEditorPanel.revalidate();
this.rangeEditorPanel.repaint();
}
/**
* @return the method.
*/
public Method getMethod() {
return this.method;
}
//==============================PRIVATE METHODS=======================//
/**
* Changes the method.
*/
public void setMethod(Method method) {
this.method = method;
this.buildSelectionBox();
this.setNumCategories(this.numberOfCategories);
}
private void buildSelectionBox() {
this.selectionButtonsBox.removeAll();
Box vBox = Box.createVerticalBox();
vBox.add(new JLabel("Discretization Method: "));
JRadioButton none = new JRadioButton("Don't Discretize",
this.method == Method.NONE);
JRadioButton equalInterval = new JRadioButton("Evenly Distributed Intervals",
this.method == Method.EVENLY_DIVIDED_INTERNVALS);
JRadioButton equalBuckets = new JRadioButton("Evenly Distributed Values",
this.method == Method.EQUAL_SIZE_BUCKETS);
none.setHorizontalTextPosition(SwingConstants.RIGHT);
equalInterval.setHorizontalTextPosition(SwingConstants.RIGHT);
equalBuckets.setHorizontalTextPosition(SwingConstants.RIGHT);
ButtonGroup group = new ButtonGroup();
group.add(equalBuckets);
group.add(equalInterval);
group.add(none);
none.addActionListener(e -> {
ContinuousDiscretizationEditor.this.method = Method.NONE;
setNumCategories(ContinuousDiscretizationEditor.this.numberOfCategories);
});
equalInterval.addActionListener(e -> {
ContinuousDiscretizationEditor.this.method = Method.EVENLY_DIVIDED_INTERNVALS;
setNumCategories(ContinuousDiscretizationEditor.this.numberOfCategories);
});
equalBuckets.addActionListener(e -> {
ContinuousDiscretizationEditor.this.method = Method.EQUAL_SIZE_BUCKETS;
setNumCategories(ContinuousDiscretizationEditor.this.numberOfCategories);
});
// System.out.println("Method = " + method);
if (this.method == Method.EQUAL_SIZE_BUCKETS) {
equalBuckets.setSelected(true);
} else if (this.method == Method.EVENLY_DIVIDED_INTERNVALS) {
equalInterval.setSelected(true);
} else if (this.method == Method.NONE) {
none.setSelected(true);
} else {
none.setSelected(true);
}
// none.setSelected(true);
vBox.add(none);
vBox.add(equalBuckets);
vBox.add(equalInterval);
this.selectionButtonsBox.add(Box.createHorizontalStrut(10));
this.selectionButtonsBox.add(vBox);
this.selectionButtonsBox.add(Box.createHorizontalGlue());
}
private RangeEditor createRangeEditor(
ContinuousDiscretizationSpec discretizationSpec) {
return new RangeEditor(discretizationSpec);
}
/**
* Calculates the equal freq discretization spec
*/
private ContinuousDiscretizationSpec getEqualFreqDiscretizationSpec(int numCategories) {
double[] breakpoints = Discretizer.getEqualFrequencyBreakPoints(this.data, numCategories);
List cats = ContinuousDiscretizationEditor.defaultCategories(numCategories);
return new ContinuousDiscretizationSpec(breakpoints, cats);
}
/**
* Calculates the equal freq discretization spec
*/
private ContinuousDiscretizationSpec getDontDiscretizeSpec(int numCategories) {
double[] breakpoints = Discretizer.getEqualFrequencyBreakPoints(this.data, numCategories);
List cats = ContinuousDiscretizationEditor.defaultCategories(numCategories);
return new ContinuousDiscretizationSpec(breakpoints, cats, ContinuousDiscretizationSpec.NONE);
}
/**
* Calculates the default discretization spec.
*/
private ContinuousDiscretizationSpec getEvenlyDividedDiscretizationSpec(
int numCategories) {
double[] breakpoints = ContinuousDiscretizationEditor.defaultBreakpoints(this.max, this.min, numCategories);
List categories = ContinuousDiscretizationEditor.defaultCategories(numCategories);
return new ContinuousDiscretizationSpec(breakpoints, categories);
}
/**
* States which method should be used to discretize matters by default.
*/
public enum Method {
EQUAL_SIZE_BUCKETS, EVENLY_DIVIDED_INTERNVALS, NONE
}
//=========================== Inner class ====================================//
private static class MyFilter implements IntSpinner.Filter {
public int filter(int oldValue, int newValue) {
if (newValue < 2) {
newValue = oldValue;
}
return newValue;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy