ClassifierPanel.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 52k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    ClassifierPanel.java
  18.  *    Copyright (C) 1999 Len Trigg
  19.  *
  20.  */
  21. package weka.gui.explorer;
  22. import weka.core.Instances;
  23. import weka.core.Instance;
  24. import weka.core.FastVector;
  25. import weka.core.OptionHandler;
  26. import weka.core.Attribute;
  27. import weka.core.Utils;
  28. import weka.core.Drawable;
  29. import weka.core.SerializedObject;
  30. import weka.classifiers.Classifier;
  31. import weka.classifiers.DistributionClassifier;
  32. import weka.classifiers.Evaluation;
  33. import weka.classifiers.CostMatrix;
  34. import weka.classifiers.evaluation.NominalPrediction;
  35. import weka.classifiers.evaluation.MarginCurve;
  36. import weka.classifiers.evaluation.ThresholdCurve;
  37. import weka.classifiers.evaluation.CostCurve;
  38. import weka.filters.Filter;
  39. import weka.gui.Logger;
  40. import weka.gui.TaskLogger;
  41. import weka.gui.SysErrLog;
  42. import weka.gui.GenericObjectEditor;
  43. import weka.gui.PropertyPanel;
  44. import weka.gui.ResultHistoryPanel;
  45. import weka.gui.SetInstancesPanel;
  46. import weka.gui.CostMatrixEditor;
  47. import weka.gui.PropertyDialog;
  48. import weka.gui.InstancesSummaryPanel;
  49. import weka.gui.SaveBuffer;
  50. import weka.gui.visualize.VisualizePanel;
  51. import weka.gui.visualize.PlotData2D;
  52. import weka.gui.visualize.Plot2D;
  53. import weka.gui.treevisualizer.*;
  54. import java.util.Random;
  55. import java.util.Date;
  56. import java.text.SimpleDateFormat;
  57. import java.awt.FlowLayout;
  58. import java.awt.BorderLayout;
  59. import java.awt.GridLayout;
  60. import java.awt.GridBagLayout;
  61. import java.awt.GridBagConstraints;
  62. import java.awt.Insets;
  63. import java.awt.Font;
  64. import java.awt.Point;
  65. import java.awt.event.ActionListener;
  66. import java.awt.event.ActionEvent;
  67. import java.awt.event.InputEvent;
  68. import java.awt.event.MouseAdapter;
  69. import java.awt.event.MouseEvent;
  70. import java.awt.Window;
  71. import java.awt.Dimension;
  72. import java.beans.PropertyChangeListener;
  73. import java.beans.PropertyChangeEvent;
  74. import java.beans.PropertyChangeSupport;
  75. import java.io.File;
  76. import java.io.FileWriter;
  77. import java.io.Writer;
  78. import java.io.BufferedWriter;
  79. import java.io.PrintWriter;
  80. import javax.swing.JFileChooser;
  81. import javax.swing.JPanel;
  82. import javax.swing.JLabel;
  83. import javax.swing.JButton;
  84. import javax.swing.BorderFactory;
  85. import javax.swing.JTextArea;
  86. import javax.swing.JScrollPane;
  87. import javax.swing.JRadioButton;
  88. import javax.swing.ButtonGroup;
  89. import javax.swing.JOptionPane;
  90. import javax.swing.JComboBox;
  91. import javax.swing.DefaultComboBoxModel;
  92. import javax.swing.JTextField;
  93. import javax.swing.SwingConstants;
  94. import javax.swing.JFrame;
  95. import javax.swing.event.ChangeListener;
  96. import javax.swing.event.ChangeEvent;
  97. import javax.swing.JViewport;
  98. import javax.swing.JCheckBox;
  99. import javax.swing.ListSelectionModel;
  100. import javax.swing.event.ListSelectionEvent;
  101. import javax.swing.event.ListSelectionListener;
  102. import javax.swing.JPopupMenu;
  103. import javax.swing.JMenu;
  104. import javax.swing.JMenuItem;
  105. /** 
  106.  * This panel allows the user to select and configure a classifier, set the
  107.  * attribute of the current dataset to be used as the class, and evaluate
  108.  * the classifier using a number of testing modes (test on the training data,
  109.  * train/test on a percentage split, n-fold cross-validation, test on a
  110.  * separate split). The results of classification runs are stored in a result
  111.  * history so that previous results are accessible.
  112.  *
  113.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  114.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  115.  * @version $Revision: 1.45 $
  116.  */
  117. public class ClassifierPanel extends JPanel {
  118.   /** Lets the user configure the classifier */
  119.   protected GenericObjectEditor m_ClassifierEditor =
  120.     new GenericObjectEditor();
  121.   /** The panel showing the current classifier selection */
  122.   protected PropertyPanel m_CEPanel = new PropertyPanel(m_ClassifierEditor);
  123.   
  124.   /** The output area for classification results */
  125.   protected JTextArea m_OutText = new JTextArea(20, 40);
  126.   /** The destination for log/status messages */
  127.   protected Logger m_Log = new SysErrLog();
  128.   /** The buffer saving object for saving output */
  129.   SaveBuffer m_SaveOut = new SaveBuffer(m_Log, this);
  130.   /** A panel controlling results viewing */
  131.   protected ResultHistoryPanel m_History = new ResultHistoryPanel(m_OutText);
  132.   /** Lets the user select the class column */
  133.   protected JComboBox m_ClassCombo = new JComboBox();
  134.   /** Click to set test mode to cross-validation */
  135.   protected JRadioButton m_CVBut = new JRadioButton("Cross-validation");
  136.   /** Click to set test mode to generate a % split */
  137.   protected JRadioButton m_PercentBut = new JRadioButton("Percentage split");
  138.   /** Click to set test mode to test on training data */
  139.   protected JRadioButton m_TrainBut = new JRadioButton("Use training set");
  140.   /** Click to set test mode to a user-specified test set */
  141.   protected JRadioButton m_TestSplitBut =
  142.     new JRadioButton("Supplied test set");
  143.   /** Check to save the predictions in the results list for visualizing
  144.       later on */
  145.   protected JCheckBox m_StorePredictionsBut = 
  146.     new JCheckBox("Store predictions for visualization");
  147.   /** Check to output the model built from the training data */
  148.   protected JCheckBox m_OutputModelBut = new JCheckBox("Output model");
  149.   /** Check to output true/false positives, precision/recall for each class */
  150.   protected JCheckBox m_OutputPerClassBut =
  151.     new JCheckBox("Output per-class stats");
  152.   /** Check to output a confusion matrix */
  153.   protected JCheckBox m_OutputConfusionBut =
  154.     new JCheckBox("Output confusion matrix");
  155.   /** Check to output entropy statistics */
  156.   protected JCheckBox m_OutputEntropyBut =
  157.     new JCheckBox("Output entropy evaluation measures");
  158.   /** Check to evaluate w.r.t a cost matrix */
  159.   protected JCheckBox m_EvalWRTCostsBut =
  160.     new JCheckBox("Cost-sensitive evaluation");
  161.   protected JButton m_SetCostsBut = new JButton("Set...");
  162.   /** Label by where the cv folds are entered */
  163.   protected JLabel m_CVLab = new JLabel("Folds", SwingConstants.RIGHT);
  164.   /** The field where the cv folds are entered */
  165.   protected JTextField m_CVText = new JTextField("10");
  166.   /** Label by where the % split is entered */
  167.   protected JLabel m_PercentLab = new JLabel("%", SwingConstants.RIGHT);
  168.   /** The field where the % split is entered */
  169.   protected JTextField m_PercentText = new JTextField("66");
  170.   /** The button used to open a separate test dataset */
  171.   protected JButton m_SetTestBut = new JButton("Set...");
  172.   /** The frame used to show the test set selection panel */
  173.   protected JFrame m_SetTestFrame;
  174.   /** The frame used to show the cost matrix editing panel */
  175.   protected PropertyDialog m_SetCostsFrame;
  176.   /**
  177.    * Alters the enabled/disabled status of elements associated with each
  178.    * radio button
  179.    */
  180.   ActionListener m_RadioListener = new ActionListener() {
  181.     public void actionPerformed(ActionEvent e) {
  182.       updateRadioLinks();
  183.     }
  184.   };
  185.   /** Button for further output/visualize options */
  186.   JButton m_MoreOptions = new JButton("More options...");
  187.   /** Click to start running the classifier */
  188.   protected JButton m_StartBut = new JButton("Start");
  189.   /** Click to stop a running classifier */
  190.   protected JButton m_StopBut = new JButton("Stop");
  191.   /** Stop the class combo from taking up to much space */
  192.   private Dimension COMBO_SIZE = new Dimension(150, m_StartBut
  193.        .getPreferredSize().height);
  194.   /** The cost matrix editor for evaluation costs */
  195.   protected CostMatrixEditor m_CostMatrixEditor = new CostMatrixEditor();
  196.   /** The main set of instances we're playing with */
  197.   protected Instances m_Instances;
  198.   /** The user-supplied test set (if any) */
  199.   protected Instances m_TestInstances;
  200.   /** The user supplied test set after preprocess filters have been applied */
  201.   protected Instances m_TestInstancesCopy;
  202.   
  203.   /** A thread that classification runs in */
  204.   protected Thread m_RunThread;
  205.   /** default x index for visualizing */
  206.   protected int m_visXIndex;
  207.   
  208.   /** default y index for visualizing */
  209.   protected int m_visYIndex;
  210.   /** The current visualization object */
  211.   protected VisualizePanel m_CurrentVis = null;
  212.   /** The preprocess panel through which filters can be applied to
  213.       user supplied test data sets */
  214.   protected PreprocessPanel m_Preprocess = null;
  215.   /** The instances summary panel displayed by m_SetTestFrame */
  216.   protected InstancesSummaryPanel m_Summary = null;
  217.   /* Register the property editors we need */
  218.   static {
  219.     java.beans.PropertyEditorManager
  220.       .registerEditor(weka.core.SelectedTag.class,
  221.       weka.gui.SelectedTagEditor.class);
  222.     java.beans.PropertyEditorManager
  223.       .registerEditor(weka.filters.Filter.class,
  224.       weka.gui.GenericObjectEditor.class);
  225.     java.beans.PropertyEditorManager
  226.       .registerEditor(weka.classifiers.Classifier [].class,
  227.       weka.gui.GenericArrayEditor.class);
  228.     java.beans.PropertyEditorManager
  229.       .registerEditor(weka.classifiers.DistributionClassifier.class,
  230.       weka.gui.GenericObjectEditor.class);
  231.     java.beans.PropertyEditorManager
  232.       .registerEditor(weka.classifiers.Classifier.class,
  233.       weka.gui.GenericObjectEditor.class);
  234.     java.beans.PropertyEditorManager
  235.       .registerEditor(weka.classifiers.CostMatrix.class,
  236.       weka.gui.CostMatrixEditor.class);
  237.   }
  238.   
  239.   /**
  240.    * Creates the classifier panel
  241.    */
  242.   public ClassifierPanel() {
  243.     // Connect / configure the components
  244.     m_OutText.setEditable(false);
  245.     m_OutText.setFont(new Font("Monospaced", Font.PLAIN, 12));
  246.     m_OutText.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
  247.     m_OutText.addMouseListener(new MouseAdapter() {
  248.       public void mouseClicked(MouseEvent e) {
  249. if ((e.getModifiers() & InputEvent.BUTTON1_MASK)
  250.     != InputEvent.BUTTON1_MASK) {
  251.   m_OutText.selectAll();
  252. }
  253.       }
  254.     });
  255.     m_History.setBorder(BorderFactory.createTitledBorder("Result list"));
  256.     m_ClassifierEditor.setClassType(Classifier.class);
  257.     m_ClassifierEditor.setValue(new weka.classifiers.ZeroR());
  258.     m_ClassifierEditor.addPropertyChangeListener(new PropertyChangeListener() {
  259.       public void propertyChange(PropertyChangeEvent e) {
  260. repaint();
  261.       }
  262.     });
  263.     m_ClassCombo.setToolTipText("Select the attribute to use as the class");
  264.     m_TrainBut.setToolTipText("Test on the same set that the classifier"
  265.       + " is trained on");
  266.     m_CVBut.setToolTipText("Perform a n-fold cross-validation");
  267.     m_PercentBut.setToolTipText("Train on a percentage of the data and"
  268. + " test on the remainder");
  269.     m_TestSplitBut.setToolTipText("Test on a user-specified dataset");
  270.     m_StartBut.setToolTipText("Starts the classification");
  271.     m_StopBut.setToolTipText("Stops a running classification");
  272.     m_StorePredictionsBut.
  273.       setToolTipText("Store predictions in the result list for later "
  274.      +"visualization");
  275.     m_OutputModelBut
  276.       .setToolTipText("Output the model obtained from the full training set");
  277.     m_OutputPerClassBut.setToolTipText("Output precision/recall & true/false"
  278.     + " positives for each class");
  279.     m_OutputConfusionBut
  280.       .setToolTipText("Output the matrix displaying class confusions");
  281.     m_OutputEntropyBut
  282.       .setToolTipText("Output entropy-based evaluation measures");
  283.     m_EvalWRTCostsBut
  284.       .setToolTipText("Evaluate errors with respect to a cost matrix");
  285.     m_StorePredictionsBut.setSelected(true);
  286.     m_OutputModelBut.setSelected(true);
  287.     m_OutputPerClassBut.setSelected(true);
  288.     m_OutputConfusionBut.setSelected(true);
  289.     m_ClassCombo.setEnabled(false);
  290.     m_ClassCombo.setPreferredSize(COMBO_SIZE);
  291.     m_ClassCombo.setMaximumSize(COMBO_SIZE);
  292.     m_ClassCombo.setMinimumSize(COMBO_SIZE);
  293.     m_CVBut.setSelected(true);
  294.     updateRadioLinks();
  295.     ButtonGroup bg = new ButtonGroup();
  296.     bg.add(m_TrainBut);
  297.     bg.add(m_CVBut);
  298.     bg.add(m_PercentBut);
  299.     bg.add(m_TestSplitBut);
  300.     m_TrainBut.addActionListener(m_RadioListener);
  301.     m_CVBut.addActionListener(m_RadioListener);
  302.     m_PercentBut.addActionListener(m_RadioListener);
  303.     m_TestSplitBut.addActionListener(m_RadioListener);
  304.     m_SetTestBut.addActionListener(new ActionListener() {
  305.       public void actionPerformed(ActionEvent e) {
  306. setTestSet();
  307.       }
  308.     });
  309.     m_EvalWRTCostsBut.addActionListener(new ActionListener() {
  310.       public void actionPerformed(ActionEvent e) {
  311. m_SetCostsBut.setEnabled(m_EvalWRTCostsBut.isSelected());
  312. if ((m_SetCostsFrame != null) 
  313.     && (!m_EvalWRTCostsBut.isSelected())) {
  314.   m_SetCostsFrame.setVisible(false);
  315. }
  316.       }
  317.     });
  318.     m_CostMatrixEditor.setValue(new CostMatrix(1));
  319.     m_SetCostsBut.setEnabled(m_EvalWRTCostsBut.isSelected());
  320.     m_SetCostsBut.addActionListener(new ActionListener() {
  321.       public void actionPerformed(ActionEvent e) {
  322. m_SetCostsBut.setEnabled(false);
  323. if (m_SetCostsFrame == null) {
  324.   m_SetCostsFrame = new PropertyDialog(m_CostMatrixEditor, 100, 100);
  325.   // pd.setSize(250,150);
  326.   m_SetCostsFrame.addWindowListener(new java.awt.event.WindowAdapter() {
  327.     public void windowClosing(java.awt.event.WindowEvent p) {
  328.       m_SetCostsBut.setEnabled(m_EvalWRTCostsBut.isSelected());
  329.       if ((m_SetCostsFrame != null) 
  330.   && (!m_EvalWRTCostsBut.isSelected())) {
  331. m_SetCostsFrame.setVisible(false);
  332.       }
  333.     }
  334.   });
  335. }
  336. m_SetCostsFrame.setVisible(true);
  337.       }
  338.     });
  339.     m_StartBut.setEnabled(false);
  340.     m_StopBut.setEnabled(false);
  341.     m_StartBut.addActionListener(new ActionListener() {
  342.       public void actionPerformed(ActionEvent e) {
  343. startClassifier();
  344.       }
  345.     });
  346.     m_StopBut.addActionListener(new ActionListener() {
  347.       public void actionPerformed(ActionEvent e) {
  348. stopClassifier();
  349.       }
  350.     });
  351.    
  352.     m_ClassCombo.addActionListener(new ActionListener() {
  353.       public void actionPerformed(ActionEvent e) {
  354. int selected = m_ClassCombo.getSelectedIndex();
  355. if (selected != -1) {
  356.   boolean isNominal = m_Instances.attribute(selected).isNominal();
  357.   m_OutputPerClassBut.setEnabled(isNominal);
  358.   m_OutputConfusionBut.setEnabled(isNominal);
  359. }
  360.       }
  361.     });
  362.     m_History.setHandleRightClicks(false);
  363.     // see if we can popup a menu for the selected result
  364.     m_History.getList().addMouseListener(new MouseAdapter() {
  365. public void mouseClicked(MouseEvent e) {
  366.   if ((e.getModifiers() & InputEvent.BUTTON1_MASK)
  367.       == InputEvent.BUTTON1_MASK) {
  368.   } else {
  369.     int index = m_History.getList().locationToIndex(e.getPoint());
  370.     if (index != -1) {
  371.       String name = m_History.getNameAtIndex(index);
  372.       visualize(name, e.getX(), e.getY());
  373.     }
  374.   }
  375. }
  376.       });
  377.     m_MoreOptions.addActionListener(new ActionListener() {
  378.       public void actionPerformed(ActionEvent e) {
  379. m_MoreOptions.setEnabled(false);
  380. JPanel moreOptionsPanel = new JPanel();
  381. moreOptionsPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
  382. moreOptionsPanel.setLayout(new GridLayout(6, 1));
  383. moreOptionsPanel.add(m_OutputModelBut);
  384. moreOptionsPanel.add(m_OutputPerClassBut);   
  385. moreOptionsPanel.add(m_OutputEntropyBut);   
  386. moreOptionsPanel.add(m_OutputConfusionBut);   
  387. moreOptionsPanel.add(m_StorePredictionsBut);
  388. JPanel costMatrixOption = new JPanel();
  389. costMatrixOption.setLayout(new BorderLayout());
  390. costMatrixOption.add(m_EvalWRTCostsBut, BorderLayout.WEST);
  391. costMatrixOption.add(m_SetCostsBut, BorderLayout.EAST);
  392. moreOptionsPanel.add(costMatrixOption);
  393. JPanel all = new JPanel();
  394. all.setLayout(new BorderLayout());
  395. JButton oK = new JButton("OK");
  396. JPanel okP = new JPanel();
  397. okP.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
  398. okP.setLayout(new GridLayout(1,1,5,5));
  399. okP.add(oK);
  400. all.add(moreOptionsPanel, BorderLayout.CENTER);
  401. all.add(okP, BorderLayout.SOUTH);
  402. final javax.swing.JFrame jf = 
  403.   new javax.swing.JFrame("Classifier evaluation options");
  404. jf.getContentPane().setLayout(new BorderLayout());
  405. jf.getContentPane().add(all, BorderLayout.CENTER);
  406. jf.addWindowListener(new java.awt.event.WindowAdapter() {
  407.   public void windowClosing(java.awt.event.WindowEvent w) {
  408.     jf.dispose();
  409.     m_MoreOptions.setEnabled(true);
  410.   }
  411. });
  412. oK.addActionListener(new ActionListener() {
  413.   public void actionPerformed(ActionEvent a) {
  414.     m_MoreOptions.setEnabled(true);
  415.     jf.dispose();
  416.   }
  417. });
  418. jf.pack();
  419. jf.setLocation(m_MoreOptions.getLocationOnScreen());
  420. jf.setVisible(true);
  421.       }
  422.     });
  423.     // Layout the GUI
  424.     JPanel p1 = new JPanel();
  425.     p1.setBorder(BorderFactory.createCompoundBorder(
  426.  BorderFactory.createTitledBorder("Classifier"),
  427.  BorderFactory.createEmptyBorder(0, 5, 5, 5)
  428.  ));
  429.     p1.setLayout(new BorderLayout());
  430.     p1.add(m_CEPanel, BorderLayout.NORTH);
  431.     JPanel p2 = new JPanel();
  432.     GridBagLayout gbL = new GridBagLayout();
  433.     p2.setLayout(gbL);
  434.     p2.setBorder(BorderFactory.createCompoundBorder(
  435.  BorderFactory.createTitledBorder("Test options"),
  436.  BorderFactory.createEmptyBorder(0, 5, 5, 5)
  437.  ));
  438.     GridBagConstraints gbC = new GridBagConstraints();
  439.     gbC.anchor = GridBagConstraints.WEST;
  440.     gbC.gridy = 0;     gbC.gridx = 0;
  441.     gbL.setConstraints(m_TrainBut, gbC);
  442.     p2.add(m_TrainBut);
  443.     gbC = new GridBagConstraints();
  444.     gbC.anchor = GridBagConstraints.WEST;
  445.     gbC.gridy = 1;     gbC.gridx = 0;
  446.     gbL.setConstraints(m_TestSplitBut, gbC);
  447.     p2.add(m_TestSplitBut);
  448.     gbC = new GridBagConstraints();
  449.     gbC.anchor = GridBagConstraints.EAST;
  450.     gbC.fill = GridBagConstraints.HORIZONTAL;
  451.     gbC.gridy = 1;     gbC.gridx = 1;    gbC.gridwidth = 2;
  452.     gbC.insets = new Insets(2, 10, 2, 0);
  453.     gbL.setConstraints(m_SetTestBut, gbC);
  454.     p2.add(m_SetTestBut);
  455.     gbC = new GridBagConstraints();
  456.     gbC.anchor = GridBagConstraints.WEST;
  457.     gbC.gridy = 2;     gbC.gridx = 0;
  458.     gbL.setConstraints(m_CVBut, gbC);
  459.     p2.add(m_CVBut);
  460.     gbC = new GridBagConstraints();
  461.     gbC.anchor = GridBagConstraints.EAST;
  462.     gbC.fill = GridBagConstraints.HORIZONTAL;
  463.     gbC.gridy = 2;     gbC.gridx = 1;
  464.     gbC.insets = new Insets(2, 10, 2, 10);
  465.     gbL.setConstraints(m_CVLab, gbC);
  466.     p2.add(m_CVLab);
  467.     gbC = new GridBagConstraints();
  468.     gbC.anchor = GridBagConstraints.EAST;
  469.     gbC.fill = GridBagConstraints.HORIZONTAL;
  470.     gbC.gridy = 2;     gbC.gridx = 2;  gbC.weightx = 100;
  471.     gbC.ipadx = 20;
  472.     gbL.setConstraints(m_CVText, gbC);
  473.     p2.add(m_CVText);
  474.     gbC = new GridBagConstraints();
  475.     gbC.anchor = GridBagConstraints.WEST;
  476.     gbC.gridy = 3;     gbC.gridx = 0;
  477.     gbL.setConstraints(m_PercentBut, gbC);
  478.     p2.add(m_PercentBut);
  479.     gbC = new GridBagConstraints();
  480.     gbC.anchor = GridBagConstraints.EAST;
  481.     gbC.fill = GridBagConstraints.HORIZONTAL;
  482.     gbC.gridy = 3;     gbC.gridx = 1;
  483.     gbC.insets = new Insets(2, 10, 2, 10);
  484.     gbL.setConstraints(m_PercentLab, gbC);
  485.     p2.add(m_PercentLab);
  486.     gbC = new GridBagConstraints();
  487.     gbC.anchor = GridBagConstraints.EAST;
  488.     gbC.fill = GridBagConstraints.HORIZONTAL;
  489.     gbC.gridy = 3;     gbC.gridx = 2;  gbC.weightx = 100;
  490.     gbC.ipadx = 20;
  491.     gbL.setConstraints(m_PercentText, gbC);
  492.     p2.add(m_PercentText);
  493.     gbC = new GridBagConstraints();
  494.     gbC.anchor = GridBagConstraints.WEST;
  495.     gbC.fill = GridBagConstraints.HORIZONTAL;
  496.     gbC.gridy = 4;     gbC.gridx = 0;  gbC.weightx = 100;
  497.     gbC.gridwidth = 3;
  498.     gbC.insets = new Insets(3, 0, 1, 0);
  499.     gbL.setConstraints(m_MoreOptions, gbC);
  500.     p2.add(m_MoreOptions);
  501.     JPanel buttons = new JPanel();
  502.     buttons.setLayout(new GridLayout(2, 2));
  503.     buttons.add(m_ClassCombo);
  504.     m_ClassCombo.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
  505.     JPanel ssButs = new JPanel();
  506.     ssButs.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
  507.     ssButs.setLayout(new GridLayout(1, 2, 5, 5));
  508.     ssButs.add(m_StartBut);
  509.     ssButs.add(m_StopBut);
  510.     buttons.add(ssButs);
  511.     
  512.     JPanel p3 = new JPanel();
  513.     p3.setBorder(BorderFactory.createTitledBorder("Classifier output"));
  514.     p3.setLayout(new BorderLayout());
  515.     final JScrollPane js = new JScrollPane(m_OutText);
  516.     p3.add(js, BorderLayout.CENTER);
  517.     js.getViewport().addChangeListener(new ChangeListener() {
  518.       private int lastHeight;
  519.       public void stateChanged(ChangeEvent e) {
  520. JViewport vp = (JViewport)e.getSource();
  521. int h = vp.getViewSize().height; 
  522. if (h != lastHeight) { // i.e. an addition not just a user scrolling
  523.   lastHeight = h;
  524.   int x = h - vp.getExtentSize().height;
  525.   vp.setViewPosition(new Point(0, x));
  526. }
  527.       }
  528.     });
  529.     
  530.     JPanel mondo = new JPanel();
  531.     gbL = new GridBagLayout();
  532.     mondo.setLayout(gbL);
  533.     gbC = new GridBagConstraints();
  534.     //    gbC.anchor = GridBagConstraints.WEST;
  535.     gbC.fill = GridBagConstraints.HORIZONTAL;
  536.     gbC.gridy = 0;     gbC.gridx = 0;
  537.     gbL.setConstraints(p2, gbC);
  538.     mondo.add(p2);
  539.     gbC = new GridBagConstraints();
  540.     gbC.anchor = GridBagConstraints.NORTH;
  541.     gbC.fill = GridBagConstraints.HORIZONTAL;
  542.     gbC.gridy = 1;     gbC.gridx = 0;
  543.     gbL.setConstraints(buttons, gbC);
  544.     mondo.add(buttons);
  545.     gbC = new GridBagConstraints();
  546.     //gbC.anchor = GridBagConstraints.NORTH;
  547.     gbC.fill = GridBagConstraints.BOTH;
  548.     gbC.gridy = 2;     gbC.gridx = 0; gbC.weightx = 0;
  549.     gbL.setConstraints(m_History, gbC);
  550.     mondo.add(m_History);
  551.     gbC = new GridBagConstraints();
  552.     gbC.fill = GridBagConstraints.BOTH;
  553.     gbC.gridy = 0;     gbC.gridx = 1;
  554.     gbC.gridheight = 3;
  555.     gbC.weightx = 100; gbC.weighty = 100;
  556.     gbL.setConstraints(p3, gbC);
  557.     mondo.add(p3);
  558.     setLayout(new BorderLayout());
  559.     add(p1, BorderLayout.NORTH);
  560.     add(mondo, BorderLayout.CENTER);
  561.   }
  562.   
  563.   /**
  564.    * Updates the enabled status of the input fields and labels.
  565.    */
  566.   protected void updateRadioLinks() {
  567.     
  568.     m_SetTestBut.setEnabled(m_TestSplitBut.isSelected());
  569.     if ((m_SetTestFrame != null) && (!m_TestSplitBut.isSelected())) {
  570.       m_SetTestFrame.setVisible(false);
  571.     }
  572.     m_CVText.setEnabled(m_CVBut.isSelected());
  573.     m_CVLab.setEnabled(m_CVBut.isSelected());
  574.     m_PercentText.setEnabled(m_PercentBut.isSelected());
  575.     m_PercentLab.setEnabled(m_PercentBut.isSelected());
  576.   }
  577.   /**
  578.    * Sets the preprocess panel through which user selected
  579.    * filters can be applied to any supplied test data
  580.    * @param p the preprocess panel to use
  581.    */
  582.   public void setPreprocess(PreprocessPanel p) {
  583.     m_Preprocess = p;
  584.   }
  585.   /**
  586.    * Sets the Logger to receive informational messages
  587.    *
  588.    * @param newLog the Logger that will now get info messages
  589.    */
  590.   public void setLog(Logger newLog) {
  591.     m_Log = newLog;
  592.   }
  593.   
  594.   /**
  595.    * Set the default attributes to use on the x and y axis
  596.    * of a new visualization object.
  597.    * @param x the index of the attribute to use on the x axis
  598.    * @param y the index of the attribute to use on the y axis
  599.    */
  600.   public void setXY_VisualizeIndexes(int x, int y) {
  601.     m_visXIndex = x;
  602.     m_visYIndex = y;
  603.   }
  604.   /**
  605.    * Tells the panel to use a new set of instances.
  606.    *
  607.    * @param inst a set of Instances
  608.    */
  609.   public void setInstances(Instances inst) {
  610.     m_Instances = inst;
  611.     setXY_VisualizeIndexes(0,0); // reset the default x and y indexes
  612.     String [] attribNames = new String [m_Instances.numAttributes()];
  613.     for (int i = 0; i < attribNames.length; i++) {
  614.       String type = "";
  615.       switch (m_Instances.attribute(i).type()) {
  616.       case Attribute.NOMINAL:
  617. type = "(Nom) ";
  618. break;
  619.       case Attribute.NUMERIC:
  620. type = "(Num) ";
  621. break;
  622.       case Attribute.STRING:
  623. type = "(Str) ";
  624. break;
  625.       default:
  626. type = "(???) ";
  627.       }
  628.       attribNames[i] = type + m_Instances.attribute(i).name();
  629.     }
  630.     m_ClassCombo.setModel(new DefaultComboBoxModel(attribNames));
  631.     if (attribNames.length > 0) {
  632.       m_ClassCombo.setSelectedIndex(attribNames.length - 1);
  633.       m_ClassCombo.setEnabled(true);
  634.       m_StartBut.setEnabled(m_RunThread == null);
  635.       m_StopBut.setEnabled(m_RunThread != null);
  636.     } else {
  637.       m_StartBut.setEnabled(false);
  638.       m_StopBut.setEnabled(false);
  639.     }
  640.   }
  641.   /**
  642.    * Attempts to filter the user specified test set through
  643.    * the most currently used set of filters (if any) from the
  644.    * pre-process panel.
  645.    */
  646.   protected void filterUserTestInstances() {
  647.     if (m_Preprocess != null && m_TestInstances != null) {
  648.       m_TestInstancesCopy = new Instances(m_TestInstances);
  649.       SerializedObject sf = m_Preprocess.getMostRecentFilters();
  650.       if (sf != null) {
  651. Filter [] filters = null;
  652. try {
  653.   filters = (Filter [])sf.getObject();
  654. } catch (Exception ex) {
  655.   JOptionPane.showMessageDialog(this,
  656. "Could not deserialize filters",
  657. null,
  658. JOptionPane.ERROR_MESSAGE);
  659. }
  660. if (filters.length != 0) {
  661.   try {
  662.     m_Log.statusMessage("Applying preprocess filters to test data...");
  663.     m_TestInstancesCopy = new Instances(m_TestInstances);
  664.     for (int i = 0; i < filters.length; i++) {
  665.       m_Log.statusMessage("Passing through filter " + (i + 1) + ": "
  666.   + filters[i].getClass().getName());
  667.       filters[i].setInputFormat(m_TestInstancesCopy);
  668.       m_TestInstancesCopy = Filter.useFilter(m_TestInstancesCopy, 
  669.      filters[i]);
  670.     }
  671.     m_Log.statusMessage("OK");
  672.   } catch (Exception ex) {
  673.     m_Log.statusMessage("See error log");
  674.     m_Log.logMessage("Problem applying filters to test data "
  675.      +"(Classifier Panel)");
  676.   }
  677.   if (m_Summary != null && m_TestInstancesCopy != null) {
  678.     m_Summary.setInstances(m_TestInstancesCopy);
  679.   }
  680. }
  681.       }
  682.     }
  683.   }
  684.   /**
  685.    * Sets the user test set. Information about the current test set
  686.    * is displayed in an InstanceSummaryPanel and the user is given the
  687.    * ability to load another set from a file or url.
  688.    *
  689.    */
  690.   protected void setTestSet() {
  691.     if (m_SetTestFrame == null) {
  692.       final SetInstancesPanel sp = new SetInstancesPanel();
  693.       m_Summary = sp.getSummary();
  694.       if (m_TestInstancesCopy != null) {
  695. sp.setInstances(m_TestInstancesCopy);
  696.       }
  697.       sp.addPropertyChangeListener(new PropertyChangeListener() {
  698. public void propertyChange(PropertyChangeEvent e) {
  699.   m_TestInstances = sp.getInstances();
  700.   filterUserTestInstances();
  701. }
  702.       });
  703.       // Add propertychangelistener to update m_TestInstances whenever
  704.       // it changes in the settestframe
  705.       m_SetTestFrame = new JFrame("Test Instances");
  706.       m_SetTestFrame.getContentPane().setLayout(new BorderLayout());
  707.       m_SetTestFrame.getContentPane().add(sp, BorderLayout.CENTER);
  708.       m_SetTestFrame.pack();
  709.     }
  710.     m_SetTestFrame.setVisible(true);
  711.   }
  712.   /**
  713.    * Process a classifier's prediction for an instance and update a
  714.    * set of plotting instances and additional plotting info. plotInfo
  715.    * for nominal class datasets holds shape types (actual data points have
  716.    * automatic shape type assignment; classifer error data points have
  717.    * box shape type). For numeric class datasets, the actual data points
  718.    * are stored in plotInstances and plotInfo stores the error (which is
  719.    * later converted to shape size values)
  720.    * @param toPredict the actual data point
  721.    * @param classifier the classifier
  722.    * @param eval the evaluation object to use for evaluating the classifer on
  723.    * the instance to predict
  724.    * @param predictions a fastvector to add the prediction to
  725.    * @param plotInstances a set of plottable instances
  726.    * @param plotShape additional plotting information (shape)
  727.    * @param plotSize additional plotting information (size)
  728.    */
  729.   private void processClassifierPrediction(Instance toPredict,
  730.    Classifier classifier,
  731.    Evaluation eval,
  732.    FastVector predictions,
  733.    Instances plotInstances,
  734.    FastVector plotShape,
  735.    FastVector plotSize) {
  736.     try {
  737.       double pred;
  738.       // classifier is a distribution classifer and class is nominal
  739.       if (predictions != null) {
  740. DistributionClassifier dc = 
  741.   (DistributionClassifier)classifier;
  742. double [] dist = 
  743.   dc.distributionForInstance(toPredict);
  744. pred = eval.evaluateModelOnce(dist, toPredict);
  745. int actual = (int)toPredict.classValue();
  746. predictions.addElement(new 
  747.   NominalPrediction(actual, dist, toPredict.weight()));
  748.       } else {
  749. pred = eval.evaluateModelOnce(classifier, 
  750.       toPredict);
  751.       }
  752.       double [] values = new double[plotInstances.numAttributes()];
  753.       for (int i = 0; i < plotInstances.numAttributes(); i++) {
  754. if (i < toPredict.classIndex()) {
  755.   values[i] = toPredict.value(i);
  756. } else if (i == toPredict.classIndex()) {
  757.   values[i] = pred;
  758.   values[i+1] = toPredict.value(i);
  759.   /* // if the class value of the instances to predict is missing then
  760.   // set it to the predicted value
  761.   if (toPredict.isMissing(i)) {
  762.     values[i+1] = pred;
  763.     } */
  764.   i++;
  765. } else {
  766.   values[i] = toPredict.value(i-1);
  767. }
  768.       }
  769.       plotInstances.add(new Instance(1.0, values));
  770.       if (toPredict.classAttribute().isNominal()) {
  771. if (toPredict.isMissing(toPredict.classIndex())) {
  772.   plotShape.addElement(new Integer(Plot2D.MISSING_SHAPE));
  773. } else if (pred != toPredict.classValue()) {
  774.   // set to default error point shape
  775.   plotShape.addElement(new Integer(Plot2D.ERROR_SHAPE));
  776. } else {
  777.   // otherwise set to constant (automatically assigned) point shape
  778.   plotShape.addElement(new Integer(Plot2D.CONST_AUTOMATIC_SHAPE));
  779. }
  780. plotSize.addElement(new Integer(Plot2D.DEFAULT_SHAPE_SIZE));
  781.       } else {
  782. // store the error (to be converted to a point size later)
  783. Double errd = null;
  784. if (!toPredict.isMissing(toPredict.classIndex())) {
  785.   errd = new Double(pred - toPredict.classValue());
  786.   plotShape.addElement(new Integer(Plot2D.CONST_AUTOMATIC_SHAPE));
  787. } else {
  788.   // missing shape if actual class not present
  789.   plotShape.addElement(new Integer(Plot2D.MISSING_SHAPE));
  790. }
  791. plotSize.addElement(errd);
  792.       }
  793.     } catch (Exception ex) {
  794.       ex.printStackTrace();
  795.     }
  796.   }
  797.   /**
  798.    * Post processes numeric class errors into shape sizes for plotting
  799.    * in the visualize panel
  800.    * @param plotSize a FastVector of numeric class errors
  801.    */
  802.   private void postProcessPlotInfo(FastVector plotSize) {
  803.     int maxpSize = 20;
  804.     double maxErr = Double.NEGATIVE_INFINITY;
  805.     double minErr = Double.POSITIVE_INFINITY;
  806.     double err;
  807.     
  808.     for (int i = 0; i < plotSize.size(); i++) {
  809.       Double errd = (Double)plotSize.elementAt(i);
  810.       if (errd != null) {
  811. err = Math.abs(errd.doubleValue());
  812.         if (err < minErr) {
  813.   minErr = err;
  814. }
  815. if (err > maxErr) {
  816.   maxErr = err;
  817. }
  818.       }
  819.     }
  820.     
  821.     for (int i = 0; i < plotSize.size(); i++) {
  822.       Double errd = (Double)plotSize.elementAt(i);
  823.       if (errd != null) {
  824. err = Math.abs(errd.doubleValue());
  825. if (maxErr - minErr > 0) {
  826.   double temp = (((err - minErr) / (maxErr - minErr)) 
  827.  * maxpSize);
  828.   plotSize.setElementAt(new Integer((int)temp), i);
  829. } else {
  830.   plotSize.setElementAt(new Integer(1), i);
  831. }
  832.       } else {
  833. plotSize.setElementAt(new Integer(1), i);
  834.       }
  835.     }
  836.   }
  837.   /**
  838.    * Sets up the structure for the visualizable instances. This dataset
  839.    * contains the original attributes plus the classifier's predictions
  840.    * for the class as an attribute called "predicted+WhateverTheClassIsCalled".
  841.    * @param trainInstancs the instances that the classifier is trained on
  842.    * @return a new set of instances containing one more attribute (predicted
  843.    * class) than the trainInstances
  844.    */
  845.   private Instances setUpVisualizableInstances(Instances trainInstances) {
  846.     FastVector hv = new FastVector();
  847.     Attribute predictedClass;
  848.     Attribute classAt = trainInstances.attribute(trainInstances.classIndex());
  849.     if (classAt.isNominal()) {
  850.       FastVector attVals = new FastVector();
  851.       for (int i = 0; i < classAt.numValues(); i++) {
  852. attVals.addElement(classAt.value(i));
  853.       }
  854.       predictedClass = new Attribute("predicted"+classAt.name(), attVals);
  855.     } else {
  856.       predictedClass = new Attribute("predicted"+classAt.name());
  857.     }
  858.     for (int i = 0; i < trainInstances.numAttributes(); i++) {
  859.       if (i == trainInstances.classIndex()) {
  860. hv.addElement(predictedClass);
  861.       }
  862.       hv.addElement(trainInstances.attribute(i).copy());
  863.     }
  864.     return new Instances(trainInstances.relationName()+"_predicted", hv, 
  865.  trainInstances.numInstances());
  866.   }
  867.   /**
  868.    * Starts running the currently configured classifier with the current
  869.    * settings. This is run in a separate thread, and will only start if
  870.    * there is no classifier already running. The classifier output is sent
  871.    * to the results history panel.
  872.    */
  873.   protected void startClassifier() {
  874.     if (m_RunThread == null) {
  875.       m_StartBut.setEnabled(false);
  876.       m_StopBut.setEnabled(true);
  877.       m_RunThread = new Thread() {
  878. public void run() {
  879.   // Copy the current state of things
  880.   m_Log.statusMessage("Setting up...");
  881.   CostMatrix costMatrix = null;
  882.   Instances inst = new Instances(m_Instances);
  883.   Instances userTest = null;
  884.   // additional vis info (either shape type or point size)
  885.   FastVector plotShape = new FastVector();
  886.   FastVector plotSize = new FastVector();
  887.   Instances predInstances = null;
  888.   // will hold the prediction objects if the class is nominal
  889.   FastVector predictions = null;
  890.  
  891.   // for timing
  892.   long trainTimeStart = 0, trainTimeElapsed = 0;
  893.   if (m_TestInstances != null) {
  894.     userTest = new Instances(m_TestInstancesCopy);
  895.   }
  896.   if (m_EvalWRTCostsBut.isSelected()) {
  897.     costMatrix = new CostMatrix((CostMatrix) m_CostMatrixEditor
  898. .getValue());
  899.   }
  900.   boolean outputModel = m_OutputModelBut.isSelected();
  901.   boolean outputConfusion = m_OutputConfusionBut.isSelected();
  902.   boolean outputPerClass = m_OutputPerClassBut.isSelected();
  903.   boolean outputSummary = true;
  904.           boolean outputEntropy = m_OutputEntropyBut.isSelected();
  905.   boolean saveVis = m_StorePredictionsBut.isSelected();
  906.   String grph = null;
  907.   int testMode = 0;
  908.   int numFolds = 10, percent = 66;
  909.   int classIndex = m_ClassCombo.getSelectedIndex();
  910.   Classifier classifier = (Classifier) m_ClassifierEditor.getValue();
  911.   StringBuffer outBuff = new StringBuffer();
  912.   String name = (new SimpleDateFormat("HH:mm:ss - "))
  913.   .format(new Date());
  914.   String cname = classifier.getClass().getName();
  915.   if (cname.startsWith("weka.classifiers.")) {
  916.     name += cname.substring("weka.classifiers.".length());
  917.   } else {
  918.     name += cname;
  919.   }
  920.   try {
  921.     if (m_CVBut.isSelected()) {
  922.       testMode = 1;
  923.       numFolds = Integer.parseInt(m_CVText.getText());
  924.       if (numFolds <= 1) {
  925. throw new Exception("Number of folds must be greater than 1");
  926.       }
  927.     } else if (m_PercentBut.isSelected()) {
  928.       testMode = 2;
  929.       percent = Integer.parseInt(m_PercentText.getText());
  930.       if ((percent <= 0) || (percent >= 100)) {
  931. throw new Exception("Percentage must be between 0 and 100");
  932.       }
  933.     } else if (m_TrainBut.isSelected()) {
  934.       testMode = 3;
  935.     } else if (m_TestSplitBut.isSelected()) {
  936.       testMode = 4;
  937.       // Check the test instance compatibility
  938.       if (userTest == null) {
  939. throw new Exception("No user test set has been opened");
  940.       }
  941.       if (!inst.equalHeaders(userTest)) {
  942. throw new Exception("Train and test set are not compatible");
  943.       }
  944.       userTest.setClassIndex(classIndex);
  945.     } else {
  946.       throw new Exception("Unknown test mode");
  947.     }
  948.     inst.setClassIndex(classIndex);
  949.     // set up the structure of the plottable instances for 
  950.     // visualization
  951.     predInstances = setUpVisualizableInstances(inst);
  952.     predInstances.setClassIndex(inst.classIndex()+1);
  953.     
  954.     if (inst.classAttribute().isNominal() && 
  955. classifier instanceof DistributionClassifier) {
  956.       predictions = new FastVector();
  957.     }
  958.     // Output some header information
  959.     m_Log.logMessage("Started " + cname);
  960.     if (m_Log instanceof TaskLogger) {
  961.       ((TaskLogger)m_Log).taskStarted();
  962.     }
  963.     outBuff.append("=== Run information ===nn");
  964.     outBuff.append("Scheme:       " + cname);
  965.     if (classifier instanceof OptionHandler) {
  966.       String [] o = ((OptionHandler) classifier).getOptions();
  967.       outBuff.append(" " + Utils.joinOptions(o));
  968.     }
  969.     outBuff.append("n");
  970.     outBuff.append("Relation:     " + inst.relationName() + 'n');
  971.     outBuff.append("Instances:    " + inst.numInstances() + 'n');
  972.     outBuff.append("Attributes:   " + inst.numAttributes() + 'n');
  973.     if (inst.numAttributes() < 100) {
  974.       for (int i = 0; i < inst.numAttributes(); i++) {
  975. outBuff.append("              " + inst.attribute(i).name()
  976.        + 'n');
  977.       }
  978.     } else {
  979.       outBuff.append("              [list of attributes omitted]n");
  980.     }
  981.     outBuff.append("Test mode:    ");
  982.     switch (testMode) {
  983.       case 3: // Test on training
  984.       outBuff.append("evaluate on training datan");
  985.       break;
  986.       case 1: // CV mode
  987.       outBuff.append("" + numFolds + "-fold cross-validationn");
  988.       break;
  989.       case 2: // Percent split
  990.       outBuff.append("split " + percent
  991.        + "% train, remainder testn");
  992.       break;
  993.       case 4: // Test on user split
  994.       outBuff.append("user supplied test set: "
  995.      + userTest.numInstances() + " instancesn");
  996.       break;
  997.     }
  998.             if (costMatrix != null) {
  999.                outBuff.append("Evaluation cost matrix:n")
  1000.                .append(costMatrix.toString()).append("n");
  1001.             }
  1002.     outBuff.append("n");
  1003.     m_History.addResult(name, outBuff);
  1004.     m_History.setSingle(name);
  1005.     
  1006.     // Build the model and output it.
  1007.     if (outputModel || (testMode == 3) || (testMode == 4)) {
  1008.       m_Log.statusMessage("Building model on training data...");
  1009.       trainTimeStart = System.currentTimeMillis();
  1010.       classifier.buildClassifier(inst);
  1011.       trainTimeElapsed = System.currentTimeMillis() - trainTimeStart;
  1012.     }
  1013.     if (outputModel) {
  1014.       outBuff.append("=== Classifier model (full training set) ===nn");
  1015.       outBuff.append(classifier.toString() + "n");
  1016.       outBuff.append("nTime taken to build model: " +
  1017.      Utils.doubleToString(trainTimeElapsed / 1000.0,2)
  1018.      + " secondsnn");
  1019.       m_History.updateResult(name);
  1020.       if (classifier instanceof Drawable) {
  1021. grph = null;
  1022. try {
  1023.   grph = ((Drawable)classifier).graph();
  1024. } catch (Exception ex) {
  1025. }
  1026.       }
  1027.     }
  1028.     
  1029.     Evaluation eval = null;
  1030.     switch (testMode) {
  1031.       case 3: // Test on training
  1032.       m_Log.statusMessage("Evaluating on training data...");
  1033.       eval = new Evaluation(inst, costMatrix);
  1034.       for (int jj=0;jj<inst.numInstances();jj++) {
  1035. processClassifierPrediction(inst.instance(jj), classifier,
  1036.     eval, predictions,
  1037.     predInstances, plotShape, 
  1038.     plotSize);
  1039. if ((jj % 100) == 0) {
  1040.   m_Log.statusMessage("Evaluating on training data. Processed "
  1041.       +jj+" instances...");
  1042. }
  1043.       }
  1044.       outBuff.append("=== Evaluation on training set ===n");
  1045.       break;
  1046.       case 1: // CV mode
  1047.       m_Log.statusMessage("Randomizing instances...");
  1048.       inst.randomize(new Random(42));
  1049.       if (inst.attribute(classIndex).isNominal()) {
  1050. m_Log.statusMessage("Stratifying instances...");
  1051. inst.stratify(numFolds);
  1052.       }
  1053.       eval = new Evaluation(inst, costMatrix);
  1054.       // Make some splits and do a CV
  1055.       for (int fold = 0; fold < numFolds; fold++) {
  1056. m_Log.statusMessage("Creating splits for fold "
  1057.     + (fold + 1) + "...");
  1058. Instances train = inst.trainCV(numFolds, fold);
  1059. Instances test = inst.testCV(numFolds, fold);
  1060. m_Log.statusMessage("Building model for fold "
  1061.     + (fold + 1) + "...");
  1062. classifier.buildClassifier(train);
  1063. m_Log.statusMessage("Evaluating model for fold "
  1064.     + (fold + 1) + "...");
  1065. for (int jj=0;jj<test.numInstances();jj++) {
  1066.   processClassifierPrediction(test.instance(jj), classifier,
  1067.       eval, predictions,
  1068.       predInstances, plotShape,
  1069.       plotSize);
  1070. }
  1071.       }
  1072.       if (inst.attribute(classIndex).isNominal()) {
  1073. outBuff.append("=== Stratified cross-validation ===n");
  1074.       } else {
  1075. outBuff.append("=== Cross-validation ===n");
  1076.       }
  1077.       break;
  1078.       case 2: // Percent split
  1079.       m_Log.statusMessage("Randomizing instances...");
  1080.       inst.randomize(new Random(42));
  1081.       int trainSize = inst.numInstances() * percent / 100;
  1082.       int testSize = inst.numInstances() - trainSize;
  1083.       Instances train = new Instances(inst, 0, trainSize);
  1084.       Instances test = new Instances(inst, trainSize, testSize);
  1085.       m_Log.statusMessage("Building model on training split...");
  1086.       classifier.buildClassifier(train);
  1087.       eval = new Evaluation(train, costMatrix);
  1088.       m_Log.statusMessage("Evaluating on test split...");
  1089.      
  1090.       for (int jj=0;jj<test.numInstances();jj++) {
  1091. processClassifierPrediction(test.instance(jj), classifier,
  1092.     eval, predictions,
  1093.     predInstances, plotShape,
  1094.     plotSize);
  1095. if ((jj % 100) == 0) {
  1096.   m_Log.statusMessage("Evaluating on test split. Processed "
  1097.       +jj+" instances...");
  1098. }
  1099.       }
  1100.       outBuff.append("=== Evaluation on test split ===n");
  1101.       break;
  1102.       case 4: // Test on user split
  1103.       m_Log.statusMessage("Evaluating on test data...");
  1104.       eval = new Evaluation(inst, costMatrix);
  1105.       
  1106.       for (int jj=0;jj<userTest.numInstances();jj++) {
  1107. processClassifierPrediction(userTest.instance(jj), classifier,
  1108.     eval, predictions,
  1109.     predInstances, plotShape,
  1110.     plotSize);
  1111. if ((jj % 100) == 0) {
  1112.   m_Log.statusMessage("Evaluating on test data. Processed "
  1113.       +jj+" instances...");
  1114. }
  1115.       }
  1116.       outBuff.append("=== Evaluation on test set ===n");
  1117.       break;
  1118.       default:
  1119.       throw new Exception("Test mode not implemented");
  1120.     }
  1121.     
  1122.     if (outputSummary) {
  1123.       outBuff.append(eval.toSummaryString(outputEntropy) + "n");
  1124.     }
  1125.     if (inst.attribute(classIndex).isNominal()) {
  1126.       if (outputPerClass) {
  1127. outBuff.append(eval.toClassDetailsString() + "n");
  1128.       }
  1129.       if (outputConfusion) {
  1130. outBuff.append(eval.toMatrixString() + "n");
  1131.       }
  1132.     }
  1133.     m_History.updateResult(name);
  1134.     m_Log.logMessage("Finished " + cname);
  1135.     m_Log.statusMessage("OK");
  1136.   } catch (Exception ex) {
  1137.     ex.printStackTrace();
  1138.     m_Log.logMessage(ex.getMessage());
  1139.     m_Log.statusMessage("See error log");
  1140.   } finally {
  1141.     try {
  1142.       if (predInstances != null && predInstances.numInstances() > 0) {
  1143. if (predInstances.attribute(predInstances.classIndex())
  1144.     .isNumeric()) {
  1145.   postProcessPlotInfo(plotSize);
  1146. }
  1147. m_CurrentVis = new VisualizePanel();
  1148. m_CurrentVis.setName(name+" ("+inst.relationName()+")");
  1149. m_CurrentVis.setLog(m_Log);
  1150. PlotData2D tempd = new PlotData2D(predInstances);
  1151. tempd.setShapeSize(plotSize);
  1152. tempd.setShapeType(plotShape);
  1153. tempd.setPlotName(name+" ("+inst.relationName()+")");
  1154. tempd.addInstanceNumberAttribute();
  1155. m_CurrentVis.addPlot(tempd);
  1156. m_CurrentVis.setColourIndex(predInstances.classIndex()+1);
  1157. m_CurrentVis.setXIndex(m_visXIndex); 
  1158. m_CurrentVis.setYIndex(m_visYIndex);
  1159. m_CurrentVis.addActionListener(new ActionListener() {
  1160.   public void actionPerformed(ActionEvent e) {
  1161.     if (m_CurrentVis.getInstances().
  1162. relationName().
  1163. compareTo(m_Instances.relationName()) == 0) {
  1164.       setXY_VisualizeIndexes(m_CurrentVis.getXIndex(), 
  1165.      m_CurrentVis.getYIndex());
  1166.     }
  1167.   }
  1168.   });
  1169.     
  1170. if (saveVis) {
  1171.   FastVector vv = new FastVector();
  1172.   vv.addElement(m_CurrentVis);
  1173.   if (grph != null) {
  1174.     vv.addElement(grph);
  1175.   }
  1176.   if (predictions != null) {
  1177.     vv.addElement(predictions);
  1178.     vv.addElement(inst.classAttribute());
  1179.   }
  1180.   m_History.addObject(name, vv);
  1181. }
  1182.       }
  1183.     } catch (Exception ex) {
  1184.       ex.printStackTrace();
  1185.     }
  1186.     
  1187.     if (isInterrupted()) {
  1188.       m_Log.logMessage("Interrupted " + cname);
  1189.       m_Log.statusMessage("See error log");
  1190.     }
  1191.     m_RunThread = null;
  1192.     m_StartBut.setEnabled(true);
  1193.     m_StopBut.setEnabled(false);
  1194.             if (m_Log instanceof TaskLogger) {
  1195.               ((TaskLogger)m_Log).taskFinished();
  1196.             }
  1197.           }
  1198. }
  1199.       };
  1200.       m_RunThread.setPriority(Thread.MIN_PRIORITY);
  1201.       m_RunThread.start();
  1202.     }
  1203.   }
  1204.   /**
  1205.    * Handles constructing a popup menu with visualization options.
  1206.    * @param name the name of the result history list entry clicked on by
  1207.    * the user
  1208.    * @param x the x coordinate for popping up the menu
  1209.    * @param y the y coordinate for popping up the menu
  1210.    */
  1211.   protected void visualize(String name, int x, int y) {
  1212.     final String selectedName = name;
  1213.     JPopupMenu resultListMenu = new JPopupMenu();
  1214.     JMenuItem visMainBuffer = new JMenuItem("View in main window");
  1215.     visMainBuffer.addActionListener(new ActionListener() {
  1216. public void actionPerformed(ActionEvent e) {
  1217.   m_History.setSingle(selectedName);
  1218. }
  1219.       });
  1220.     resultListMenu.add(visMainBuffer);
  1221.     JMenuItem visSepBuffer = new JMenuItem("View in separate window");
  1222.     visSepBuffer.addActionListener(new ActionListener() {
  1223. public void actionPerformed(ActionEvent e) {
  1224.   m_History.openFrame(selectedName);
  1225. }
  1226.       });
  1227.     resultListMenu.add(visSepBuffer);
  1228.     JMenuItem saveOutput = new JMenuItem("Save result buffer");
  1229.     saveOutput.addActionListener(new ActionListener() {
  1230. public void actionPerformed(ActionEvent e) {
  1231.   saveBuffer(selectedName);
  1232. }
  1233.       });
  1234.     resultListMenu.add(saveOutput);
  1235.     resultListMenu.addSeparator();
  1236.     FastVector o = (FastVector)m_History.getNamedObject(selectedName);
  1237.     if (o != null) {
  1238.       VisualizePanel temp_vp = null;
  1239.       String temp_grph = null;
  1240.       FastVector temp_preds = null;
  1241.       Attribute temp_classAtt = null;
  1242.      
  1243.       for (int i = 0; i < o.size(); i++) {
  1244. Object temp = o.elementAt(i);
  1245. if (temp instanceof VisualizePanel) { // normal errors
  1246.   temp_vp = (VisualizePanel)temp;
  1247. } else if (temp instanceof String) { // graphable output
  1248.   temp_grph = (String)temp;
  1249. } else if (temp instanceof FastVector) { // predictions
  1250.   temp_preds = (FastVector)temp;
  1251. } else if (temp instanceof Attribute) { // class attribute
  1252.   temp_classAtt = (Attribute)temp;
  1253. }
  1254.       }
  1255.       final VisualizePanel vp = temp_vp;
  1256.       final String grph = temp_grph;
  1257.       final FastVector preds = temp_preds;
  1258.       final Attribute classAtt = temp_classAtt;
  1259.       JMenuItem visErrors = new JMenuItem("Visualize classifer errors");
  1260.       if (vp != null) {
  1261. visErrors.addActionListener(new ActionListener() {
  1262.     public void actionPerformed(ActionEvent e) {
  1263.       visualizeClassifierErrors(vp);
  1264.     }
  1265.   });
  1266. resultListMenu.add(visErrors);
  1267.       }
  1268.       JMenuItem visTree = new JMenuItem("Visualize tree");
  1269.       if (grph != null) {
  1270. visTree.addActionListener(new ActionListener() {
  1271.     public void actionPerformed(ActionEvent e) {
  1272.       visualizeTree(grph,vp.getName());
  1273.     }
  1274.   });
  1275. resultListMenu.add(visTree);
  1276.       }
  1277.       JMenuItem visMargin = new JMenuItem("Visualize margin curve");
  1278.       if (preds != null) {
  1279. visMargin.addActionListener(new ActionListener() {
  1280.     public void actionPerformed(ActionEvent e) {
  1281.       try {
  1282. MarginCurve tc = new MarginCurve();
  1283. Instances result = tc.getCurve(preds);
  1284. VisualizePanel vmc = new VisualizePanel();
  1285. vmc.setName(result.relationName());
  1286. vmc.setLog(m_Log);
  1287. PlotData2D tempd = new PlotData2D(result);
  1288. tempd.setPlotName(result.relationName());
  1289. tempd.addInstanceNumberAttribute();
  1290. vmc.addPlot(tempd);
  1291. visualizeClassifierErrors(vmc);
  1292.       } catch (Exception ex) {
  1293. ex.printStackTrace();
  1294.       }
  1295.     }
  1296.   });
  1297. resultListMenu.add(visMargin);
  1298.       }
  1299.       JMenu visThreshold = new JMenu("Visualize threshold curve");
  1300.       if (preds != null && classAtt != null) {
  1301. for (int i = 0; i < classAtt.numValues(); i++) {
  1302.   JMenuItem clv = new JMenuItem(classAtt.value(i));
  1303.   final int classValue = i;
  1304.   clv.addActionListener(new ActionListener() {
  1305.       public void actionPerformed(ActionEvent e) {
  1306. try {
  1307.   ThresholdCurve tc = new ThresholdCurve();
  1308.   Instances result = tc.getCurve(preds, classValue);
  1309.   VisualizePanel vmc = new VisualizePanel();
  1310.   vmc.setLog(m_Log);
  1311.   vmc.setName(result.relationName()+". Class value "+
  1312.       classAtt.value(classValue)+")");
  1313.   PlotData2D tempd = new PlotData2D(result);
  1314.   tempd.setPlotName(result.relationName());
  1315.   tempd.addInstanceNumberAttribute();
  1316.   vmc.addPlot(tempd);
  1317.   visualizeClassifierErrors(vmc);
  1318. } catch (Exception ex) {
  1319.   ex.printStackTrace();
  1320. }
  1321.       }
  1322.     });
  1323.   visThreshold.add(clv);
  1324. }
  1325. resultListMenu.add(visThreshold);
  1326.       }
  1327.       JMenu visCost = new JMenu("Visualize cost curve");
  1328.       if (preds != null && classAtt != null) {
  1329. for (int i = 0; i < classAtt.numValues(); i++) {
  1330.   JMenuItem clv = new JMenuItem(classAtt.value(i));
  1331.   final int classValue = i;
  1332.   clv.addActionListener(new ActionListener() {
  1333.       public void actionPerformed(ActionEvent e) {
  1334. try {
  1335.   CostCurve cc = new CostCurve();
  1336.   Instances result = cc.getCurve(preds, classValue);
  1337.   VisualizePanel vmc = new VisualizePanel();
  1338.   vmc.setLog(m_Log);
  1339.   vmc.setName(result.relationName()+". Class value "+
  1340.       classAtt.value(classValue)+")");
  1341.   PlotData2D tempd = new PlotData2D(result);
  1342.   tempd.m_displayAllPoints = true;
  1343.   tempd.setPlotName(result.relationName());
  1344.   boolean [] connectPoints = 
  1345.     new boolean [result.numInstances()];
  1346.   for (int jj = 1; jj < connectPoints.length; jj+=2) {
  1347.     connectPoints[jj] = true;
  1348.   }
  1349.   tempd.setConnectPoints(connectPoints);
  1350.   //   tempd.addInstanceNumberAttribute();
  1351.   vmc.addPlot(tempd);
  1352.   visualizeClassifierErrors(vmc);
  1353. } catch (Exception ex) {
  1354.   ex.printStackTrace();
  1355. }
  1356.       }
  1357.     });
  1358.   visCost.add(clv);
  1359. }
  1360. resultListMenu.add(visCost);
  1361.       }
  1362.     }
  1363.     resultListMenu.show(m_History.getList(), x, y);
  1364.   }
  1365.   /**
  1366.    * Pops up a TreeVisualizer for the classifier from the currently
  1367.    * selected item in the results list
  1368.    * @param dottyString the description of the tree in dotty format
  1369.    * @param treeName the title to assign to the display
  1370.    */
  1371.   protected void visualizeTree(String dottyString, String treeName) {
  1372.     final javax.swing.JFrame jf = 
  1373.       new javax.swing.JFrame("Weka Classifier Tree Visualizer: "+treeName);
  1374.     jf.setSize(500,400);
  1375.     jf.getContentPane().setLayout(new BorderLayout());
  1376.     TreeVisualizer tv = new TreeVisualizer(null,
  1377.    dottyString,
  1378.    new PlaceNode2());
  1379.     jf.getContentPane().add(tv, BorderLayout.CENTER);
  1380.     jf.addWindowListener(new java.awt.event.WindowAdapter() {
  1381. public void windowClosing(java.awt.event.WindowEvent e) {
  1382.   jf.dispose();
  1383. }
  1384.       });
  1385.     
  1386.     jf.setVisible(true);
  1387.   }
  1388.   /**
  1389.    * Pops up a VisualizePanel for visualizing the data and errors for 
  1390.    * the classifier from the currently selected item in the results list
  1391.    * @param sp the VisualizePanel to pop up.
  1392.    */
  1393.   protected void visualizeClassifierErrors(VisualizePanel sp) {
  1394.    
  1395.     if (sp != null) {
  1396.       String plotName = sp.getName(); 
  1397. final javax.swing.JFrame jf = 
  1398. new javax.swing.JFrame("Weka Classifier Visualize: "+plotName);
  1399. jf.setSize(500,400);
  1400. jf.getContentPane().setLayout(new BorderLayout());
  1401. jf.getContentPane().add(sp, BorderLayout.CENTER);
  1402. jf.addWindowListener(new java.awt.event.WindowAdapter() {
  1403.   public void windowClosing(java.awt.event.WindowEvent e) {
  1404.     jf.dispose();
  1405.   }
  1406. });
  1407.     jf.setVisible(true);
  1408.     }
  1409.   }
  1410.   /**
  1411.    * Save the currently selected classifier output to a file.
  1412.    * @param name the name of the buffer to save
  1413.    */
  1414.   protected void saveBuffer(String name) {
  1415.     StringBuffer sb = m_History.getNamedBuffer(name);
  1416.     if (sb != null) {
  1417.       if (m_SaveOut.save(sb)) {
  1418. m_Log.logMessage("Save succesful.");
  1419.       }
  1420.     }
  1421.   }
  1422.   
  1423.   /**
  1424.    * Stops the currently running classifier (if any).
  1425.    */
  1426.   protected void stopClassifier() {
  1427.     if (m_RunThread != null) {
  1428.       m_RunThread.interrupt();
  1429.       
  1430.       // This is deprecated (and theoretically the interrupt should do).
  1431.       m_RunThread.stop();
  1432.       
  1433.     }
  1434.   }
  1435.   
  1436.   /**
  1437.    * Tests out the classifier panel from the command line.
  1438.    *
  1439.    * @param args may optionally contain the name of a dataset to load.
  1440.    */
  1441.   public static void main(String [] args) {
  1442.     try {
  1443.       final javax.swing.JFrame jf =
  1444. new javax.swing.JFrame("Weka Knowledge Explorer: Classifier");
  1445.       jf.getContentPane().setLayout(new BorderLayout());
  1446.       final ClassifierPanel sp = new ClassifierPanel();
  1447.       jf.getContentPane().add(sp, BorderLayout.CENTER);
  1448.       weka.gui.LogPanel lp = new weka.gui.LogPanel();
  1449.       sp.setLog(lp);
  1450.       jf.getContentPane().add(lp, BorderLayout.SOUTH);
  1451.       jf.addWindowListener(new java.awt.event.WindowAdapter() {
  1452. public void windowClosing(java.awt.event.WindowEvent e) {
  1453.   jf.dispose();
  1454.   System.exit(0);
  1455. }
  1456.       });
  1457.       jf.pack();
  1458.       jf.setSize(800, 600);
  1459.       jf.setVisible(true);
  1460.       if (args.length == 1) {
  1461. System.err.println("Loading instances from " + args[0]);
  1462. java.io.Reader r = new java.io.BufferedReader(
  1463.    new java.io.FileReader(args[0]));
  1464. Instances i = new Instances(r);
  1465. sp.setInstances(i);
  1466.       }
  1467.     } catch (Exception ex) {
  1468.       ex.printStackTrace();
  1469.       System.err.println(ex.getMessage());
  1470.     }
  1471.   }
  1472. }