weka.gui.visualize.MatrixPanel.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.visualize.MatrixPanel.java

Source

/*
 *   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 3 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, see <http://www.gnu.org/licenses/>.
 */

/*
 *    MatrixPanel.java
 *    Copyright (C) 2002-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui.visualize;

import weka.core.Attribute;
import weka.core.Environment;
import weka.core.Instances;
import weka.core.Settings;
import weka.core.Utils;
import weka.gui.ExtensionFileFilter;
import weka.gui.WekaFileChooser;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

/**
 * This panel displays a plot matrix of the user selected attributes of a given
 * data set.
 * 
 * The datapoints are coloured using a discrete colouring set if the user has
 * selected a nominal attribute for colouring. If the user has selected a
 * numeric attribute then the datapoints are coloured using a colour spectrum
 * ranging from blue to red (low values to high). Datapoints missing a class
 * value are displayed in black.
 * 
 * @author Ashraf M. Kibriya (amk14@cs.waikato.ac.nz)
 * @version $Revision$
 */
public class MatrixPanel extends JPanel {

    /** for serialization */
    private static final long serialVersionUID = -1232642719869188740L;

    /** The that panel contains the actual matrix */
    private final Plot m_plotsPanel;

    /** The panel that displays the legend of the colouring attribute */
    protected final ClassPanel m_cp = new ClassPanel();

    /**
     * The panel that contains all the buttons and tools, i.e. resize, jitter bars
     * and sub-sampling buttons etc on the bottom of the panel
     */
    protected JPanel optionsPanel;

    /** Split pane for splitting the matrix and the buttons and bars */
    protected JSplitPane jp;
    /**
     * The button that updates the display to reflect the changes made by the
     * user. E.g. changed attribute set for the matrix
     */
    protected JButton m_updateBt = new JButton("Update");

    /** The button to display a window to select attributes */
    protected JButton m_selAttrib = new JButton("Select Attributes");

    /** The dataset for which this panel will display the plot matrix for */
    protected Instances m_data = null;

    /** The list for selecting the attributes to display the plot matrix */
    protected JList m_attribList = new JList();

    /** The scroll pane to scrolling the matrix */
    protected final JScrollPane m_js = new JScrollPane();

    /** The combo box to allow user to select the colouring attribute */
    protected JComboBox m_classAttrib = new JComboBox();

    /** The slider to adjust the size of the cells in the matrix */
    protected JSlider m_plotSize = new JSlider(50, 200, 100);

    /** The slider to adjust the size of the datapoints */
    protected JSlider m_pointSize = new JSlider(1, 10, 1);

    /** The slider to add jitter to the plots */
    protected JSlider m_jitter = new JSlider(0, 20, 0);

    /** For adding random jitter */
    private final Random rnd = new Random();

    /** Array containing precalculated jitter values */
    private int jitterVals[][];

    /** This stores the size of the datapoint */
    private int datapointSize = 1;

    /** The text area for percentage to resample data */
    protected JTextField m_resamplePercent = new JTextField(5);

    /** The label for resample percentage */
    protected JButton m_resampleBt = new JButton("SubSample % :");

    /** Random seed for random subsample */
    protected JTextField m_rseed = new JTextField(5);

    /** Displays the current size beside the slider bar for cell size */
    private final JLabel m_plotSizeLb = new JLabel("PlotSize: [100]");

    /** Displays the current size beside the slider bar for point size */
    private final JLabel m_pointSizeLb = new JLabel("PointSize: [10]");

    /** This array contains the indices of the attributes currently selected */
    private int[] m_selectedAttribs;

    /** This contains the index of the currently selected colouring attribute */
    private int m_classIndex;

    /**
     * This is a local array cache for all the instance values for faster
     * rendering
     */
    private int[][] m_points;

    /**
     * This is an array cache for the colour of each of the instances depending on
     * the colouring attribute. If the colouring attribute is nominal then it
     * contains the index of the colour in our colour list. Otherwise, for numeric
     * colouring attribute, it contains the precalculated red component for each
     * instance's colour
     */
    private int[] m_pointColors;

    /**
     * Contains true for each attribute value (only the selected attributes+class
     * attribute) that is missing, for each instance. m_missing[i][j] == true if
     * m_selectedAttribs[j] is missing in instance i.
     * m_missing[i][m_missing[].length-1] == true if class value is missing in
     * instance i.
     */
    private boolean[][] m_missing;

    /**
     * This array contains for the classAttribute: <br>
     * m_type[0] = [type of attribute, nominal, string or numeric]<br>
     * m_type[1] = [number of discrete values of nominal or string attribute <br>
     * or same as m_type[0] for numeric attribute]
     */
    private int[] m_type;

    /** Stores the maximum size for PlotSize label to keep it's size constant */
    private final Dimension m_plotLBSizeD;

    /** Stores the maximum size for PointSize label to keep it's size constant */
    private final Dimension m_pointLBSizeD;

    /** Contains discrete colours for colouring for nominal attributes */
    private final ArrayList<Color> m_colorList = new ArrayList<Color>();

    /** default colour list */
    private static final Color[] m_defaultColors = { Color.blue, Color.red, Color.cyan, new Color(75, 123, 130),
            Color.pink, Color.green, Color.orange, new Color(255, 0, 255), new Color(255, 0, 0),
            new Color(0, 255, 0), Color.black };

    /** color for the font used in column and row names */
    private final Color fontColor = new Color(98, 101, 156);

    /** font used in column and row names */
    private final java.awt.Font f = new java.awt.Font("Dialog", java.awt.Font.BOLD, 11);

    /** Settings (if available) to pass through to the VisualizePanels */
    protected Settings m_settings;

    /** For the background of the little plots */
    protected Color m_backgroundColor = Color.white;

    /**
     * ID of the owner (perspective, panel etc.) under which to lookup our
     * settings
     */
    protected String m_settingsOwnerID;

    protected transient Image m_osi = null;
    protected boolean[][] m_plottedCells;
    protected boolean m_regenerateOSI = true;
    protected boolean m_clearOSIPlottedCells;
    protected double m_previousPercent = -1;

    protected JCheckBox m_fastScroll = new JCheckBox("Fast scrolling (uses more memory)");

    /**
     * Constructor
     */
    public MatrixPanel() {
        m_rseed.setText("1");

        /** Setting up GUI **/
        m_selAttrib.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                final JDialog jd = new JDialog((JFrame) MatrixPanel.this.getTopLevelAncestor(),
                        "Attribute Selection Panel", ModalityType.DOCUMENT_MODAL);

                JPanel jp = new JPanel();
                JScrollPane js = new JScrollPane(m_attribList);
                JButton okBt = new JButton("OK");
                JButton cancelBt = new JButton("Cancel");
                final int[] savedSelection = m_attribList.getSelectedIndices();

                okBt.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        jd.dispose();
                    }
                });

                cancelBt.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        m_attribList.setSelectedIndices(savedSelection);
                        jd.dispose();
                    }
                });
                jd.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        m_attribList.setSelectedIndices(savedSelection);
                        jd.dispose();
                    }
                });
                jp.add(okBt);
                jp.add(cancelBt);

                jd.getContentPane().add(js, BorderLayout.CENTER);
                jd.getContentPane().add(jp, BorderLayout.SOUTH);

                if (js.getPreferredSize().width < 200) {
                    jd.setSize(250, 250);
                } else {
                    jd.setSize(js.getPreferredSize().width + 10, 250);
                }

                jd.setLocation(m_selAttrib.getLocationOnScreen().x,
                        m_selAttrib.getLocationOnScreen().y - jd.getHeight());
                jd.setVisible(true);
            }
        });

        m_updateBt.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updatePanel();
            }
        });
        m_updateBt.setPreferredSize(m_selAttrib.getPreferredSize());

        m_jitter.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent ce) {
                if (m_fastScroll.isSelected()) {
                    m_clearOSIPlottedCells = true;
                }
            }
        });

        m_plotSize.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent ce) {
                m_plotSizeLb.setText("PlotSize: [" + m_plotSize.getValue() + "]");
                m_plotSizeLb.setPreferredSize(m_plotLBSizeD);
                m_jitter.setMaximum(m_plotSize.getValue() / 5); // 20% of cell Size
                m_regenerateOSI = true;
            }
        });

        m_pointSize.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent ce) {
                m_pointSizeLb.setText("PointSize: [" + m_pointSize.getValue() + "]");
                m_pointSizeLb.setPreferredSize(m_pointLBSizeD);
                datapointSize = m_pointSize.getValue();
                if (m_fastScroll.isSelected()) {
                    m_clearOSIPlottedCells = true;
                }
            }
        });

        m_resampleBt.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JLabel rseedLb = new JLabel("Random Seed: ");
                JTextField rseedTxt = m_rseed;
                JLabel percentLb = new JLabel("Subsample as");
                JLabel percent2Lb = new JLabel("% of input: ");
                final JTextField percentTxt = new JTextField(5);
                percentTxt.setText(m_resamplePercent.getText());
                JButton doneBt = new JButton("Done");

                final JDialog jd = new JDialog((JFrame) MatrixPanel.this.getTopLevelAncestor(), "Subsample % Panel",
                        ModalityType.DOCUMENT_MODAL) {
                    private static final long serialVersionUID = -269823533147146296L;

                    @Override
                    public void dispose() {
                        m_resamplePercent.setText(percentTxt.getText());
                        super.dispose();
                    }
                };
                jd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

                doneBt.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent ae) {
                        jd.dispose();
                    }
                });
                GridBagLayout gbl = new GridBagLayout();
                GridBagConstraints gbc = new GridBagConstraints();
                JPanel p1 = new JPanel(gbl);
                gbc.anchor = GridBagConstraints.WEST;
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.insets = new Insets(0, 2, 2, 2);
                gbc.gridwidth = GridBagConstraints.RELATIVE;
                p1.add(rseedLb, gbc);
                gbc.weightx = 0;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.weightx = 1;
                p1.add(rseedTxt, gbc);
                gbc.insets = new Insets(8, 2, 0, 2);
                gbc.weightx = 0;
                p1.add(percentLb, gbc);
                gbc.insets = new Insets(0, 2, 2, 2);
                gbc.gridwidth = GridBagConstraints.RELATIVE;
                p1.add(percent2Lb, gbc);
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.weightx = 1;
                p1.add(percentTxt, gbc);
                gbc.insets = new Insets(8, 2, 2, 2);

                JPanel p3 = new JPanel(gbl);
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.gridwidth = GridBagConstraints.REMAINDER;
                gbc.weightx = 1;
                gbc.weighty = 0;
                p3.add(p1, gbc);
                gbc.insets = new Insets(8, 4, 8, 4);
                p3.add(doneBt, gbc);

                jd.getContentPane().setLayout(new BorderLayout());
                jd.getContentPane().add(p3, BorderLayout.NORTH);
                jd.pack();
                jd.setLocation(m_resampleBt.getLocationOnScreen().x,
                        m_resampleBt.getLocationOnScreen().y - jd.getHeight());
                jd.setVisible(true);
            }
        });

        optionsPanel = new JPanel(new GridBagLayout()); // all the rest of the
                                                        // panels are in here.
        final JPanel p2 = new JPanel(new BorderLayout()); // this has class colour
                                                          // panel
        final JPanel p3 = new JPanel(new GridBagLayout()); // this has update and
                                                           // select buttons
        final JPanel p4 = new JPanel(new GridBagLayout()); // this has the slider
                                                           // bars and combobox
        GridBagConstraints gbc = new GridBagConstraints();

        m_plotLBSizeD = m_plotSizeLb.getPreferredSize();
        m_pointLBSizeD = m_pointSizeLb.getPreferredSize();
        m_pointSizeLb.setText("PointSize: [1]");
        m_pointSizeLb.setPreferredSize(m_pointLBSizeD);
        m_resampleBt.setPreferredSize(m_selAttrib.getPreferredSize());

        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = new Insets(2, 2, 2, 2);
        p4.add(m_plotSizeLb, gbc);
        gbc.weightx = 1;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        p4.add(m_plotSize, gbc);
        gbc.weightx = 0;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        p4.add(m_pointSizeLb, gbc);
        gbc.weightx = 1;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        p4.add(m_pointSize, gbc);
        gbc.weightx = 0;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        p4.add(new JLabel("Jitter: "), gbc);
        gbc.weightx = 1;
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        p4.add(m_jitter, gbc);
        p4.add(m_classAttrib, gbc);

        gbc.gridwidth = GridBagConstraints.REMAINDER;
        gbc.weightx = 1;
        gbc.fill = GridBagConstraints.NONE;
        p3.add(m_fastScroll, gbc);
        p3.add(m_updateBt, gbc);
        p3.add(m_selAttrib, gbc);
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        gbc.weightx = 0;
        gbc.fill = GridBagConstraints.VERTICAL;
        gbc.anchor = GridBagConstraints.WEST;
        p3.add(m_resampleBt, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        p3.add(m_resamplePercent, gbc);

        p2.setBorder(BorderFactory.createTitledBorder("Class Colour"));
        p2.add(m_cp, BorderLayout.SOUTH);

        gbc.insets = new Insets(8, 5, 2, 5);
        gbc.anchor = GridBagConstraints.SOUTHWEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1;
        gbc.gridwidth = GridBagConstraints.RELATIVE;
        optionsPanel.add(p4, gbc);
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        optionsPanel.add(p3, gbc);
        optionsPanel.add(p2, gbc);

        m_fastScroll.setSelected(false);
        m_fastScroll.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!m_fastScroll.isSelected()) {
                    m_osi = null;
                } else {
                    m_plottedCells = new boolean[m_selectedAttribs.length][m_selectedAttribs.length];
                }
                MatrixPanel.this.invalidate();
                MatrixPanel.this.repaint();
            }
        });

        this.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent cv) {
                m_js.setMinimumSize(new Dimension(MatrixPanel.this.getWidth(),
                        MatrixPanel.this.getHeight() - optionsPanel.getPreferredSize().height - 10));
                jp.setDividerLocation(MatrixPanel.this.getHeight() - optionsPanel.getPreferredSize().height - 10);
            }
        });

        optionsPanel.setMinimumSize(new Dimension(0, 0));
        jp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, m_js, optionsPanel);
        jp.setOneTouchExpandable(true);
        jp.setResizeWeight(1);
        this.setLayout(new BorderLayout());
        this.add(jp, BorderLayout.CENTER);

        /** Setting up the initial color list **/
        for (int i = 0; i < m_defaultColors.length; i++) {
            m_colorList.add(m_defaultColors[i]);
        }

        /** Initializing internal fields and components **/
        m_selectedAttribs = m_attribList.getSelectedIndices();
        m_plotsPanel = new Plot();
        m_plotsPanel.setLayout(null);
        m_js.getHorizontalScrollBar().setUnitIncrement(10);
        m_js.getVerticalScrollBar().setUnitIncrement(10);
        m_js.setViewportView(m_plotsPanel);
        m_js.setColumnHeaderView(m_plotsPanel.getColHeader());
        m_js.setRowHeaderView(m_plotsPanel.getRowHeader());
        final JLabel lb = new JLabel(" Plot Matrix");
        lb.setFont(f);
        lb.setForeground(fontColor);
        lb.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        m_js.setCorner(JScrollPane.UPPER_LEFT_CORNER, lb);
        m_cp.setInstances(m_data);
        m_cp.setBorder(BorderFactory.createEmptyBorder(15, 10, 10, 10));
        m_cp.addRepaintNotify(m_plotsPanel);
        // m_updateBt.doClick(); //not until setting up the instances
    }

    /**
     * Initializes internal data fields, i.e. data values, type, missing and color
     * cache arrays
     */
    public void initInternalFields() {
        Instances inst = m_data;
        m_classIndex = m_classAttrib.getSelectedIndex();
        m_selectedAttribs = m_attribList.getSelectedIndices();
        double minC = 0, maxC = 0;

        /** Resampling **/
        double currentPercent = Double.parseDouble(m_resamplePercent.getText());
        if (currentPercent <= 100) {
            if (currentPercent != m_previousPercent) {
                m_clearOSIPlottedCells = true;
            }
            inst = new Instances(m_data, 0, m_data.numInstances());
            inst.randomize(new Random(Integer.parseInt(m_rseed.getText())));

            // System.err.println("gettingPercent: " +
            // Math.round(
            // Double.parseDouble(m_resamplePercent.getText())
            // / 100D * m_data.numInstances()
            // )
            // );

            inst = new Instances(inst, 0, (int) Math.round(currentPercent / 100D * inst.numInstances()));
            m_previousPercent = currentPercent;
        }
        m_points = new int[inst.numInstances()][m_selectedAttribs.length]; // changed
        m_pointColors = new int[inst.numInstances()];
        m_missing = new boolean[inst.numInstances()][m_selectedAttribs.length + 1]; // changed
        m_type = new int[2]; // [m_selectedAttribs.length]; //changed
        jitterVals = new int[inst.numInstances()][2];

        /**
         * Setting up the color list for non-numeric attribute as well as jittervals
         **/
        if (!(inst.attribute(m_classIndex).isNumeric())) {

            for (int i = m_colorList.size(); i < inst.attribute(m_classIndex).numValues() + 1; i++) {
                Color pc = m_defaultColors[i % 10];
                int ija = i / 10;
                ija *= 2;
                for (int j = 0; j < ija; j++) {
                    pc = pc.darker();
                }
                m_colorList.add(pc);
            }

            for (int i = 0; i < inst.numInstances(); i++) {
                // set to black for missing class value which is last colour is default
                // list
                if (inst.instance(i).isMissing(m_classIndex)) {
                    m_pointColors[i] = m_defaultColors.length - 1;
                } else {
                    m_pointColors[i] = (int) inst.instance(i).value(m_classIndex);
                }

                jitterVals[i][0] = rnd.nextInt(m_jitter.getValue() + 1) - m_jitter.getValue() / 2;
                jitterVals[i][1] = rnd.nextInt(m_jitter.getValue() + 1) - m_jitter.getValue() / 2;

            }
        }
        /** Setting up color variations for numeric attribute as well as jittervals **/
        else {
            for (int i = 0; i < inst.numInstances(); i++) {
                if (!(inst.instance(i).isMissing(m_classIndex))) {
                    minC = maxC = inst.instance(i).value(m_classIndex);
                    break;
                }
            }

            for (int i = 1; i < inst.numInstances(); i++) {
                if (!(inst.instance(i).isMissing(m_classIndex))) {
                    if (minC > inst.instance(i).value(m_classIndex)) {
                        minC = inst.instance(i).value(m_classIndex);
                    }
                    if (maxC < inst.instance(i).value(m_classIndex)) {
                        maxC = inst.instance(i).value(m_classIndex);
                    }
                }
            }

            for (int i = 0; i < inst.numInstances(); i++) {
                double r = (inst.instance(i).value(m_classIndex) - minC) / (maxC - minC);
                r = (r * 240) + 15;
                m_pointColors[i] = (int) r;

                jitterVals[i][0] = rnd.nextInt(m_jitter.getValue() + 1) - m_jitter.getValue() / 2;
                jitterVals[i][1] = rnd.nextInt(m_jitter.getValue() + 1) - m_jitter.getValue() / 2;
            }
        }

        /** Creating local cache of the data values **/
        double min[] = new double[m_selectedAttribs.length], max = 0; // changed
        double ratio[] = new double[m_selectedAttribs.length]; // changed
        double cellSize = m_plotSize.getValue(), temp1 = 0, temp2 = 0;

        for (int j = 0; j < m_selectedAttribs.length; j++) {
            int i;
            for (i = 0; i < inst.numInstances(); i++) {
                min[j] = max = 0;
                if (!(inst.instance(i).isMissing(m_selectedAttribs[j]))) {
                    min[j] = max = inst.instance(i).value(m_selectedAttribs[j]);
                    break;
                }
            }
            for (; i < inst.numInstances(); i++) {
                if (!(inst.instance(i).isMissing(m_selectedAttribs[j]))) {
                    if (inst.instance(i).value(m_selectedAttribs[j]) < min[j]) {
                        min[j] = inst.instance(i).value(m_selectedAttribs[j]);
                    }
                    if (inst.instance(i).value(m_selectedAttribs[j]) > max) {
                        max = inst.instance(i).value(m_selectedAttribs[j]);
                    }
                }
            }
            ratio[j] = cellSize / (max - min[j]);
        }

        boolean classIndexProcessed = false;
        for (int j = 0; j < m_selectedAttribs.length; j++) {
            if (inst.attribute(m_selectedAttribs[j]).isNominal()
                    || inst.attribute(m_selectedAttribs[j]).isString()) {
                // m_type[0][j] = 1; m_type[1][j] =
                // inst.attribute(m_selectedAttribs[j]).numValues();

                temp1 = cellSize / inst.attribute(m_selectedAttribs[j]).numValues(); // m_type[1][j];
                temp2 = temp1 / 2;
                for (int i = 0; i < inst.numInstances(); i++) {
                    m_points[i][j] = (int) Math.round(temp2 + temp1 * inst.instance(i).value(m_selectedAttribs[j]));
                    if (inst.instance(i).isMissing(m_selectedAttribs[j])) {
                        m_missing[i][j] = true; // represents missing value
                        if (m_selectedAttribs[j] == m_classIndex) {
                            m_missing[i][m_missing[0].length - 1] = true;
                            classIndexProcessed = true;
                        }
                    }
                }
            } else {
                // m_type[0][j] = m_type[1][j] = 0;
                for (int i = 0; i < inst.numInstances(); i++) {
                    m_points[i][j] = (int) Math
                            .round((inst.instance(i).value(m_selectedAttribs[j]) - min[j]) * ratio[j]);
                    if (inst.instance(i).isMissing(m_selectedAttribs[j])) {
                        m_missing[i][j] = true; // represents missing value
                        if (m_selectedAttribs[j] == m_classIndex) {
                            m_missing[i][m_missing[0].length - 1] = true;
                            classIndexProcessed = true;
                        }
                    }
                }
            }
        }

        if (inst.attribute(m_classIndex).isNominal() || inst.attribute(m_classIndex).isString()) {
            m_type[0] = 1;
            m_type[1] = inst.attribute(m_classIndex).numValues();
        } else {
            m_type[0] = m_type[1] = 0;
        }

        if (classIndexProcessed == false) { // class Index has not been processed as
                                            // class index is not among the selected
                                            // attribs
            for (int i = 0; i < inst.numInstances(); i++) {
                if (inst.instance(i).isMissing(m_classIndex)) {
                    m_missing[i][m_missing[0].length - 1] = true;
                }
            }
        }

        m_cp.setColours(m_colorList);
    }

    /**
     * Sets up the UI's attributes lists
     */
    public void setupAttribLists() {
        String[] tempAttribNames = new String[m_data.numAttributes()];
        String type;

        m_classAttrib.removeAllItems();
        for (int i = 0; i < tempAttribNames.length; i++) {
            type = " (" + Attribute.typeToStringShort(m_data.attribute(i)) + ")";
            tempAttribNames[i] = new String("Colour: " + m_data.attribute(i).name() + " " + type);
            m_classAttrib.addItem(tempAttribNames[i]);
        }
        if (m_data.classIndex() == -1) {
            m_classAttrib.setSelectedIndex(tempAttribNames.length - 1);
        } else {
            m_classAttrib.setSelectedIndex(m_data.classIndex());
        }
        m_attribList.setListData(tempAttribNames);
        m_attribList.setSelectionInterval(0, tempAttribNames.length - 1);
    }

    /**
     * Calculates the percentage to resample
     */
    public void setPercent() {
        if (m_data.numInstances() > 700) {
            double percnt = 500D / m_data.numInstances() * 100;
            percnt *= 100;
            percnt = Math.round(percnt);
            percnt /= 100;

            m_resamplePercent.setText("" + percnt);
        } else {
            m_resamplePercent.setText("100");
        }
    }

    /**
     * This method changes the Instances object of this class to a new one. It
     * also does all the necessary initializations for displaying the panel. This
     * must be called before trying to display the panel.
     * 
     * @param newInst The new set of Instances
     */
    public void setInstances(Instances newInst) {

        m_osi = null;
        m_fastScroll.setSelected(false);
        m_data = newInst;
        setPercent();
        setupAttribLists();
        m_rseed.setText("1");
        initInternalFields();
        m_cp.setInstances(m_data);
        m_cp.setCindex(m_classIndex);
        m_updateBt.doClick();
    }

    /**
     * Main method for testing this class
     */
    public static void main(String[] args) {
        final JFrame jf = new JFrame("Weka Explorer: MatrixPanel");
        final JButton setBt = new JButton("Set Instances");
        Instances data = null;
        try {
            if (args.length == 1) {
                data = new Instances(new BufferedReader(new FileReader(args[0])));
            } else {
                System.out.println("Usage: MatrixPanel <arff file>");
                System.exit(-1);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(-1);
        }

        final MatrixPanel mp = new MatrixPanel();
        mp.setInstances(data);
        setBt.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                WekaFileChooser chooser = new WekaFileChooser(new java.io.File(System.getProperty("user.dir")));
                ExtensionFileFilter myfilter = new ExtensionFileFilter("arff", "Arff data files");
                chooser.setFileFilter(myfilter);
                int returnVal = chooser.showOpenDialog(jf);

                if (returnVal == JFileChooser.APPROVE_OPTION) {
                    try {
                        System.out.println("You chose to open this file: " + chooser.getSelectedFile().getName());
                        Instances in = new Instances(new FileReader(chooser.getSelectedFile().getAbsolutePath()));
                        mp.setInstances(in);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });
        // System.out.println("Loaded: "+args[0]+"\nRelation: "+data.relationName()+"\nAttributes: "+data.numAttributes());
        // System.out.println("The attributes are: ");
        // for(int i=0; i<data.numAttributes(); i++)
        // System.out.println(data.attribute(i).name());

        // RepaintManager.currentManager(jf.getRootPane()).setDoubleBufferingEnabled(false);
        jf.getContentPane().setLayout(new BorderLayout());
        jf.getContentPane().add(mp, BorderLayout.CENTER);
        jf.getContentPane().add(setBt, BorderLayout.SOUTH);
        jf.getContentPane().setFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 11));
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setSize(800, 600);
        jf.setVisible(true);
        jf.repaint();
    }

    /**
     * Internal class responsible for displaying the actual matrix Requires the
     * internal data fields of the parent class to be properly initialized before
     * being created
     */
    private class Plot extends JPanel implements MouseMotionListener, MouseListener {

        /** for serialization */
        private static final long serialVersionUID = -1721245738439420882L;

        int extpad = 3, intpad = 4, cellSize = 100, cellRange = 100;
        java.awt.Rectangle r;
        java.awt.FontMetrics fm;
        int lastxpos, lastypos;
        JPanel jPlColHeader, jPlRowHeader;

        /**
         * Constructor
         */
        public Plot() {
            super();
            this.setToolTipText("blah");
            this.addMouseMotionListener(this);
            this.addMouseListener(this);
            initialize();
        }

        /** Initializes the internal fields */
        public void initialize() {
            lastxpos = lastypos = 0;
            cellRange = cellSize;
            cellSize = cellRange + 2 * intpad;

            jPlColHeader = new JPanel() {
                private static final long serialVersionUID = -9098547751937467506L;
                java.awt.Rectangle r;

                @Override
                public void paint(Graphics g) {
                    r = g.getClipBounds();
                    g.setColor(this.getBackground());
                    g.fillRect(r.x, r.y, r.width, r.height);
                    g.setFont(f);
                    fm = g.getFontMetrics();
                    int xpos = 0, ypos = 0, attribWidth = 0;

                    g.setColor(fontColor);
                    xpos = extpad;
                    ypos = extpad + fm.getHeight();

                    for (int m_selectedAttrib : m_selectedAttribs) {
                        if (xpos + cellSize < r.x) {
                            xpos += cellSize + extpad;
                            continue;
                        } else if (xpos > r.x + r.width) {
                            break;
                        } else {
                            attribWidth = fm.stringWidth(m_data.attribute(m_selectedAttrib).name());
                            g.drawString(m_data.attribute(m_selectedAttrib).name(),
                                    (attribWidth < cellSize) ? (xpos + (cellSize / 2 - attribWidth / 2)) : xpos,
                                    ypos);
                        }
                        xpos += cellSize + extpad;
                    }
                    fm = null;
                    r = null;
                }

                @Override
                public Dimension getPreferredSize() {
                    fm = this.getFontMetrics(this.getFont());
                    return new Dimension(m_selectedAttribs.length * (cellSize + extpad),
                            2 * extpad + fm.getHeight());
                }
            };

            jPlRowHeader = new JPanel() {
                private static final long serialVersionUID = 8474957069309552844L;

                java.awt.Rectangle r;

                @Override
                public void paint(Graphics g) {
                    r = g.getClipBounds();
                    g.setColor(this.getBackground());
                    g.fillRect(r.x, r.y, r.width, r.height);
                    g.setFont(f);
                    fm = g.getFontMetrics();
                    int xpos = 0, ypos = 0;

                    g.setColor(fontColor);
                    xpos = extpad;
                    ypos = extpad;

                    for (int j = m_selectedAttribs.length - 1; j >= 0; j--) {
                        if (ypos + cellSize < r.y) {
                            ypos += cellSize + extpad;
                            continue;
                        } else if (ypos > r.y + r.height) {
                            break;
                        } else {
                            g.drawString(m_data.attribute(m_selectedAttribs[j]).name(), xpos + extpad,
                                    ypos + cellSize / 2);
                        }
                        xpos = extpad;
                        ypos += cellSize + extpad;
                    }
                    r = null;
                }

                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(100 + extpad, m_selectedAttribs.length * (cellSize + extpad));
                }
            };
            jPlColHeader.setFont(f);
            jPlRowHeader.setFont(f);
            this.setFont(f);
        }

        public JPanel getRowHeader() {
            return jPlRowHeader;
        }

        public JPanel getColHeader() {
            return jPlColHeader;
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            Graphics g = this.getGraphics();
            int xpos = extpad, ypos = extpad;

            for (int j = m_selectedAttribs.length - 1; j >= 0; j--) {
                for (@SuppressWarnings("unused")
                int m_selectedAttrib : m_selectedAttribs) {
                    if (e.getX() >= xpos && e.getX() <= xpos + cellSize + extpad) {
                        if (e.getY() >= ypos && e.getY() <= ypos + cellSize + extpad) {
                            if (xpos != lastxpos || ypos != lastypos) {
                                g.setColor(Color.red);
                                g.drawRect(xpos - 1, ypos - 1, cellSize + 1, cellSize + 1);
                                if (lastxpos != 0 && lastypos != 0) {
                                    g.setColor(this.getBackground().darker());
                                    g.drawRect(lastxpos - 1, lastypos - 1, cellSize + 1, cellSize + 1);
                                }
                                lastxpos = xpos;
                                lastypos = ypos;
                            }
                            return;
                        }
                    }
                    xpos += cellSize + extpad;
                }
                xpos = extpad;
                ypos += cellSize + extpad;
            }
            if (lastxpos != 0 && lastypos != 0) {
                g.setColor(this.getBackground().darker());
                g.drawRect(lastxpos - 1, lastypos - 1, cellSize + 1, cellSize + 1);
            }
            lastxpos = lastypos = 0;
        }

        @Override
        public void mouseDragged(MouseEvent e) {
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            int i = 0, j = 0, found = 0;

            int xpos = extpad, ypos = extpad;
            for (j = m_selectedAttribs.length - 1; j >= 0; j--) {
                for (i = 0; i < m_selectedAttribs.length; i++) {
                    if (e.getX() >= xpos && e.getX() <= xpos + cellSize + extpad) {
                        if (e.getY() >= ypos && e.getY() <= ypos + cellSize + extpad) {
                            found = 1;
                            break;
                        }
                    }
                    xpos += cellSize + extpad;
                }
                if (found == 1) {
                    break;
                }
                xpos = extpad;
                ypos += cellSize + extpad;
            }
            if (found == 0) {
                return;
            }

            JFrame jf = Utils.getWekaJFrame("Weka Explorer: Visualizing " + m_data.relationName(), this);
            VisualizePanel vp = new VisualizePanel();
            try {
                PlotData2D pd = new PlotData2D(m_data);
                pd.setPlotName("Master Plot");
                vp.setMasterPlot(pd);
                // System.out.println("x: "+i+" y: "+j);
                vp.setXIndex(m_selectedAttribs[i]);
                vp.setYIndex(m_selectedAttribs[j]);
                vp.m_ColourCombo.setSelectedIndex(m_classIndex);
                if (m_settings != null) {
                    vp.applySettings(m_settings, m_settingsOwnerID);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            jf.getContentPane().add(vp);
            jf.addWindowListener(new java.awt.event.WindowAdapter() {
                @Override
                public void windowClosing(java.awt.event.WindowEvent e) {
                    jf.dispose();
                }
            });
            jf.pack();
            jf.setSize(800, 600);
            jf.setLocationRelativeTo(SwingUtilities.getWindowAncestor(this));
            jf.setVisible(true);
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }

        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }

        /**
         * sets the new jitter value for the plots
         */
        public void setJitter(int newjitter) {
        }

        /**
         * sets the new size for the plots
         */
        public void setCellSize(int newCellSize) {
            cellSize = newCellSize;
            initialize();
        }

        /**
         * Returns the X and Y attributes of the plot the mouse is currently on
         */
        @Override
        public String getToolTipText(MouseEvent event) {
            int xpos = extpad, ypos = extpad;

            for (int j = m_selectedAttribs.length - 1; j >= 0; j--) {
                for (int m_selectedAttrib : m_selectedAttribs) {
                    if (event.getX() >= xpos && event.getX() <= xpos + cellSize + extpad) {
                        if (event.getY() >= ypos && event.getY() <= ypos + cellSize + extpad) {
                            return ("X: " + m_data.attribute(m_selectedAttrib).name() + " Y: "
                                    + m_data.attribute(m_selectedAttribs[j]).name() + " (click to enlarge)");
                        }
                    }
                    xpos += cellSize + extpad;
                }
                xpos = extpad;
                ypos += cellSize + extpad;
            }
            return ("Matrix Panel");
        }

        /**
         * Paints a single Plot at xpos, ypos. and xattrib and yattrib on X and Y
         * axes
         */
        public void paintGraph(Graphics g, int xattrib, int yattrib, int xpos, int ypos) {
            int x, y;
            g.setColor(m_backgroundColor.equals(Color.BLACK) ? m_backgroundColor.brighter().brighter()
                    : m_backgroundColor.darker().darker());
            g.drawRect(xpos - 1, ypos - 1, cellSize + 1, cellSize + 1);
            g.setColor(m_backgroundColor);
            g.fillRect(xpos, ypos, cellSize, cellSize);
            for (int i = 0; i < m_points.length; i++) {

                if (!(m_missing[i][yattrib] || m_missing[i][xattrib])) {

                    if (m_type[0] == 0) {
                        if (m_missing[i][m_missing[0].length - 1]) {
                            g.setColor(m_defaultColors[m_defaultColors.length - 1]);
                        } else {
                            g.setColor(new Color(m_pointColors[i], 150, (255 - m_pointColors[i])));
                        }
                    } else {
                        g.setColor(m_colorList.get(m_pointColors[i]));
                    }

                    if (m_points[i][xattrib] + jitterVals[i][0] < 0
                            || m_points[i][xattrib] + jitterVals[i][0] > cellRange) {
                        if (cellRange - m_points[i][yattrib] + jitterVals[i][1] < 0
                                || cellRange - m_points[i][yattrib] + jitterVals[i][1] > cellRange) {
                            // both x and y out of range don't add jitter
                            x = intpad + m_points[i][xattrib];
                            y = intpad + (cellRange - m_points[i][yattrib]);
                        } else {
                            // only x out of range
                            x = intpad + m_points[i][xattrib];
                            y = intpad + (cellRange - m_points[i][yattrib]) + jitterVals[i][1];
                        }
                    } else if (cellRange - m_points[i][yattrib] + jitterVals[i][1] < 0
                            || cellRange - m_points[i][yattrib] + jitterVals[i][1] > cellRange) {
                        // only y out of range
                        x = intpad + m_points[i][xattrib] + jitterVals[i][0];
                        y = intpad + (cellRange - m_points[i][yattrib]);
                    } else {
                        // none out of range
                        x = intpad + m_points[i][xattrib] + jitterVals[i][0];
                        y = intpad + (cellRange - m_points[i][yattrib]) + jitterVals[i][1];
                    }
                    if (datapointSize == 1) {
                        g.drawLine(x + xpos, y + ypos, x + xpos, y + ypos);
                    } else {
                        g.drawOval(x + xpos - datapointSize / 2, y + ypos - datapointSize / 2, datapointSize,
                                datapointSize);
                    }
                }
            }
            g.setColor(fontColor);
        }

        private void createOSI() {
            int iwidth = this.getWidth();
            int iheight = this.getHeight();
            m_osi = this.createImage(iwidth, iheight);
            clearOSI();
        }

        private void clearOSI() {
            if (m_osi == null) {
                return;
            }

            int iwidth = this.getWidth();
            int iheight = this.getHeight();
            Graphics m = m_osi.getGraphics();
            m.setColor(this.getBackground().darker().darker());
            m.fillRect(0, 0, iwidth, iheight);
        }

        /**
         * Paints the matrix of plots in the current visible region
         */
        public void paintME(Graphics g) {
            Graphics g2 = g;
            if (m_osi == null && m_fastScroll.isSelected()) {
                createOSI();
            }
            if (m_osi != null && m_fastScroll.isSelected()) {
                g2 = m_osi.getGraphics();
            }
            r = g.getClipBounds();

            g.setColor(this.getBackground());
            g.fillRect(r.x, r.y, r.width, r.height);
            g.setColor(fontColor);

            int xpos = 0, ypos = 0;

            xpos = extpad;
            ypos = extpad;

            for (int j = m_selectedAttribs.length - 1; j >= 0; j--) {
                if (ypos + cellSize < r.y) {
                    ypos += cellSize + extpad;
                    continue;
                } else if (ypos > r.y + r.height) {
                    break;
                } else {
                    for (int i = 0; i < m_selectedAttribs.length; i++) {
                        if (xpos + cellSize < r.x) {
                            xpos += cellSize + extpad;
                            continue;
                        } else if (xpos > r.x + r.width) {
                            break;
                        } else if (m_fastScroll.isSelected()) {
                            if (!m_plottedCells[i][j]) {
                                paintGraph(g2, i, j, xpos, ypos); // m_selectedAttribs[i],
                                                                  // m_selectedAttribs[j], xpos,
                                                                  // ypos);
                                m_plottedCells[i][j] = true;
                            }
                        } else {
                            paintGraph(g2, i, j, xpos, ypos);
                        }
                        xpos += cellSize + extpad;
                    }
                }
                xpos = extpad;
                ypos += cellSize + extpad;
            }
        }

        /**
         * paints this JPanel (PlotsPanel)
         */
        @Override
        public void paintComponent(Graphics g) {
            paintME(g);
            if (m_osi != null && m_fastScroll.isSelected()) {
                g.drawImage(m_osi, 0, 0, this);
            }
        }
    }

    /**
     * Set the point size for the plots
     *
     * @param pointSize the point size to use
     */
    public void setPointSize(int pointSize) {
        if (pointSize <= m_pointSize.getMaximum() && pointSize > m_pointSize.getMinimum()) {
            m_pointSize.setValue(pointSize);
        }
    }

    /**
     * Set the plot size
     *
     * @param plotSize the plot size to use
     */
    public void setPlotSize(int plotSize) {
        if (plotSize >= m_plotSize.getMinimum() && plotSize <= m_plotSize.getMaximum()) {
            m_plotSize.setValue(plotSize);
        }
    }

    /**
     * Set the background colour for the cells in the matrix
     * 
     * @param c the background colour
     */
    public void setPlotBackgroundColour(Color c) {
        m_backgroundColor = c;
    }

    /**
     * @param settings
     * @param ownerID
     */
    public void applySettings(Settings settings, String ownerID) {
        m_settings = settings;
        m_settingsOwnerID = ownerID;

        setPointSize(settings.getSetting(ownerID, weka.gui.explorer.VisualizePanel.ScatterDefaults.POINT_SIZE_KEY,
                weka.gui.explorer.VisualizePanel.ScatterDefaults.POINT_SIZE, Environment.getSystemWide()));

        setPlotSize(settings.getSetting(ownerID, weka.gui.explorer.VisualizePanel.ScatterDefaults.PLOT_SIZE_KEY,
                weka.gui.explorer.VisualizePanel.ScatterDefaults.PLOT_SIZE, Environment.getSystemWide()));

        setPlotBackgroundColour(settings.getSetting(ownerID, VisualizeUtils.VisualizeDefaults.BACKGROUND_COLOUR_KEY,
                VisualizeUtils.VisualizeDefaults.BACKGROUND_COLOR, Environment.getSystemWide()));
    }

    /**
     * Update the display. Typically called after changing plot size, point size
     * etc.
     */
    public void updatePanel() {
        // m_selectedAttribs = m_attribList.getSelectedIndices();
        initInternalFields();

        Plot a = m_plotsPanel;
        a.setCellSize(m_plotSize.getValue());
        Dimension d = new Dimension((m_selectedAttribs.length) * (a.cellSize + a.extpad) + 2,
                (m_selectedAttribs.length) * (a.cellSize + a.extpad) + 2);
        // System.out.println("Size: "+a.cellSize+" Extpad: "+
        // a.extpad+" selected: "+
        // m_selectedAttribs.length+' '+d);
        a.setPreferredSize(d);
        a.setSize(a.getPreferredSize());
        a.setJitter(m_jitter.getValue());

        if (m_fastScroll.isSelected() && m_clearOSIPlottedCells) {
            m_plottedCells = new boolean[m_selectedAttribs.length][m_selectedAttribs.length];
            m_clearOSIPlottedCells = false;
        }

        if (m_regenerateOSI) {
            m_osi = null;
        }
        m_js.revalidate();
        m_cp.setColours(m_colorList);
        m_cp.setCindex(m_classIndex);
        m_regenerateOSI = false;

        repaint();
    }
}