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

edu.cmu.tetradapp.editor.RegressionParamsEditorPanel 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;

import edu.cmu.tetrad.data.DataModel;
import edu.cmu.tetrad.data.DataSet;
import edu.cmu.tetrad.data.DataUtils;
import edu.cmu.tetrad.data.DiscreteVariable;
import edu.cmu.tetrad.graph.Node;
import edu.cmu.tetrad.util.NumberFormatUtil;
import edu.cmu.tetrad.util.Parameters;
import edu.cmu.tetradapp.model.RegressionModel;
import edu.cmu.tetradapp.util.DoubleTextField;
import edu.cmu.tetradapp.workbench.LayoutUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.IOException;
import java.util.List;
import java.util.*;

/**
 * Allows one to drop/drap variables from a source list to a response area and a predictors list. Also lets one specify
 * an alpha level.
 *
 * @author Tyler Gibson
 */
@SuppressWarnings("unchecked")
class RegressionParamsEditorPanel extends JPanel {

    private static final long serialVersionUID = -194301447990323529L;
    /**
     * A mapping between variable names and what sort of variable they are: 1 - binary, 2- discrete, 3 - continuous.
     */
    private static final Map VAR_MAP = new HashMap<>();
    /**
     * The font to render fields in.
     */
    private static final Font FONT = new Font("Dialog", Font.PLAIN, 12);
    /**
     * The list of predictors.
     */
    private static JList PREDICTORS_LIST;
    /**
     * The list of source variables.
     */
    private static JList SOURCE_LIST;
    /**
     * A list with a single item in it for the response variable.
     */
    private static JTextField RESPONSE_FIELD;
    private final boolean logistic;
    private final RegressionModel regressionModel;
    /**
     * The params that are being edited.
     */
    private final Parameters params;

    /**
     * Constructs the editor given the Parameters and the
     * DataModel that should be used.
     *
     * @param regressionModel a {@link edu.cmu.tetradapp.model.RegressionModel} object
     * @param parameters      a {@link edu.cmu.tetrad.util.Parameters} object
     * @param model           a {@link edu.cmu.tetrad.data.DataModel} object
     * @param logistic        a boolean
     */
    public RegressionParamsEditorPanel(RegressionModel regressionModel, Parameters parameters,
                                       DataModel model, boolean logistic) {
        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        if (parameters == null) {
            throw new NullPointerException("The given params must not be null");
        }
        this.params = parameters;
        this.logistic = logistic;
        List variableNames = regressionModel.getVariableNames();
        this.regressionModel = regressionModel;

        // create components
        RegressionParamsEditorPanel.PREDICTORS_LIST = RegressionParamsEditorPanel.createList();
        VariableListModel predictorsModel = (VariableListModel) RegressionParamsEditorPanel.getPredictorsList().getModel();
        RegressionParamsEditorPanel.SOURCE_LIST = RegressionParamsEditorPanel.createList();
        if (logistic && model instanceof DataSet) {
            buildMap((DataSet) model);
            RegressionParamsEditorPanel.getSourceList().setCellRenderer(new LogisticRegRenderer());
        }
        VariableListModel variableModel = (VariableListModel) RegressionParamsEditorPanel.getSourceList().getModel();
        RegressionParamsEditorPanel.RESPONSE_FIELD = createResponse(RegressionParamsEditorPanel.getSourceList());

        // if regressors are already set use'em.
        List regressors = regressionModel.getRegressorNames();
        if (regressors != null) {
            predictorsModel.addAll(regressors);
            List initVars = new ArrayList<>(variableNames);
            initVars.removeAll(regressors);
            variableModel.addAll(initVars);
        } else {
            variableModel.addAll(variableNames);
        }
        // if target is set use it too
        String target = regressionModel.getTargetName();
        if (target != null) {
            variableModel.remove(target);
            //     response.setText(target);
        }

        // deal with drag and drop
        new DropTarget(RegressionParamsEditorPanel.getSourceList(), DnDConstants.ACTION_MOVE, new TargetListener(), true);
        new DropTarget(RegressionParamsEditorPanel.getResponseField(), DnDConstants.ACTION_MOVE, new TargetListener(), true);
        new DropTarget(RegressionParamsEditorPanel.getPredictorsList(), DnDConstants.ACTION_MOVE, new TargetListener(), true);

        DragSource dragSource = DragSource.getDefaultDragSource();
        dragSource.createDefaultDragGestureRecognizer(RegressionParamsEditorPanel.getResponseField(), DnDConstants.ACTION_MOVE, new SourceListener());
        dragSource = DragSource.getDefaultDragSource();
        dragSource.createDefaultDragGestureRecognizer(RegressionParamsEditorPanel.getSourceList(), DnDConstants.ACTION_MOVE, new SourceListener());
        dragSource = DragSource.getDefaultDragSource();
        dragSource.createDefaultDragGestureRecognizer(RegressionParamsEditorPanel.getPredictorsList(), DnDConstants.ACTION_MOVE, new SourceListener());
        // build the gui
        Box box = Box.createHorizontalBox();
        box.add(Box.createHorizontalStrut(10));
        Box label = RegressionParamsEditorPanel.createLabel("Variables:");
        int height = label.getPreferredSize().height + RegressionParamsEditorPanel.getResponseField().getPreferredSize().height + 10;
        Box vBox1 = Box.createVerticalBox();
        vBox1.add(label);
        JScrollPane pane = RegressionParamsEditorPanel.createScrollPane(RegressionParamsEditorPanel.getSourceList(), new Dimension(100, 350 + height));
        vBox1.add(pane);
        vBox1.add(Box.createVerticalStrut(10));
        vBox1.add(buildAlphaArea(this.params.getDouble("alpha", 0.001)));
        vBox1.add(Box.createVerticalStrut(10));
        vBox1.add(buildSortButton());
        vBox1.add(Box.createVerticalGlue());
        box.add(vBox1);

        box.add(Box.createHorizontalStrut(4));
        box.add(buildSelectorArea(label.getPreferredSize().height));
        box.add(Box.createHorizontalStrut(4));

        Box vBox = Box.createVerticalBox();
        vBox.add(RegressionParamsEditorPanel.createLabel("Response:"));

        vBox.add(RegressionParamsEditorPanel.getResponseField());
        vBox.add(Box.createVerticalStrut(10));
        vBox.add(RegressionParamsEditorPanel.createLabel("Predictor(s):"));
        vBox.add(RegressionParamsEditorPanel.createScrollPane(RegressionParamsEditorPanel.getPredictorsList(), new Dimension(100, 350)));
        vBox.add(Box.createVerticalGlue());

        box.add(vBox);
        box.add(Box.createHorizontalStrut(10));
        box.add(Box.createHorizontalGlue());

        this.add(Box.createVerticalStrut(20));
        this.add(box);
    }

    //============================= Private Methods =================================//
    private static List getSelected(JList list) {
        List selected = list.getSelectedValuesList();
        List selectedList = new ArrayList<>(selected == null ? 0 : selected.size());
        if (selected != null) {
            for (Object o : selected) {
                selectedList.add((Comparable) o);
            }
        }
        return selectedList;
    }

    private static JScrollPane createScrollPane(JList comp, Dimension dim) {
        JScrollPane pane = new JScrollPane(comp);
        LayoutUtils.setAllSizes(pane, dim);
        return pane;
    }

    private static Box createLabel(String text) {
        JLabel label = new JLabel(text);
        label.setAlignmentX(Component.LEFT_ALIGNMENT);
        Box box = Box.createHorizontalBox();
        box.add(label);
        box.add(Box.createHorizontalGlue());
        return box;
    }

    private static JList createList() {
        JList list = new JList(new VariableListModel());
        list.setFont(RegressionParamsEditorPanel.getFONT());
        list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        list.setVisibleRowCount(10);
        return list;
    }

    private static DataFlavor getListDataFlavor() {
        try {
            return new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=java.lang.Object",
                    "Local Variable List");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static Map getVarMap() {
        return RegressionParamsEditorPanel.VAR_MAP;
    }

    private static JList getPredictorsList() {
        return RegressionParamsEditorPanel.PREDICTORS_LIST;
    }

    private static JList getSourceList() {
        return RegressionParamsEditorPanel.SOURCE_LIST;
    }

    private static JTextField getResponseField() {
        return RegressionParamsEditorPanel.RESPONSE_FIELD;
    }

    private static Font getFONT() {
        return RegressionParamsEditorPanel.FONT;
    }

    /**
     * Bulids the arrows that allow one to move variables around (can also use drag and drop)
     */
    private Box buildSelectorArea(int startHeight) {
        Box box = Box.createVerticalBox();
        JButton moveToResponse = new JButton(">");
        JButton moveToPredictor = new JButton(">");
        JButton moveToSource = new JButton("<");

        moveToResponse.addActionListener((e) -> {
            VariableListModel sourceModel = (VariableListModel) RegressionParamsEditorPanel.getSourceList().getModel();
            String target = RegressionParamsEditorPanel.getResponseField().getText();
            List selected = RegressionParamsEditorPanel.getSelected(RegressionParamsEditorPanel.getSourceList());
            if (selected.isEmpty()) {
                return;
            } else if (1 < selected.size()) {
                JOptionPane.showMessageDialog(this, "Cannot have more than one response variable");
                return;
            } else if (this.logistic && !isBinary((String) selected.get(0))) {
                JOptionPane.showMessageDialog(this,
                        "Response variable must be binary.");
                return;
            }
            sourceModel.removeAll(selected);
            RegressionParamsEditorPanel.getResponseField().setText((String) selected.get(0));
            RegressionParamsEditorPanel.getResponseField().setCaretPosition(0);
            this.regressionModel.setTargetName((String) selected.get(0));
            if (target != null && target.length() != 0) {
                sourceModel.add(target);
            }
        });

        moveToPredictor.addActionListener((e) -> {
            VariableListModel predictorsModel = (VariableListModel) RegressionParamsEditorPanel.getPredictorsList().getModel();
            VariableListModel sourceModel = (VariableListModel) RegressionParamsEditorPanel.getSourceList().getModel();
            List selected = RegressionParamsEditorPanel.getSelected(RegressionParamsEditorPanel.getSourceList());
            sourceModel.removeAll(selected);
            predictorsModel.addAll(selected);
            this.regressionModel.setRegressorName(getPredictors());
        });

        moveToSource.addActionListener((e) -> {
            VariableListModel predictorsModel = (VariableListModel) RegressionParamsEditorPanel.getPredictorsList().getModel();
            VariableListModel sourceModel = (VariableListModel) RegressionParamsEditorPanel.getSourceList().getModel();
            List selected = RegressionParamsEditorPanel.getSelected(RegressionParamsEditorPanel.getPredictorsList());
            // if not empty remove/add, otherwise try the response list.
            if (!selected.isEmpty()) {
                predictorsModel.removeAll(selected);
                sourceModel.addAll(selected);
                this.regressionModel.setRegressorName(getPredictors());
            } else if (RegressionParamsEditorPanel.getResponseField().getText() != null && RegressionParamsEditorPanel.getResponseField().getText().length() != 0) {
                String text = RegressionParamsEditorPanel.getResponseField().getText();
                this.regressionModel.setTargetName(null);
                RegressionParamsEditorPanel.getResponseField().setText(null);
                sourceModel.addAll(Collections.singletonList(text));
            }
        });

        box.add(Box.createVerticalStrut(startHeight));
        box.add(moveToResponse);
        box.add(Box.createVerticalStrut(150));
        box.add(moveToPredictor);
        box.add(Box.createVerticalStrut(10));
        box.add(moveToSource);
        box.add(Box.createVerticalGlue());

        return box;
    }

    private Box buildSortButton() {
        JButton sort = new JButton("Sort Variables");
        sort.setFont(sort.getFont().deriveFont(11f));
        sort.setMargin(new Insets(3, 3, 3, 3));
        sort.addActionListener((e) -> {
            VariableListModel predictorsModel = (VariableListModel) RegressionParamsEditorPanel.getPredictorsList().getModel();
            VariableListModel sourceModel = (VariableListModel) RegressionParamsEditorPanel.getSourceList().getModel();
            predictorsModel.sort();
            sourceModel.sort();
        });
        Box box = Box.createHorizontalBox();
        box.add(sort);
        box.add(Box.createHorizontalGlue());

        return box;
    }

    private Box buildAlphaArea(double alpha) {
        DoubleTextField field = new DoubleTextField(alpha, 4, NumberFormatUtil.getInstance().getNumberFormat());
        field.setFilter((value, oldValue) -> {
            if (0.0 <= value && value <= 1.0) {
                this.params.set("alpha", value);
                this.firePropertyChange("significanceChanged", oldValue, value);
                return value;
            }
            return oldValue;
        });

        Box box = Box.createHorizontalBox();
        box.add(new JLabel("Alpha: "));
        box.add(field);
        box.add(Box.createHorizontalGlue());
        return box;
    }

    private void buildMap(DataSet model) {
        for (Node node : model.getVariables()) {
            if (DataUtils.isBinary(model, model.getColumn(node))) {
                RegressionParamsEditorPanel.getVarMap().put(node.getName(), 1);
            } else if (node instanceof DiscreteVariable) {
                RegressionParamsEditorPanel.getVarMap().put(node.getName(), 2);
            } else {
                RegressionParamsEditorPanel.getVarMap().put(node.getName(), 3);
            }
        }
    }

    private JTextField createResponse(JList list) {
        JTextField pane = new JTextField();
        pane.setFont(RegressionParamsEditorPanel.getFONT());
        pane.setFocusable(true);
        pane.setEditable(false);
        pane.setBackground(list.getBackground());

        String target = this.regressionModel.getTargetName();
        if (target != null) {
            pane.setText(target);
        } else {
            pane.setText("Hello");
        }
        pane.setCaretPosition(0);
        LayoutUtils.setAllSizes(pane, new Dimension(100, pane.getPreferredSize().height));
        if (target == null) {
            pane.setText(null);
        }
        pane.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                RegressionParamsEditorPanel.getPredictorsList().clearSelection();
            }
        });

        return pane;
    }

    private List getPredictors() {
        ListModel model = RegressionParamsEditorPanel.getPredictorsList().getModel();
        List predictors = new ArrayList<>();
        for (int i = 0; i < model.getSize(); i++) {
            predictors.add((String) model.getElementAt(i));
        }
        return predictors;
    }

    private void addToSource(String var) {
        VariableListModel model = (VariableListModel) RegressionParamsEditorPanel.getSourceList().getModel();
        model.add(var);
    }

    private boolean isBinary(String node) {
        int i = RegressionParamsEditorPanel.getVarMap().get(node);
        return i == 1;
    }

    //========================== Inner classes (a lot of'em) =========================================//

    /**
     * 

Getter for the field params.

* * @return a {@link edu.cmu.tetrad.util.Parameters} object */ public Parameters getParams() { return this.params; } /** * A renderer that adds info about whether a variable is binary or not. */ private static class LogisticRegRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { String var = (String) value; if (var == null) { setText(" "); return this; } int binary = RegressionParamsEditorPanel.getVarMap().get(var); if (binary == 1) { var += " (Binary)"; } else if (binary == 2) { var += " (Discrete)"; } else if (binary == 3) { var += " (Continuous)"; } setText(var); if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } return this; } } /** * A basic model for the list (needed an addAll feature, which the detault model didn't have) */ private static class VariableListModel extends AbstractListModel { private final Vector delegate = new Vector<>(); public int getSize() { return this.delegate.size(); } public Object getElementAt(int index) { return this.delegate.get(index); } public void remove(Comparable element) { int index = this.delegate.indexOf(element); if (0 <= index) { this.delegate.remove(index); this.fireIntervalRemoved(this, index, index); } } public void add(Comparable element) { this.delegate.add(element); this.fireIntervalAdded(this, this.delegate.size(), this.delegate.size()); } public void removeFirst(Comparable element) { this.delegate.removeElement(element); this.fireContentsChanged(this, 0, this.delegate.size()); } public void removeAll(List elements) { this.delegate.removeAll(elements); this.fireContentsChanged(this, 0, this.delegate.size()); } public void addAll(List elements) { this.delegate.addAll(elements); this.fireContentsChanged(this, 0, this.delegate.size()); } public void removeAll() { this.delegate.clear(); this.fireContentsChanged(this, 0, 0); } public void sort() { Collections.sort(this.delegate); this.fireContentsChanged(this, 0, this.delegate.size()); } } /** * A basic transferable. */ private static class ListTransferable implements Transferable { private static final DataFlavor FLAVOR = RegressionParamsEditorPanel.getListDataFlavor(); private final List object; public ListTransferable(List object) { if (object == null) { throw new NullPointerException(); } this.object = object; } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[]{ListTransferable.FLAVOR}; } public boolean isDataFlavorSupported(DataFlavor flavor) { return flavor == ListTransferable.FLAVOR; } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (ListTransferable.FLAVOR != flavor) { throw new UnsupportedFlavorException(flavor); } return this.object; } } private class TargetListener extends DropTargetAdapter { public void drop(DropTargetDropEvent dtde) { Transferable t = dtde.getTransferable(); Component comp = dtde.getDropTargetContext().getComponent(); if (comp instanceof JList || comp instanceof JTextField) { try { // if response, remove everything first if (comp == RegressionParamsEditorPanel.getResponseField()) { String var = RegressionParamsEditorPanel.getResponseField().getText(); if (var != null && var.length() != 0) { addToSource(var); } List vars = (List) t.getTransferData(ListTransferable.FLAVOR); if (vars.isEmpty()) { dtde.rejectDrop(); return; } else if (1 < vars.size()) { JOptionPane.showMessageDialog(RegressionParamsEditorPanel.this, "There can only be one response variable."); dtde.rejectDrop(); return; } else if (RegressionParamsEditorPanel.this.logistic && !isBinary((String) vars.get(0))) { JOptionPane.showMessageDialog(RegressionParamsEditorPanel.this, "The response variable must be binary"); dtde.rejectDrop(); return; } RegressionParamsEditorPanel.getResponseField().setText((String) vars.get(0)); RegressionParamsEditorPanel.getResponseField().setCaretPosition(0); } else { JList list = (JList) comp; VariableListModel model = (VariableListModel) list.getModel(); List vars = (List) t.getTransferData(ListTransferable.FLAVOR); model.addAll(vars); } RegressionParamsEditorPanel.this.regressionModel.setTargetName(RegressionParamsEditorPanel.getResponseField().getText()); RegressionParamsEditorPanel.this.regressionModel.setRegressorName(getPredictors()); dtde.getDropTargetContext().dropComplete(true); } catch (Exception ex) { dtde.rejectDrop(); ex.printStackTrace(); } } else { dtde.rejectDrop(); } } } /** * A source/gesture listener for the JLists */ private class SourceListener extends DragSourceAdapter implements DragGestureListener { public void dragDropEnd(DragSourceDropEvent evt) { if (evt.getDropSuccess()) { Component comp = evt.getDragSourceContext().getComponent(); Transferable t = evt.getDragSourceContext().getTransferable(); if (t instanceof ListTransferable) { try { //noinspection unchecked List o = (List) t.getTransferData(ListTransferable.FLAVOR); if (comp instanceof JList list) { VariableListModel model = (VariableListModel) list.getModel(); for (Comparable c : o) { model.removeFirst(c); } } else { JTextField pane = (JTextField) comp; pane.setText(null); } RegressionParamsEditorPanel.this.regressionModel.setTargetName(RegressionParamsEditorPanel.getResponseField().getText()); RegressionParamsEditorPanel.this.regressionModel.setRegressorName(getPredictors()); } catch (Exception ex) { ex.printStackTrace(); } } } } public void dragGestureRecognized(DragGestureEvent dge) { Component comp = dge.getComponent(); List selected = null; if (comp instanceof JList list) { selected = list.getSelectedValuesList(); } else { JTextField pane = (JTextField) comp; String text = pane.getText(); if (text != null && text.length() != 0) { selected = Collections.singletonList(text); } } if (selected != null) { ListTransferable t = new ListTransferable(Collections.singletonList(selected)); dge.startDrag(DragSource.DefaultMoveDrop, t, this); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy