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

weka.gui.visualize.AttributePanel Maven / Gradle / Ivy

Go to download

The Waikato Environment for Knowledge Analysis (WEKA), a machine learning workbench. This is the stable version. Apart from bugfixes, this version does not receive any other updates.

There is a newer version: 3.8.6
Show newest version
/*
 *   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 .
 */

/*
 *    AttributePanel.java
 *    Copyright (C) 1999-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 javax.swing.JPanel;
import javax.swing.JScrollPane;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

/**
 * This panel displays one dimensional views of the attributes in a dataset.
 * Colouring is done on the basis of a column in the dataset or an auxiliary
 * array (useful for colouring cluster predictions).
 * 
 * @author Malcolm Ware ([email protected])
 * @author Mark Hall ([email protected])
 * @version $Revision: 12391 $
 */
public class AttributePanel extends JScrollPane {

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

  /** The instances to be plotted */
  protected Instances m_plotInstances = null;

  /** Holds the min and max values of the colouring attributes */
  protected double m_maxC;
  protected double m_minC;
  protected int m_cIndex;
  protected int m_xIndex;
  protected int m_yIndex;

  /** The colour map to use for colouring points */
  protected ArrayList m_colorList;

  /** default colours for colouring discrete class */
  protected Color[] m_DefaultColors = { Color.blue, Color.red, Color.green,
    Color.cyan, Color.pink, new Color(255, 0, 255), Color.orange,
    new Color(255, 0, 0), new Color(0, 255, 0), Color.white };

  /**
   * If set, it allows this panel to avoid setting a color in the color list
   * that is equal to the background color
   */
  protected Color m_backgroundColor = null;

  /** The list of things listening to this panel */
  protected ArrayList m_Listeners =
    new ArrayList();

  /** Holds the random height for each instance. */
  protected int[] m_heights;
  // protected Color[] colors_array;

  /**
   * The container window for the attribute bars, and also where the X,Y or B
   * get printed.
   */
  protected JPanel m_span = null;

  /**
   * The default colour to use for the background of the bars if a colour is not
   * defined in Visualize.props
   */
  protected Color m_barColour = Color.black;

  /**
   * inner inner class used for plotting the points into a bar for a particular
   * attribute.
   */
  protected class AttributeSpacing extends JPanel {

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

    /** The min and max values for this attribute. */
    protected double m_maxVal;
    protected double m_minVal;

    /** The attribute itself. */
    protected Attribute m_attrib;

    /** The index for this attribute. */
    protected int m_attribIndex;

    /** The x position of each point. */
    protected int[] m_cached;
    // note for m_cached, if you wanted to speed up the drawing algorithm
    // and save memory, the system could be setup to drop out any
    // of the instances not being drawn (you would need to find a new way
    // of matching the height however).

    /**
     * A temporary array used to strike any instances that would be drawn
     * redundantly.
     */
    protected boolean[][] m_pointDrawn;

    /** Used to determine if the positions need to be recalculated. */
    protected int m_oldWidth = -9000;

    /**
     * The container window for the attribute bars, and also where the X,Y or B
     * get printed.
     */

    /**
     * This constructs the bar with the specified attribute and sets its index
     * to be used for selecting by the mouse.
     * 
     * @param a The attribute this bar represents.
     * @param aind The index of this bar.
     */
    public AttributeSpacing(Attribute a, int aind) {
      m_attrib = a;
      m_attribIndex = aind;
      this.setBackground(m_barColour);
      this.setPreferredSize(new Dimension(0, 20));
      this.setMinimumSize(new Dimension(0, 20));
      m_cached = new int[m_plotInstances.numInstances()];

      // this will only get allocated if m_plotInstances != null
      // this is used to determine the min and max values for plotting
      double min = Double.POSITIVE_INFINITY;
      double max = Double.NEGATIVE_INFINITY;
      double value;
      if (m_plotInstances.attribute(m_attribIndex).isNominal()) {
        m_minVal = 0;
        m_maxVal = m_plotInstances.attribute(m_attribIndex).numValues() - 1;
      } else {
        for (int i = 0; i < m_plotInstances.numInstances(); i++) {
          if (!m_plotInstances.instance(i).isMissing(m_attribIndex)) {
            value = m_plotInstances.instance(i).value(m_attribIndex);
            if (value < min) {
              min = value;
            }
            if (value > max) {
              max = value;
            }
          }
        }
        m_minVal = min;
        m_maxVal = max;
        if (min == max) {
          m_maxVal += 0.05;
          m_minVal -= 0.05;
        }
      }

      this.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
          if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
            setX(m_attribIndex);
            if (m_Listeners.size() > 0) {
              for (int i = 0; i < m_Listeners.size(); i++) {
                AttributePanelListener l = (m_Listeners.get(i));
                l.attributeSelectionChange(new AttributePanelEvent(true, false,
                  m_attribIndex));
              }
            }
          } else {
            // put it on the y axis
            setY(m_attribIndex);
            if (m_Listeners.size() > 0) {
              for (int i = 0; i < m_Listeners.size(); i++) {
                AttributePanelListener l = (m_Listeners.get(i));
                l.attributeSelectionChange(new AttributePanelEvent(false, true,
                  m_attribIndex));
              }
            }
          }
        }
      });
    }

    /**
     * Convert an raw x value to Panel x coordinate.
     * 
     * @param val the raw x value
     * @return an x value for plotting in the panel.
     */
    private double convertToPanel(double val) {
      double temp = (val - m_minVal) / (m_maxVal - m_minVal);
      double temp2 = temp * (this.getWidth() - 10);

      return temp2 + 4;
    }

    /**
     * paints all the visible instances to the panel , and recalculates their
     * position if need be.
     * 
     * @param gx The graphics context.
     */
    @Override
    public void paintComponent(Graphics gx) {
      setBackground(m_barColour);
      super.paintComponent(gx);
      int xp, yp, h;
      h = this.getWidth();
      if (m_plotInstances != null && m_plotInstances.numAttributes() > 0
        && m_plotInstances.numInstances() > 0) {

        if (m_oldWidth != h) {
          m_pointDrawn = new boolean[h][20];
          for (int noa = 0; noa < m_plotInstances.numInstances(); noa++) {
            if (!m_plotInstances.instance(noa).isMissing(m_attribIndex)
              && !m_plotInstances.instance(noa).isMissing(m_cIndex)) {
              m_cached[noa] =
                (int) convertToPanel(m_plotInstances.instance(noa).value(
                  m_attribIndex));

              if (m_pointDrawn[m_cached[noa] % h][m_heights[noa]]) {
                m_cached[noa] = -9000;
              } else {
                m_pointDrawn[m_cached[noa] % h][m_heights[noa]] = true;
              }

            } else {
              m_cached[noa] = -9000; // this value will not happen
              // so it is safe
            }
          }
          m_oldWidth = h;
        }

        if (m_plotInstances.attribute(m_cIndex).isNominal()) {
          for (int noa = 0; noa < m_plotInstances.numInstances(); noa++) {

            if (m_cached[noa] != -9000) {
              xp = m_cached[noa];
              yp = m_heights[noa];
              if (m_plotInstances.attribute(m_attribIndex).isNominal()) {
                xp += (int) (Math.random() * 5) - 2;
              }
              int ci = (int) m_plotInstances.instance(noa).value(m_cIndex);

              gx.setColor(m_colorList.get(ci % m_colorList.size()));
              gx.drawRect(xp, yp, 1, 1);
            }
          }
        } else {
          double r;
          for (int noa = 0; noa < m_plotInstances.numInstances(); noa++) {
            if (m_cached[noa] != -9000) {

              r =
                (m_plotInstances.instance(noa).value(m_cIndex) - m_minC)
                  / (m_maxC - m_minC);

              r = (r * 240) + 15;

              gx.setColor(new Color((int) r, 150, (int) (255 - r)));

              xp = m_cached[noa];
              yp = m_heights[noa];
              if (m_plotInstances.attribute(m_attribIndex).isNominal()) {
                xp += (int) (Math.random() * 5) - 2;
              }
              gx.drawRect(xp, yp, 1, 1);
            }
          }
        }
      }
    }
  }

  /**
   * Set the properties for the AttributePanel
   */
  private void setProperties() {
    if (VisualizeUtils.VISUALIZE_PROPERTIES != null) {
      String thisClass = this.getClass().getName();
      String barKey = thisClass + ".barColour";

      String barC = VisualizeUtils.VISUALIZE_PROPERTIES.getProperty(barKey);
      if (barC == null) {
        /*
         * System.err.println("Warning: no configuration property found in "
         * +VisualizeUtils.PROPERTY_FILE +" for "+barKey);
         */
      } else {
        // System.err.println("Setting attribute bar colour to: "+barC);
        m_barColour = VisualizeUtils.processColour(barC, m_barColour);
      }
    }
  }

  /**
   * Apply settings
   * 
   * @param settings the settings to apply
   * @param ownerID the ID of owner perspective, panel. Used when looking up our
   *          settings
   */
  public void applySettings(Settings settings, String ownerID) {
    m_barColour =
      settings.getSetting(ownerID,
        VisualizeUtils.VisualizeDefaults.BAR_BACKGROUND_COLOUR_KEY,
        VisualizeUtils.VisualizeDefaults.BAR_BACKGROUND_COLOUR,
        Environment.getSystemWide());
    repaint();
  }

  public AttributePanel() {
    this(null);
  }

  /**
   * This constructs an attributePanel.
   */
  public AttributePanel(Color background) {
    m_backgroundColor = background;

    setProperties();
    this.setBackground(Color.blue);
    setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_ALWAYS);
    m_colorList = new ArrayList(10);

    for (int noa = m_colorList.size(); noa < 10; noa++) {
      Color pc = m_DefaultColors[noa % 10];
      int ija = noa / 10;
      ija *= 2;
      for (int j = 0; j < ija; j++) {
        pc = pc.darker();
      }

      m_colorList.add(pc);
    }
  }

  /**
   * Add a listener to the list of things listening to this panel
   * 
   * @param a the listener to notify when attribute bars are clicked on
   */
  public void addAttributePanelListener(AttributePanelListener a) {
    m_Listeners.add(a);
  }

  /**
   * Set the index of the attribute by which to colour the data. Updates the
   * number of entries in the colour list if there are more values for this new
   * attribute than previous ones.
   * 
   * @param c the index of the attribute to colour on
   * @param h maximum value of this attribute
   * @param l minimum value of this attribute
   */
  public void setCindex(int c, double h, double l) {
    m_cIndex = c;
    m_maxC = h;
    m_minC = l;

    if (m_span != null) {
      if (m_plotInstances.numAttributes() > 0
        && m_cIndex < m_plotInstances.numAttributes()) {
        if (m_plotInstances.attribute(m_cIndex).isNominal()) {
          if (m_plotInstances.attribute(m_cIndex).numValues() > m_colorList
            .size()) {
            extendColourMap();
          }
        }
      }
      this.repaint();
    }
  }

  /**
   * Set the index of the attribute by which to colour the data. Updates the
   * number of entries in the colour list if there are more values for this new
   * attribute than previous ones.
   * 
   * @param c the index of the attribute to colour on
   */
  public void setCindex(int c) {
    m_cIndex = c;
    /*
     * m_maxC = h; m_minC = l;
     */

    if (m_span != null) {
      if (m_cIndex < m_plotInstances.numAttributes()
        && m_plotInstances.attribute(m_cIndex).isNumeric()) {
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;
        double value;

        for (int i = 0; i < m_plotInstances.numInstances(); i++) {
          if (!m_plotInstances.instance(i).isMissing(m_cIndex)) {
            value = m_plotInstances.instance(i).value(m_cIndex);
            if (value < min) {
              min = value;
            }
            if (value > max) {
              max = value;
            }
          }
        }

        m_minC = min;
        m_maxC = max;
      } else {
        if (m_plotInstances.attribute(m_cIndex).numValues() > m_colorList
          .size()) {
          extendColourMap();
        }
      }

      this.repaint();
    }
  }

  /**
   * Adds more colours to the colour list
   */
  private void extendColourMap() {
    if (m_plotInstances.attribute(m_cIndex).isNominal()) {
      for (int i = m_colorList.size(); i < m_plotInstances.attribute(m_cIndex)
        .numValues(); i++) {
        Color pc = m_DefaultColors[i % 10];
        int ija = i / 10;
        ija *= 2;
        for (int j = 0; j < ija; j++) {
          pc = pc.brighter();
        }

        if (m_backgroundColor != null) {
          pc = Plot2D.checkAgainstBackground(pc, m_backgroundColor);
        }

        m_colorList.add(pc);
      }
    }
  }

  /**
   * Sets a list of colours to use for colouring data points
   * 
   * @param cols a list of java.awt.Color
   */
  public void setColours(ArrayList cols) {
    m_colorList = cols;
  }

  protected void setDefaultColourList(Color[] list) {
    m_DefaultColors = list;
  }

  /**
   * This sets the instances to be drawn into the attribute panel
   * 
   * @param ins The instances.
   */
  public void setInstances(Instances ins) throws Exception {
    if (ins.numAttributes() > 512) {
      throw new Exception("Can't display more than 512 attributes!");
    }

    if (m_span == null) {
      m_span = new JPanel() {
        private static final long serialVersionUID = 7107576557995451922L;

        @Override
        public void paintComponent(Graphics gx) {
          super.paintComponent(gx);
          gx.setColor(Color.red);
          if (m_yIndex != m_xIndex) {
            gx.drawString("X", 5, m_xIndex * 20 + 16);
            gx.drawString("Y", 5, m_yIndex * 20 + 16);
          } else {
            gx.drawString("B", 5, m_xIndex * 20 + 16);
          }
        }
      };
    }

    m_span.removeAll();
    m_plotInstances = ins;
    if (ins.numInstances() > 0 && ins.numAttributes() > 0) {
      JPanel padder = new JPanel();
      JPanel padd2 = new JPanel();

      /*
       * if (m_splitListener != null) { m_plotInstances.randomize(new Random());
       * }
       */

      m_heights = new int[ins.numInstances()];

      m_cIndex = ins.numAttributes() - 1;
      for (int noa = 0; noa < ins.numInstances(); noa++) {
        m_heights[noa] = (int) (Math.random() * 19);
      }
      m_span.setPreferredSize(new Dimension(m_span.getPreferredSize().width,
        (m_cIndex + 1) * 20));
      m_span.setMaximumSize(new Dimension(m_span.getMaximumSize().width,
        (m_cIndex + 1) * 20));
      AttributeSpacing tmp;

      GridBagLayout gb = new GridBagLayout();
      GridBagLayout gb2 = new GridBagLayout();
      GridBagConstraints constraints = new GridBagConstraints();

      padder.setLayout(gb);
      m_span.setLayout(gb2);
      constraints.anchor = GridBagConstraints.CENTER;
      constraints.gridx = 0;
      constraints.gridy = 0;
      constraints.weightx = 5;
      constraints.fill = GridBagConstraints.HORIZONTAL;
      constraints.gridwidth = 1;
      constraints.gridheight = 1;
      constraints.insets = new Insets(0, 0, 0, 0);
      padder.add(m_span, constraints);
      constraints.gridx = 0;
      constraints.gridy = 1;
      constraints.weightx = 5;
      constraints.fill = GridBagConstraints.BOTH;
      constraints.gridwidth = 1;
      constraints.gridheight = 1;
      constraints.weighty = 5;
      constraints.insets = new Insets(0, 0, 0, 0);
      padder.add(padd2, constraints);
      constraints.weighty = 0;
      setViewportView(padder);
      // getViewport().setLayout(null);
      // m_span.setMinimumSize(new Dimension(100, (m_cIndex + 1) * 24));
      // m_span.setSize(100, (m_cIndex + 1) * 24);
      constraints.anchor = GridBagConstraints.CENTER;
      constraints.gridx = 0;
      constraints.gridy = 0;
      constraints.weightx = 5;
      constraints.fill = GridBagConstraints.HORIZONTAL;
      constraints.gridwidth = 1;
      constraints.gridheight = 1;
      constraints.weighty = 5;
      constraints.insets = new Insets(2, 20, 2, 4);

      for (int noa = 0; noa < ins.numAttributes(); noa++) {
        tmp = new AttributeSpacing(ins.attribute(noa), noa);

        constraints.gridy = noa;
        m_span.add(tmp, constraints);
      }
    }
  }

  /**
   * shows which bar is the current x attribute.
   * 
   * @param x The attributes index.
   */
  public void setX(int x) {
    if (m_span != null) {
      m_xIndex = x;
      m_span.repaint();
    }
  }

  /**
   * shows which bar is the current y attribute.
   * 
   * @param y The attributes index.
   */
  public void setY(int y) {
    if (m_span != null) {
      m_yIndex = y;
      m_span.repaint();
    }
  }

  /**
   * Main method for testing this class.
   * 
   * @param args first argument should be an arff file. Second argument can be
   *          an optional class col
   */
  public static void main(String[] args) {
    try {
      if (args.length < 1) {
        System.err.println("Usage : weka.gui.visualize.AttributePanel "
          + " [class col]");
        System.exit(1);
      }
      final javax.swing.JFrame jf =
        new javax.swing.JFrame("Weka Explorer: Attribute");
      jf.setSize(100, 100);
      jf.getContentPane().setLayout(new BorderLayout());
      final AttributePanel p2 = new AttributePanel();
      p2.addAttributePanelListener(new AttributePanelListener() {
        @Override
        public void attributeSelectionChange(AttributePanelEvent e) {
          if (e.m_xChange) {
            System.err.println("X index changed to : " + e.m_indexVal);
          } else {
            System.err.println("Y index changed to : " + e.m_indexVal);
          }
        }
      });
      jf.getContentPane().add(p2, BorderLayout.CENTER);
      jf.addWindowListener(new java.awt.event.WindowAdapter() {
        @Override
        public void windowClosing(java.awt.event.WindowEvent e) {
          jf.dispose();
          System.exit(0);
        }
      });
      if (args.length >= 1) {
        System.err.println("Loading instances from " + args[0]);
        java.io.Reader r =
          new java.io.BufferedReader(new java.io.FileReader(args[0]));
        Instances i = new Instances(r);
        i.setClassIndex(i.numAttributes() - 1);
        p2.setInstances(i);
      }
      if (args.length > 1) {
        p2.setCindex((Integer.parseInt(args[1])) - 1);
      } else {
        p2.setCindex(0);
      }
      jf.setVisible(true);
    } catch (Exception ex) {
      ex.printStackTrace();
      System.err.println(ex.getMessage());
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy