weka.gui.visualize.AttributePanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weka-stable Show documentation
Show all versions of weka-stable Show documentation
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.
/*
* 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());
}
}
}