All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.cmu.tetradapp.editor.datamanip.ContinuousDiscretizationEditor Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////
// 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;
    }

    /**
     * 

getNumCategories.

* * @return the number of categories. */ public int getNumCategories() { return this.numberOfCategories; } /** * Sets the number of categories to use. * * @param numCategories a int */ 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); } /** *

getDiscretizationSpec.

* * @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. * * @param _spec a {@link edu.cmu.tetrad.data.DiscretizationSpec} object */ 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(); } /** *

Getter for the field method.

* * @return the method. */ public Method getMethod() { return this.method; } //==============================PRIVATE METHODS=======================// /** * Changes the method. * * @param method a {@link edu.cmu.tetradapp.editor.datamanip.ContinuousDiscretizationEditor.Method} object */ 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