/*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* ClassifierErrorsWeka.java
* Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
*/
package wekaexamples.gui.visualize.plugins;
import weka.core.FastVector;
import weka.core.Instances;
import weka.core.Utils;
import weka.gui.visualize.Plot2D;
import weka.gui.visualize.PlotData2D;
import weka.gui.visualize.VisualizePanel;
import weka.gui.visualize.plugins.ErrorVisualizePlugin;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
/**
* Example class for displaying the classifier errors using Weka panels.
*
* @author fracpete (fracpete at waikato dot ac dot nz)
* @version $Revision$
*/
public class ClassifierErrorsWeka
implements ErrorVisualizePlugin {
/**
* Post processes numeric class errors into shape sizes for plotting
* in the visualize panel.
*
* @param plotSize a FastVector of numeric class errors
*/
protected void postProcessPlotInfo(FastVector plotSize) {
int maxpSize = 20;
double maxErr = Double.NEGATIVE_INFINITY;
double minErr = Double.POSITIVE_INFINITY;
double err;
for (int i = 0; i < plotSize.size(); i++) {
Double errd = (Double)plotSize.elementAt(i);
if (errd != null) {
err = Math.abs(errd.doubleValue());
if (err < minErr) {
minErr = err;
}
if (err > maxErr) {
maxErr = err;
}
}
}
for (int i = 0; i < plotSize.size(); i++) {
Double errd = (Double)plotSize.elementAt(i);
if (errd != null) {
err = Math.abs(errd.doubleValue());
if (maxErr - minErr > 0) {
double temp = (((err - minErr) / (maxErr - minErr))
* maxpSize);
plotSize.setElementAt(new Integer((int)temp), i);
} else {
plotSize.setElementAt(new Integer(1), i);
}
} else {
plotSize.setElementAt(new Integer(1), i);
}
}
}
/**
* Get a JMenu or JMenuItem which contain action listeners
* that perform the visualization of the classifier errors.
* <p/>
* The actual class is the attribute declared as class attribute, the
* predicted class values is found in the attribute prior to the class
* attribute's position. In other words, if the <code>classIndex()</code>
* method returns 10, then the attribute position for the predicted class
* values is 9.
* <p/>
* Exceptions thrown because of changes in Weka since compilation need to
* be caught by the implementer.
*
* @see NoClassDefFoundError
* @see IncompatibleClassChangeError
*
* @param predInst the instances with the actual and predicted class values
* @return menuitem for opening visualization(s), or null
* to indicate no visualization is applicable for the input
*/
public JMenuItem getVisualizeMenuItem(Instances predInst) {
final Instances predInstF = predInst;
JMenuItem result = new JMenuItem("Classifier errors (Weka panels)");
result.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// setup visualize panel
VisualizePanel vp = new VisualizePanel();
vp.setName("Classifier errors for " + predInstF.relationName());
PlotData2D tempd = new PlotData2D(predInstF);
FastVector plotSize = new FastVector();
FastVector plotShape = new FastVector();
for (int i = 0; i < predInstF.numInstances(); i++) {
double actual = predInstF.instance(i).value(predInstF.classIndex());
double predicted = predInstF.instance(i).value(predInstF.classIndex() - 1);
if (predInstF.classAttribute().isNominal()) {
if (Utils.isMissingValue(actual) || Utils.isMissingValue(predicted)) {
plotShape.addElement(new Integer(Plot2D.MISSING_SHAPE));
}
else if (actual != predicted) {
// set to default error point shape
plotShape.addElement(new Integer(Plot2D.ERROR_SHAPE));
}
else {
// otherwise set to constant (automatically assigned) point shape
plotShape.addElement(new Integer(Plot2D.CONST_AUTOMATIC_SHAPE));
}
plotSize.addElement(new Integer(Plot2D.DEFAULT_SHAPE_SIZE));
}
else {
// store the error (to be converted to a point size later)
Double errd = null;
if (Utils.isMissingValue(actual) || Utils.isMissingValue(predicted)) {
// missing shape if actual class not present or prediction is missing
plotShape.addElement(new Integer(Plot2D.MISSING_SHAPE));
}
else {
errd = new Double(predicted - actual);
plotShape.addElement(new Integer(Plot2D.CONST_AUTOMATIC_SHAPE));
}
plotSize.addElement(errd);
}
}
try {
if (predInstF.attribute(predInstF.classIndex()).isNumeric())
postProcessPlotInfo(plotSize);
tempd.setShapeSize(plotSize);
tempd.setShapeType(plotShape);
tempd.setPlotName("Classifier errors for " + predInstF.relationName());
tempd.addInstanceNumberAttribute();
vp.addPlot(tempd);
vp.setColourIndex(predInstF.classIndex()+1);
}
catch (Exception ex) {
ex.printStackTrace();
return;
}
// pre-select class and predicted class
try {
vp.setXIndex(vp.getInstances().classIndex()); // class
vp.setYIndex(vp.getInstances().classIndex() - 1); // predicted class
}
catch (Exception ex) {
// ignored
}
// create and display frame
final JFrame jf = new JFrame("Classifier errors for " + predInstF.relationName());
jf.setSize(600,400);
jf.getContentPane().setLayout(new BorderLayout());
jf.getContentPane().add(vp, BorderLayout.CENTER);
jf.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
jf.dispose();
}
});
jf.setVisible(true);
}
});
return result;
}
/**
* Get the minimum version of Weka, inclusive, the class
* is designed to work with. eg: <code>3.5.0</code>
*
* @return the minimum version
*/
public String getMinVersion() {
return "3.5.9";
}
/**
* Get the maximum version of Weka, exclusive, the class
* is designed to work with. eg: <code>3.6.0</code>
*
* @return the maximum version
*/
public String getMaxVersion() {
return "3.7.0";
}
/**
* Get the specific version of Weka the class is designed for.
* eg: <code>3.5.1</code>
*
* @return the version the plugin was designed for
*/
public String getDesignVersion() {
return "3.6.0";
}
}