source: src/main/java/weka/gui/explorer/ClustererPanel.java @ 25

Last change on this file since 25 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 55.6 KB
Line 
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/*
18 *    ClustererPanel.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.explorer;
24
25import weka.clusterers.ClusterEvaluation;
26import weka.clusterers.Clusterer;
27import weka.core.Attribute;
28import weka.core.Capabilities;
29import weka.core.CapabilitiesHandler;
30import weka.core.Drawable;
31import weka.core.FastVector;
32import weka.core.Instances;
33import weka.core.OptionHandler;
34import weka.core.SerializedObject;
35import weka.core.Utils;
36import weka.core.Version;
37import weka.filters.Filter;
38import weka.filters.unsupervised.attribute.Remove;
39import weka.gui.ExtensionFileFilter;
40import weka.gui.GenericObjectEditor;
41import weka.gui.InstancesSummaryPanel;
42import weka.gui.ListSelectorDialog;
43import weka.gui.Logger;
44import weka.gui.PropertyPanel;
45import weka.gui.ResultHistoryPanel;
46import weka.gui.SaveBuffer;
47import weka.gui.SetInstancesPanel;
48import weka.gui.SysErrLog;
49import weka.gui.TaskLogger;
50import weka.gui.explorer.Explorer.CapabilitiesFilterChangeEvent;
51import weka.gui.explorer.Explorer.CapabilitiesFilterChangeListener;
52import weka.gui.explorer.Explorer.ExplorerPanel;
53import weka.gui.explorer.Explorer.LogHandler;
54import weka.gui.treevisualizer.PlaceNode2;
55import weka.gui.treevisualizer.TreeVisualizer;
56import weka.gui.visualize.VisualizePanel;
57import weka.gui.visualize.plugins.TreeVisualizePlugin;
58
59import java.awt.BorderLayout;
60import java.awt.Dimension;
61import java.awt.Font;
62import java.awt.GridBagConstraints;
63import java.awt.GridBagLayout;
64import java.awt.GridLayout;
65import java.awt.Insets;
66import java.awt.Point;
67import java.awt.event.ActionEvent;
68import java.awt.event.ActionListener;
69import java.awt.event.InputEvent;
70import java.awt.event.MouseAdapter;
71import java.awt.event.MouseEvent;
72import java.beans.PropertyChangeEvent;
73import java.beans.PropertyChangeListener;
74import java.io.File;
75import java.io.FileInputStream;
76import java.io.FileOutputStream;
77import java.io.InputStream;
78import java.io.ObjectInputStream;
79import java.io.ObjectOutputStream;
80import java.io.OutputStream;
81import java.text.SimpleDateFormat;
82import java.util.Date;
83import java.util.Random;
84import java.util.Vector;
85import java.util.zip.GZIPInputStream;
86import java.util.zip.GZIPOutputStream;
87
88import javax.swing.BorderFactory;
89import javax.swing.ButtonGroup;
90import javax.swing.DefaultComboBoxModel;
91import javax.swing.DefaultListModel;
92import javax.swing.JButton;
93import javax.swing.JCheckBox;
94import javax.swing.JComboBox;
95import javax.swing.JFileChooser;
96import javax.swing.JFrame;
97import javax.swing.JLabel;
98import javax.swing.JList;
99import javax.swing.JMenu;
100import javax.swing.JMenuItem;
101import javax.swing.JOptionPane;
102import javax.swing.JPanel;
103import javax.swing.JPopupMenu;
104import javax.swing.JRadioButton;
105import javax.swing.JScrollPane;
106import javax.swing.JTextArea;
107import javax.swing.JTextField;
108import javax.swing.JViewport;
109import javax.swing.SwingConstants;
110import javax.swing.event.ChangeEvent;
111import javax.swing.event.ChangeListener;
112import javax.swing.filechooser.FileFilter;
113
114import weka.gui.hierarchyvisualizer.HierarchyVisualizer;
115
116/**
117 * This panel allows the user to select and configure a clusterer, and evaluate
118 * the clusterer using a number of testing modes (test on the training data,
119 * train/test on a percentage split, test on a
120 * separate split). The results of clustering runs are stored in a result
121 * history so that previous results are accessible.
122 *
123 * @author Mark Hall (mhall@cs.waikato.ac.nz)
124 * @author Richard Kirkby (rkirkby@cs.waikato.ac.nz)
125 * @version $Revision: 5961 $
126 */
127public class ClustererPanel
128  extends JPanel
129  implements CapabilitiesFilterChangeListener, ExplorerPanel, LogHandler {
130
131  /** for serialization */
132  static final long serialVersionUID = -2474932792950820990L;
133
134  /** the parent frame */
135  protected Explorer m_Explorer = null;
136 
137  /** The filename extension that should be used for model files */
138  public static String MODEL_FILE_EXTENSION = ".model";
139
140  /** Lets the user configure the clusterer */
141  protected GenericObjectEditor m_ClustererEditor =
142    new GenericObjectEditor();
143
144  /** The panel showing the current clusterer selection */
145  protected PropertyPanel m_CLPanel = new PropertyPanel(m_ClustererEditor);
146 
147  /** The output area for classification results */
148  protected JTextArea m_OutText = new JTextArea(20, 40);
149
150  /** The destination for log/status messages */
151  protected Logger m_Log = new SysErrLog();
152
153  /** The buffer saving object for saving output */
154  SaveBuffer m_SaveOut = new SaveBuffer(m_Log, this);
155
156  /** A panel controlling results viewing */
157  protected ResultHistoryPanel m_History = new ResultHistoryPanel(m_OutText);
158
159  /** Click to set test mode to generate a % split */
160  protected JRadioButton m_PercentBut = new JRadioButton("Percentage split");
161
162  /** Click to set test mode to test on training data */
163  protected JRadioButton m_TrainBut = new JRadioButton("Use training set");
164
165  /** Click to set test mode to a user-specified test set */
166  protected JRadioButton m_TestSplitBut =
167    new JRadioButton("Supplied test set");
168
169  /** Click to set test mode to classes to clusters based evaluation */
170  protected JRadioButton m_ClassesToClustersBut = 
171    new JRadioButton("Classes to clusters evaluation");
172
173  /** Lets the user select the class column for classes to clusters based
174      evaluation */
175  protected JComboBox m_ClassCombo = new JComboBox();
176
177  /** Label by where the % split is entered */
178  protected JLabel m_PercentLab = new JLabel("%", SwingConstants.RIGHT);
179
180  /** The field where the % split is entered */
181  protected JTextField m_PercentText = new JTextField("66");
182
183  /** The button used to open a separate test dataset */
184  protected JButton m_SetTestBut = new JButton("Set...");
185
186  /** The frame used to show the test set selection panel */
187  protected JFrame m_SetTestFrame;
188
189  /** The button used to popup a list for choosing attributes to ignore while
190      clustering */
191  protected JButton m_ignoreBut = new JButton("Ignore attributes");
192
193  protected DefaultListModel m_ignoreKeyModel = new DefaultListModel();
194  protected JList m_ignoreKeyList = new JList(m_ignoreKeyModel);
195
196  //  protected Remove m_ignoreFilter = null;
197 
198  /**
199   * Alters the enabled/disabled status of elements associated with each
200   * radio button
201   */
202  ActionListener m_RadioListener = new ActionListener() {
203    public void actionPerformed(ActionEvent e) {
204      updateRadioLinks();
205    }
206  };
207
208  /** Click to start running the clusterer */
209  protected JButton m_StartBut = new JButton("Start");
210
211  /** Stop the class combo from taking up to much space */
212  private Dimension COMBO_SIZE = new Dimension(250, m_StartBut
213                                               .getPreferredSize().height);
214
215  /** Click to stop a running clusterer */
216  protected JButton m_StopBut = new JButton("Stop");
217
218  /** The main set of instances we're playing with */
219  protected Instances m_Instances;
220
221  /** The user-supplied test set (if any) */
222  protected Instances m_TestInstances;
223
224  /** The current visualization object */
225  protected VisualizePanel m_CurrentVis = null;
226
227  /** Check to save the predictions in the results list for visualizing
228      later on */
229  protected JCheckBox m_StorePredictionsBut = 
230    new JCheckBox("Store clusters for visualization");
231 
232  /** A thread that clustering runs in */
233  protected Thread m_RunThread;
234 
235  /** The instances summary panel displayed by m_SetTestFrame */
236  protected InstancesSummaryPanel m_Summary;
237
238  /** Filter to ensure only model files are selected */ 
239  protected FileFilter m_ModelFilter =
240    new ExtensionFileFilter(MODEL_FILE_EXTENSION, "Model object files");
241
242  /** The file chooser for selecting model files */
243  protected JFileChooser m_FileChooser
244    = new JFileChooser(new File(System.getProperty("user.dir")));
245
246  /* Register the property editors we need */
247  static {
248     GenericObjectEditor.registerEditors();
249  }
250 
251  /**
252   * Creates the clusterer panel
253   */
254  public ClustererPanel() {
255
256    // Connect / configure the components
257    m_OutText.setEditable(false);
258    m_OutText.setFont(new Font("Monospaced", Font.PLAIN, 12));
259    m_OutText.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
260    m_OutText.addMouseListener(new MouseAdapter() {
261      public void mouseClicked(MouseEvent e) {
262        if ((e.getModifiers() & InputEvent.BUTTON1_MASK)
263            != InputEvent.BUTTON1_MASK) {
264          m_OutText.selectAll();
265        }
266      }
267    });
268    m_History.setBorder(BorderFactory.createTitledBorder("Result list (right-click for options)"));
269    m_ClustererEditor.setClassType(Clusterer.class);
270    m_ClustererEditor.setValue(ExplorerDefaults.getClusterer());
271    m_ClustererEditor.addPropertyChangeListener(new PropertyChangeListener() {
272      public void propertyChange(PropertyChangeEvent e) {
273        m_StartBut.setEnabled(true);
274        Capabilities currentFilter = m_ClustererEditor.getCapabilitiesFilter();
275        Clusterer clusterer = (Clusterer) m_ClustererEditor.getValue();
276        Capabilities currentSchemeCapabilities =  null;
277        if (clusterer != null && currentFilter != null && 
278            (clusterer instanceof CapabilitiesHandler)) {
279          currentSchemeCapabilities = ((CapabilitiesHandler)clusterer).getCapabilities();
280         
281          if (!currentSchemeCapabilities.supportsMaybe(currentFilter) &&
282              !currentSchemeCapabilities.supports(currentFilter)) {
283            m_StartBut.setEnabled(false);
284          }
285        }
286        repaint();
287      }
288    });
289
290    m_TrainBut.setToolTipText("Cluster the same set that the clusterer"
291                              + " is trained on");
292    m_PercentBut.setToolTipText("Train on a percentage of the data and"
293                                + " cluster the remainder");
294    m_TestSplitBut.setToolTipText("Cluster a user-specified dataset");
295    m_ClassesToClustersBut.setToolTipText("Evaluate clusters with respect to a"
296                                          +" class");
297    m_ClassCombo.setToolTipText("Select the class attribute for class based"
298                                +" evaluation");
299    m_StartBut.setToolTipText("Starts the clustering");
300    m_StopBut.setToolTipText("Stops a running clusterer");
301    m_StorePredictionsBut.
302      setToolTipText("Store predictions in the result list for later "
303                     +"visualization");
304    m_ignoreBut.setToolTipText("Ignore attributes during clustering");
305
306    m_FileChooser.setFileFilter(m_ModelFilter);
307    m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
308
309    m_ClassCombo.setPreferredSize(COMBO_SIZE);
310    m_ClassCombo.setMaximumSize(COMBO_SIZE);
311    m_ClassCombo.setMinimumSize(COMBO_SIZE);
312    m_ClassCombo.setEnabled(false);
313
314    m_PercentBut.setSelected(ExplorerDefaults.getClustererTestMode() == 2);
315    m_TrainBut.setSelected(ExplorerDefaults.getClustererTestMode() == 3);
316    m_TestSplitBut.setSelected(ExplorerDefaults.getClustererTestMode() == 4);
317    m_ClassesToClustersBut.setSelected(ExplorerDefaults.getClustererTestMode() == 5);
318    m_StorePredictionsBut.setSelected(ExplorerDefaults.getClustererStoreClustersForVis());
319    updateRadioLinks();
320    ButtonGroup bg = new ButtonGroup();
321    bg.add(m_TrainBut);
322    bg.add(m_PercentBut);
323    bg.add(m_TestSplitBut);
324    bg.add(m_ClassesToClustersBut);
325    m_TrainBut.addActionListener(m_RadioListener);
326    m_PercentBut.addActionListener(m_RadioListener);
327    m_TestSplitBut.addActionListener(m_RadioListener);
328    m_ClassesToClustersBut.addActionListener(m_RadioListener);
329    m_SetTestBut.addActionListener(new ActionListener() {
330      public void actionPerformed(ActionEvent e) {
331        setTestSet();
332      }
333    });
334
335    m_StartBut.setEnabled(false);
336    m_StopBut.setEnabled(false);
337    m_ignoreBut.setEnabled(false);
338    m_StartBut.addActionListener(new ActionListener() {
339      public void actionPerformed(ActionEvent e) {
340        startClusterer();
341      }
342    });
343    m_StopBut.addActionListener(new ActionListener() {
344      public void actionPerformed(ActionEvent e) {
345        stopClusterer();
346      }
347    });
348
349    m_ignoreBut.addActionListener(new ActionListener() {
350        public void actionPerformed(ActionEvent e) {
351          setIgnoreColumns();
352        }
353      });
354   
355    m_History.setHandleRightClicks(false);
356    // see if we can popup a menu for the selected result
357    m_History.getList().addMouseListener(new MouseAdapter() {
358        public void mouseClicked(MouseEvent e) {
359          if (((e.getModifiers() & InputEvent.BUTTON1_MASK)
360               != InputEvent.BUTTON1_MASK) || e.isAltDown()) {
361            int index = m_History.getList().locationToIndex(e.getPoint());
362            if (index != -1) {
363              String name = m_History.getNameAtIndex(index);
364              visualizeClusterer(name, e.getX(), e.getY());
365            } else {
366              visualizeClusterer(null, e.getX(), e.getY());
367            }
368          }
369        }
370      });
371   
372    m_ClassCombo.addActionListener(new ActionListener() {
373      public void actionPerformed(ActionEvent e) {
374        updateCapabilitiesFilter(m_ClustererEditor.getCapabilitiesFilter());
375      }
376    });
377
378    // Layout the GUI
379    JPanel p1 = new JPanel();
380    p1.setBorder(BorderFactory.createCompoundBorder(
381                 BorderFactory.createTitledBorder("Clusterer"),
382                 BorderFactory.createEmptyBorder(0, 5, 5, 5)
383                 ));
384    p1.setLayout(new BorderLayout());
385    p1.add(m_CLPanel, BorderLayout.NORTH);
386
387    JPanel p2 = new JPanel();
388    GridBagLayout gbL = new GridBagLayout();
389    p2.setLayout(gbL);
390    p2.setBorder(BorderFactory.createCompoundBorder(
391                 BorderFactory.createTitledBorder("Cluster mode"),
392                 BorderFactory.createEmptyBorder(0, 5, 5, 5)
393                 ));
394    GridBagConstraints gbC = new GridBagConstraints();
395    gbC.anchor = GridBagConstraints.WEST;
396    gbC.gridy = 0;     gbC.gridx = 0;
397    gbL.setConstraints(m_TrainBut, gbC);
398    p2.add(m_TrainBut);
399
400    gbC = new GridBagConstraints();
401    gbC.anchor = GridBagConstraints.WEST;
402    gbC.gridy = 1;     gbC.gridx = 0;
403    gbL.setConstraints(m_TestSplitBut, gbC);
404    p2.add(m_TestSplitBut);
405
406    gbC = new GridBagConstraints();
407    gbC.anchor = GridBagConstraints.EAST;
408    gbC.fill = GridBagConstraints.HORIZONTAL;
409    gbC.gridy = 1;     gbC.gridx = 1;    gbC.gridwidth = 2;
410    gbC.insets = new Insets(2, 10, 2, 0);
411    gbL.setConstraints(m_SetTestBut, gbC);
412    p2.add(m_SetTestBut);
413
414    gbC = new GridBagConstraints();
415    gbC.anchor = GridBagConstraints.WEST;
416    gbC.gridy = 2;     gbC.gridx = 0;
417    gbL.setConstraints(m_PercentBut, gbC);
418    p2.add(m_PercentBut);
419
420    gbC = new GridBagConstraints();
421    gbC.anchor = GridBagConstraints.EAST;
422    gbC.fill = GridBagConstraints.HORIZONTAL;
423    gbC.gridy = 2;     gbC.gridx = 1;
424    gbC.insets = new Insets(2, 10, 2, 10);
425    gbL.setConstraints(m_PercentLab, gbC);
426    p2.add(m_PercentLab);
427
428    gbC = new GridBagConstraints();
429    gbC.anchor = GridBagConstraints.EAST;
430    gbC.fill = GridBagConstraints.HORIZONTAL;
431    gbC.gridy = 2;     gbC.gridx = 2;  gbC.weightx = 100;
432    gbC.ipadx = 20;
433    gbL.setConstraints(m_PercentText, gbC);
434    p2.add(m_PercentText);
435
436    gbC = new GridBagConstraints();
437    gbC.anchor = GridBagConstraints.WEST;
438    gbC.gridy = 3;     gbC.gridx = 0;  gbC.gridwidth = 2;
439    gbL.setConstraints(m_ClassesToClustersBut, gbC);
440    p2.add(m_ClassesToClustersBut);
441
442    m_ClassCombo.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
443    gbC = new GridBagConstraints();
444    gbC.anchor = GridBagConstraints.WEST;
445    gbC.gridy = 4;     gbC.gridx = 0;  gbC.gridwidth = 2;
446    gbL.setConstraints(m_ClassCombo, gbC);
447    p2.add(m_ClassCombo);
448
449    gbC = new GridBagConstraints();
450    gbC.anchor = GridBagConstraints.WEST;
451    gbC.gridy = 5;     gbC.gridx = 0;  gbC.gridwidth = 2;
452    gbL.setConstraints(m_StorePredictionsBut, gbC);
453    p2.add(m_StorePredictionsBut);
454
455    JPanel buttons = new JPanel();
456    buttons.setLayout(new GridLayout(2, 1));
457    JPanel ssButs = new JPanel();
458    ssButs.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
459    ssButs.setLayout(new GridLayout(1, 2, 5, 5));
460    ssButs.add(m_StartBut);
461    ssButs.add(m_StopBut);
462
463    JPanel ib = new JPanel();
464    ib.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
465    ib.setLayout(new GridLayout(1, 1, 5, 5));
466    ib.add(m_ignoreBut);
467    buttons.add(ib);
468    buttons.add(ssButs);
469   
470    JPanel p3 = new JPanel();
471    p3.setBorder(BorderFactory.createTitledBorder("Clusterer output"));
472    p3.setLayout(new BorderLayout());
473    final JScrollPane js = new JScrollPane(m_OutText);
474    p3.add(js, BorderLayout.CENTER);
475    js.getViewport().addChangeListener(new ChangeListener() {
476      private int lastHeight;
477      public void stateChanged(ChangeEvent e) {
478        JViewport vp = (JViewport)e.getSource();
479        int h = vp.getViewSize().height; 
480        if (h != lastHeight) { // i.e. an addition not just a user scrolling
481          lastHeight = h;
482          int x = h - vp.getExtentSize().height;
483          vp.setViewPosition(new Point(0, x));
484        }
485      }
486    });   
487
488    JPanel mondo = new JPanel();
489    gbL = new GridBagLayout();
490    mondo.setLayout(gbL);
491    gbC = new GridBagConstraints();
492    //    gbC.anchor = GridBagConstraints.WEST;
493    gbC.fill = GridBagConstraints.HORIZONTAL;
494    gbC.gridy = 0;     gbC.gridx = 0;
495    gbL.setConstraints(p2, gbC);
496    mondo.add(p2);
497    gbC = new GridBagConstraints();
498    gbC.anchor = GridBagConstraints.NORTH;
499    gbC.fill = GridBagConstraints.HORIZONTAL;
500    gbC.gridy = 1;     gbC.gridx = 0;
501    gbL.setConstraints(buttons, gbC);
502    mondo.add(buttons);
503    gbC = new GridBagConstraints();
504    //gbC.anchor = GridBagConstraints.NORTH;
505    gbC.fill = GridBagConstraints.BOTH;
506    gbC.gridy = 2;     gbC.gridx = 0; gbC.weightx = 0;
507    gbL.setConstraints(m_History, gbC);
508    mondo.add(m_History);
509    gbC = new GridBagConstraints();
510    gbC.fill = GridBagConstraints.BOTH;
511    gbC.gridy = 0;     gbC.gridx = 1;
512    gbC.gridheight = 3;
513    gbC.weightx = 100; gbC.weighty = 100;
514    gbL.setConstraints(p3, gbC);
515    mondo.add(p3);
516
517    setLayout(new BorderLayout());
518    add(p1, BorderLayout.NORTH);
519    add(mondo, BorderLayout.CENTER);
520  }
521 
522  /**
523   * Updates the enabled status of the input fields and labels.
524   */
525  protected void updateRadioLinks() {
526   
527    m_SetTestBut.setEnabled(m_TestSplitBut.isSelected());
528    if ((m_SetTestFrame != null) && (!m_TestSplitBut.isSelected())) {
529      m_SetTestFrame.setVisible(false);
530    }
531    m_PercentText.setEnabled(m_PercentBut.isSelected());
532    m_PercentLab.setEnabled(m_PercentBut.isSelected());
533    m_ClassCombo.setEnabled(m_ClassesToClustersBut.isSelected());
534  }
535
536  /**
537   * Sets the Logger to receive informational messages
538   *
539   * @param newLog the Logger that will now get info messages
540   */
541  public void setLog(Logger newLog) {
542
543    m_Log = newLog;
544  }
545
546  /**
547   * Tells the panel to use a new set of instances.
548   *
549   * @param inst a set of Instances
550   */
551  public void setInstances(Instances inst) {
552
553    m_Instances = inst;
554   
555    m_ignoreKeyModel.removeAllElements();
556   
557    String [] attribNames = new String [m_Instances.numAttributes()];
558    for (int i = 0; i < m_Instances.numAttributes(); i++) {
559      String name = m_Instances.attribute(i).name();
560      m_ignoreKeyModel.addElement(name);
561
562       String type = "";
563      switch (m_Instances.attribute(i).type()) {
564      case Attribute.NOMINAL:
565        type = "(Nom) ";
566        break;
567      case Attribute.NUMERIC:
568        type = "(Num) ";
569        break;
570      case Attribute.STRING:
571        type = "(Str) ";
572        break;
573      case Attribute.DATE:
574        type = "(Dat) ";
575        break;
576      case Attribute.RELATIONAL:
577        type = "(Rel) ";
578        break;
579      default:
580        type = "(???) ";
581      }
582      String attnm = m_Instances.attribute(i).name();
583     
584      attribNames[i] = type + attnm;
585    }
586
587   
588    m_StartBut.setEnabled(m_RunThread == null);
589    m_StopBut.setEnabled(m_RunThread != null);
590    m_ignoreBut.setEnabled(true);
591    m_ClassCombo.setModel(new DefaultComboBoxModel(attribNames));
592    if (inst.classIndex() == -1)
593      m_ClassCombo.setSelectedIndex(attribNames.length - 1);
594    else
595      m_ClassCombo.setSelectedIndex(inst.classIndex());
596    updateRadioLinks();
597  }
598
599  /**
600   * Sets the user test set. Information about the current test set
601   * is displayed in an InstanceSummaryPanel and the user is given the
602   * ability to load another set from a file or url.
603   *
604   */
605  protected void setTestSet() {
606
607    if (m_SetTestFrame == null) {
608      final SetInstancesPanel sp = new SetInstancesPanel();
609      sp.setReadIncrementally(false);
610      m_Summary = sp.getSummary();
611      if (m_TestInstances != null) {
612        sp.setInstances(m_TestInstances);
613      }
614      sp.addPropertyChangeListener(new PropertyChangeListener() {
615        public void propertyChange(PropertyChangeEvent e) {
616          m_TestInstances = sp.getInstances();
617          m_TestInstances.setClassIndex(-1);  // make sure that no class attribute is set!
618        }
619      });
620      // Add propertychangelistener to update m_TestInstances whenever
621      // it changes in the settestframe
622      m_SetTestFrame = new JFrame("Test Instances");
623      sp.setParentFrame(m_SetTestFrame);   // enable Close-Button
624      m_SetTestFrame.getContentPane().setLayout(new BorderLayout());
625      m_SetTestFrame.getContentPane().add(sp, BorderLayout.CENTER);
626      m_SetTestFrame.pack();
627    }
628    m_SetTestFrame.setVisible(true);
629  }
630 
631  /**
632   * Starts running the currently configured clusterer with the current
633   * settings. This is run in a separate thread, and will only start if
634   * there is no clusterer already running. The clusterer output is sent
635   * to the results history panel.
636   */
637  protected void startClusterer() {
638
639    if (m_RunThread == null) {
640      m_StartBut.setEnabled(false);
641      m_StopBut.setEnabled(true);
642      m_ignoreBut.setEnabled(false);
643      m_RunThread = new Thread() {
644        public void run() {
645          // Copy the current state of things
646          m_Log.statusMessage("Setting up...");
647          Instances inst = new Instances(m_Instances);
648          inst.setClassIndex(-1);
649          Instances userTest = null;
650          ClustererAssignmentsPlotInstances plotInstances = ExplorerDefaults.getClustererAssignmentsPlotInstances();
651          plotInstances.setClusterer((Clusterer) m_ClustererEditor.getValue());
652          if (m_TestInstances != null) {
653            userTest = new Instances(m_TestInstances);
654          }
655         
656          boolean saveVis = m_StorePredictionsBut.isSelected();
657          String grph = null;
658          int[] ignoredAtts = null;
659
660          int testMode = 0;
661          int percent = 66;
662          Clusterer clusterer = (Clusterer) m_ClustererEditor.getValue();
663          Clusterer fullClusterer = null;
664          StringBuffer outBuff = new StringBuffer();
665          String name = (new SimpleDateFormat("HH:mm:ss - ")).format(new Date());
666          String cname = clusterer.getClass().getName();
667          if (cname.startsWith("weka.clusterers.")) {
668            name += cname.substring("weka.clusterers.".length());
669          } else {
670            name += cname;
671          }
672          String cmd = m_ClustererEditor.getValue().getClass().getName();
673          if (m_ClustererEditor.getValue() instanceof OptionHandler)
674            cmd += " " + Utils.joinOptions(((OptionHandler) m_ClustererEditor.getValue()).getOptions());
675          try {
676            m_Log.logMessage("Started " + cname);
677            m_Log.logMessage("Command: " + cmd);
678            if (m_Log instanceof TaskLogger) {
679              ((TaskLogger)m_Log).taskStarted();
680            }
681            if (m_PercentBut.isSelected()) {
682              testMode = 2;
683              percent = Integer.parseInt(m_PercentText.getText());
684              if ((percent <= 0) || (percent >= 100)) {
685                throw new Exception("Percentage must be between 0 and 100");
686              }
687            } else if (m_TrainBut.isSelected()) {
688              testMode = 3;
689            } else if (m_TestSplitBut.isSelected()) {
690              testMode = 4;
691              // Check the test instance compatibility
692              if (userTest == null) {
693                throw new Exception("No user test set has been opened");
694              }
695              if (!inst.equalHeaders(userTest)) {
696                throw new Exception("Train and test set are not compatible\n" + inst.equalHeadersMsg(userTest));
697              }
698            } else if (m_ClassesToClustersBut.isSelected()) {
699              testMode = 5;
700            } else {
701              throw new Exception("Unknown test mode");
702            }
703
704            Instances trainInst = new Instances(inst);
705            if (m_ClassesToClustersBut.isSelected()) {
706              trainInst.setClassIndex(m_ClassCombo.getSelectedIndex());
707              inst.setClassIndex(m_ClassCombo.getSelectedIndex());
708              if (inst.classAttribute().isNumeric()) {
709                throw new Exception("Class must be nominal for class based "
710                                    +"evaluation!");
711              }
712            }
713            if (!m_ignoreKeyList.isSelectionEmpty()) {
714              trainInst = removeIgnoreCols(trainInst);
715            }
716
717            // Output some header information
718            outBuff.append("=== Run information ===\n\n");
719            outBuff.append("Scheme:       " + cname);
720            if (clusterer instanceof OptionHandler) {
721              String [] o = ((OptionHandler) clusterer).getOptions();
722              outBuff.append(" " + Utils.joinOptions(o));
723            }
724            outBuff.append("\n");
725            outBuff.append("Relation:     " + inst.relationName() + '\n');
726            outBuff.append("Instances:    " + inst.numInstances() + '\n');
727            outBuff.append("Attributes:   " + inst.numAttributes() + '\n');
728            if (inst.numAttributes() < 100) {
729              boolean [] selected = new boolean [inst.numAttributes()];
730              for (int i = 0; i < inst.numAttributes(); i++) {
731                selected[i] = true;
732              }
733              if (!m_ignoreKeyList.isSelectionEmpty()) {
734                int [] indices = m_ignoreKeyList.getSelectedIndices();
735                for (int i = 0; i < indices.length; i++) {
736                  selected[indices[i]] = false;
737                }
738              }
739              if (m_ClassesToClustersBut.isSelected()) {
740                selected[m_ClassCombo.getSelectedIndex()] = false;
741              }
742              for (int i = 0; i < inst.numAttributes(); i++) {
743                if (selected[i]) {
744                  outBuff.append("              " + inst.attribute(i).name()
745                                 + '\n');
746                }
747              }
748              if (!m_ignoreKeyList.isSelectionEmpty() 
749                  || m_ClassesToClustersBut.isSelected()) {
750                outBuff.append("Ignored:\n");
751                for (int i = 0; i < inst.numAttributes(); i++) {
752                  if (!selected[i]) {
753                    outBuff.append("              " + inst.attribute(i).name()
754                                   + '\n');
755                  }
756                }
757              }
758            } else {
759              outBuff.append("              [list of attributes omitted]\n");
760            }
761
762            if (!m_ignoreKeyList.isSelectionEmpty()) {
763              ignoredAtts = m_ignoreKeyList.getSelectedIndices();
764            }
765
766            if (m_ClassesToClustersBut.isSelected()) {
767              // add class to ignored list
768              if (ignoredAtts == null) {
769                ignoredAtts = new int[1];
770                ignoredAtts[0] = m_ClassCombo.getSelectedIndex();
771              } else {
772                int[] newIgnoredAtts = new int[ignoredAtts.length+1];
773                System.arraycopy(ignoredAtts, 0, newIgnoredAtts, 0, ignoredAtts.length);
774                newIgnoredAtts[ignoredAtts.length] = m_ClassCombo.getSelectedIndex();
775                ignoredAtts = newIgnoredAtts;
776              }
777            }
778
779
780            outBuff.append("Test mode:    ");
781            switch (testMode) {
782              case 3: // Test on training
783              outBuff.append("evaluate on training data\n");
784              break;
785              case 2: // Percent split
786              outBuff.append("split " + percent
787                               + "% train, remainder test\n");
788              break;
789              case 4: // Test on user split
790              outBuff.append("user supplied test set: "
791                             + userTest.numInstances() + " instances\n");
792              break;
793            case 5: // Classes to clusters evaluation on training
794              outBuff.append("Classes to clusters evaluation on training data");
795             
796              break;
797            }
798            outBuff.append("\n");
799            m_History.addResult(name, outBuff);
800            m_History.setSingle(name);
801           
802            // Build the model and output it.
803            m_Log.statusMessage("Building model on training data...");
804
805            // remove the class attribute (if set) and build the clusterer
806            clusterer.buildClusterer(removeClass(trainInst));
807           
808            if (testMode == 2) {
809              outBuff.append("\n=== Clustering model (full training set) ===\n\n");
810           
811              outBuff.append(clusterer.toString() + '\n');
812            }
813            m_History.updateResult(name);
814            if (clusterer instanceof Drawable) {
815              try {
816                grph = ((Drawable)clusterer).graph();
817              } catch (Exception ex) {
818              }
819            }
820            // copy full model for output
821            SerializedObject so = new SerializedObject(clusterer);
822            fullClusterer = (Clusterer) so.getObject();
823           
824            ClusterEvaluation eval = new ClusterEvaluation();
825            eval.setClusterer(clusterer);
826            switch (testMode) {
827              case 3: case 5: // Test on training
828              m_Log.statusMessage("Clustering training data...");
829              eval.evaluateClusterer(trainInst);
830              plotInstances.setInstances(inst);
831              plotInstances.setClusterEvaluation(eval);
832              outBuff.append("=== Model and evaluation on training set ===\n\n");
833              break;
834
835              case 2: // Percent split
836              m_Log.statusMessage("Randomizing instances...");
837              inst.randomize(new Random(1));
838              trainInst.randomize(new Random(1));
839              int trainSize = trainInst.numInstances() * percent / 100;
840              int testSize = trainInst.numInstances() - trainSize;
841              Instances train = new Instances(trainInst, 0, trainSize);
842              Instances test = new Instances(trainInst, trainSize, testSize);
843              Instances testVis = new Instances(inst, trainSize, testSize);
844              m_Log.statusMessage("Building model on training split...");
845              clusterer.buildClusterer(train);
846              m_Log.statusMessage("Evaluating on test split...");
847              eval.evaluateClusterer(test);
848              plotInstances.setInstances(testVis);
849              plotInstances.setClusterEvaluation(eval);
850              outBuff.append("=== Model and evaluation on test split ===\n");
851              break;
852               
853              case 4: // Test on user split
854              m_Log.statusMessage("Evaluating on test data...");
855              Instances userTestT = new Instances(userTest);
856              if (!m_ignoreKeyList.isSelectionEmpty()) {
857                userTestT = removeIgnoreCols(userTestT);
858              }
859              eval.evaluateClusterer(userTestT);
860              plotInstances.setInstances(userTest);
861              plotInstances.setClusterEvaluation(eval);
862              outBuff.append("=== Model and evaluation on test set ===\n");
863              break;
864
865              default:
866              throw new Exception("Test mode not implemented");
867            }
868            outBuff.append(eval.clusterResultsToString());
869            outBuff.append("\n");
870            m_History.updateResult(name);
871            m_Log.logMessage("Finished " + cname);
872            m_Log.statusMessage("OK");
873          } catch (Exception ex) {
874            ex.printStackTrace();
875            m_Log.logMessage(ex.getMessage());
876            JOptionPane.showMessageDialog(ClustererPanel.this,
877                                          "Problem evaluating clusterer:\n"
878                                          + ex.getMessage(),
879                                          "Evaluate clusterer",
880                                          JOptionPane.ERROR_MESSAGE);
881            m_Log.statusMessage("Problem evaluating clusterer");
882          } finally {
883            if (plotInstances != null) {
884              plotInstances.setUp();
885              m_CurrentVis = new VisualizePanel();
886              m_CurrentVis.setName(name+" ("+inst.relationName()+")");
887              m_CurrentVis.setLog(m_Log);
888              try {
889                m_CurrentVis.addPlot(plotInstances.getPlotData(name));
890              } catch (Exception ex) {
891                System.err.println(ex);
892              }
893              plotInstances.cleanUp();
894
895              FastVector vv = new FastVector();
896              vv.addElement(fullClusterer);
897              Instances trainHeader = new Instances(m_Instances, 0);
898              vv.addElement(trainHeader);
899              if (ignoredAtts != null) vv.addElement(ignoredAtts);
900              if (saveVis) {
901                vv.addElement(m_CurrentVis);
902                if (grph != null) {
903                  vv.addElement(grph);
904                }
905               
906              }
907              m_History.addObject(name, vv);
908            }
909            if (isInterrupted()) {
910              m_Log.logMessage("Interrupted " + cname);
911              m_Log.statusMessage("See error log");
912            }
913            m_RunThread = null;
914            m_StartBut.setEnabled(true);
915            m_StopBut.setEnabled(false);
916            m_ignoreBut.setEnabled(true);
917            if (m_Log instanceof TaskLogger) {
918              ((TaskLogger)m_Log).taskFinished();
919            }
920          }
921        }
922      };
923      m_RunThread.setPriority(Thread.MIN_PRIORITY);
924      m_RunThread.start();
925    }
926  }
927
928  private Instances removeClass(Instances inst) {
929    Remove af = new Remove();
930    Instances retI = null;
931   
932    try {
933      if (inst.classIndex() < 0) {
934        retI = inst;
935      } else {
936        af.setAttributeIndices(""+(inst.classIndex()+1));
937        af.setInvertSelection(false);
938        af.setInputFormat(inst);
939        retI = Filter.useFilter(inst, af);
940      }
941    } catch (Exception e) {
942      e.printStackTrace();
943    }
944    return retI;
945  }
946
947  private Instances removeIgnoreCols(Instances inst) {
948   
949    // If the user is doing classes to clusters evaluation and
950    // they have opted to ignore the class, then unselect the class in
951    // the ignore list
952    if (m_ClassesToClustersBut.isSelected()) {
953      int classIndex = m_ClassCombo.getSelectedIndex();
954      if (m_ignoreKeyList.isSelectedIndex(classIndex)) {
955        m_ignoreKeyList.removeSelectionInterval(classIndex, classIndex);
956      }
957    }
958    int [] selected = m_ignoreKeyList.getSelectedIndices();
959    Remove af = new Remove();
960    Instances retI = null;
961
962    try {
963      af.setAttributeIndicesArray(selected);
964      af.setInvertSelection(false);
965      af.setInputFormat(inst);
966      retI = Filter.useFilter(inst, af);
967    } catch (Exception e) {
968      e.printStackTrace();
969    }
970   
971    return retI;
972  }
973
974  private Instances removeIgnoreCols(Instances inst, int[] toIgnore) {
975
976    Remove af = new Remove();
977    Instances retI = null;
978
979    try {
980      af.setAttributeIndicesArray(toIgnore);
981      af.setInvertSelection(false);
982      af.setInputFormat(inst);
983      retI = Filter.useFilter(inst, af);
984    } catch (Exception e) {
985      e.printStackTrace();
986    }
987   
988    return retI;
989  }
990
991  /**
992   * Stops the currently running clusterer (if any).
993   */
994  protected void stopClusterer() {
995
996    if (m_RunThread != null) {
997      m_RunThread.interrupt();
998     
999      // This is deprecated (and theoretically the interrupt should do).
1000      m_RunThread.stop();
1001     
1002    }
1003  }
1004
1005  /**
1006   * Pops up a TreeVisualizer for the clusterer from the currently
1007   * selected item in the results list
1008   * @param graphString the description of the tree in dotty format
1009   * @param treeName the title to assign to the display
1010   */
1011  protected void visualizeTree(String graphString, String treeName) {
1012    final javax.swing.JFrame jf = 
1013      new javax.swing.JFrame("Weka Classifier Tree Visualizer: "+treeName);
1014    jf.setSize(500,400);
1015    jf.getContentPane().setLayout(new BorderLayout());
1016    if (graphString.contains("digraph")) {
1017            TreeVisualizer tv = new TreeVisualizer(null,
1018                                                   graphString,
1019                                                   new PlaceNode2());
1020            jf.getContentPane().add(tv, BorderLayout.CENTER);
1021            jf.addWindowListener(new java.awt.event.WindowAdapter() {
1022                public void windowClosing(java.awt.event.WindowEvent e) {
1023                  jf.dispose();
1024                }
1025              });
1026            jf.setVisible(true);
1027            tv.fitToScreen();
1028    } else if (graphString.startsWith("Newick:")) {
1029            HierarchyVisualizer tv = new HierarchyVisualizer(graphString.substring(7));
1030            jf.getContentPane().add(tv, BorderLayout.CENTER);
1031            jf.addWindowListener(new java.awt.event.WindowAdapter() {
1032                public void windowClosing(java.awt.event.WindowEvent e) {
1033                        jf.dispose();
1034                }
1035            });
1036            jf.setVisible(true);
1037            tv.fitToScreen();
1038    }
1039  }
1040
1041  /**
1042   * Pops up a visualize panel to display cluster assignments
1043   * @param sp the visualize panel to display
1044   */
1045  protected void visualizeClusterAssignments(VisualizePanel sp) {
1046    if (sp != null) {
1047      String plotName = sp.getName();
1048      final javax.swing.JFrame jf = 
1049        new javax.swing.JFrame("Weka Clusterer Visualize: "+plotName);
1050      jf.setSize(500,400);
1051      jf.getContentPane().setLayout(new BorderLayout());
1052      jf.getContentPane().add(sp, BorderLayout.CENTER);
1053      jf.addWindowListener(new java.awt.event.WindowAdapter() {
1054          public void windowClosing(java.awt.event.WindowEvent e) {
1055            jf.dispose();
1056          }
1057        });
1058
1059      jf.setVisible(true);
1060    }
1061  }
1062
1063  /**
1064   * Handles constructing a popup menu with visualization options
1065   * @param name the name of the result history list entry clicked on by
1066   * the user
1067   * @param x the x coordinate for popping up the menu
1068   * @param y the y coordinate for popping up the menu
1069   */
1070  protected void visualizeClusterer(String name, int x, int y) {
1071    final String selectedName = name;
1072    JPopupMenu resultListMenu = new JPopupMenu();
1073
1074    JMenuItem visMainBuffer = new JMenuItem("View in main window");
1075    if (selectedName != null) {
1076      visMainBuffer.addActionListener(new ActionListener() {
1077          public void actionPerformed(ActionEvent e) {
1078            m_History.setSingle(selectedName);
1079          }
1080        });
1081    } else {
1082      visMainBuffer.setEnabled(false);
1083    }
1084    resultListMenu.add(visMainBuffer);
1085
1086    JMenuItem visSepBuffer = new JMenuItem("View in separate window");
1087    if (selectedName != null) {
1088    visSepBuffer.addActionListener(new ActionListener() {
1089        public void actionPerformed(ActionEvent e) {
1090          m_History.openFrame(selectedName);
1091        }
1092      });
1093    } else {
1094      visSepBuffer.setEnabled(false);
1095    }
1096    resultListMenu.add(visSepBuffer);
1097
1098    JMenuItem saveOutput = new JMenuItem("Save result buffer");
1099    if (selectedName != null) {
1100      saveOutput.addActionListener(new ActionListener() {
1101          public void actionPerformed(ActionEvent e) {
1102            saveBuffer(selectedName);
1103          }
1104        });
1105    } else {
1106      saveOutput.setEnabled(false);
1107    }
1108    resultListMenu.add(saveOutput);
1109   
1110    JMenuItem deleteOutput = new JMenuItem("Delete result buffer");
1111    if (selectedName != null) {
1112      deleteOutput.addActionListener(new ActionListener() {
1113        public void actionPerformed(ActionEvent e) {
1114          m_History.removeResult(selectedName);
1115        }
1116      });
1117    } else {
1118      deleteOutput.setEnabled(false);
1119    }
1120    resultListMenu.add(deleteOutput);
1121
1122    resultListMenu.addSeparator();
1123
1124    JMenuItem loadModel = new JMenuItem("Load model");
1125    loadModel.addActionListener(new ActionListener() {
1126        public void actionPerformed(ActionEvent e) {
1127          loadClusterer();
1128        }
1129      });
1130    resultListMenu.add(loadModel);
1131
1132    FastVector o = null;
1133    if (selectedName != null) {
1134      o = (FastVector)m_History.getNamedObject(selectedName);
1135    }
1136
1137    VisualizePanel temp_vp = null;
1138    String temp_grph = null;
1139    Clusterer temp_clusterer = null;
1140    Instances temp_trainHeader = null;
1141    int[] temp_ignoreAtts = null;
1142   
1143    if (o != null) {
1144      for (int i = 0; i < o.size(); i++) {
1145        Object temp = o.elementAt(i);
1146        if (temp instanceof Clusterer) {
1147          temp_clusterer = (Clusterer)temp;
1148        } else if (temp instanceof Instances) { // training header
1149          temp_trainHeader = (Instances)temp;
1150        } else if (temp instanceof int[]) { // ignored attributes
1151          temp_ignoreAtts = (int[])temp;
1152        } else if (temp instanceof VisualizePanel) { // normal errors
1153          temp_vp = (VisualizePanel)temp;
1154        } else if (temp instanceof String) { // graphable output
1155          temp_grph = (String)temp;
1156        }
1157      } 
1158    }
1159     
1160    final VisualizePanel vp = temp_vp;
1161    final String grph = temp_grph;
1162    final Clusterer clusterer = temp_clusterer;
1163    final Instances trainHeader = temp_trainHeader;
1164    final int[] ignoreAtts = temp_ignoreAtts;
1165   
1166    JMenuItem saveModel = new JMenuItem("Save model");
1167    if (clusterer != null) {
1168      saveModel.addActionListener(new ActionListener() {
1169          public void actionPerformed(ActionEvent e) {
1170            saveClusterer(selectedName, clusterer, trainHeader, ignoreAtts);
1171          }
1172        });
1173    } else {
1174      saveModel.setEnabled(false);
1175    }
1176    resultListMenu.add(saveModel);
1177   
1178    JMenuItem reEvaluate =
1179      new JMenuItem("Re-evaluate model on current test set");
1180    if (clusterer != null && m_TestInstances != null) {
1181      reEvaluate.addActionListener(new ActionListener() {
1182          public void actionPerformed(ActionEvent e) {
1183            reevaluateModel(selectedName, clusterer, trainHeader, ignoreAtts);
1184          }
1185        }); 
1186    } else {
1187      reEvaluate.setEnabled(false);
1188    }
1189    resultListMenu.add(reEvaluate);
1190   
1191    resultListMenu.addSeparator();
1192   
1193    JMenuItem visClusts = new JMenuItem("Visualize cluster assignments");
1194    if (vp != null) {
1195      visClusts.addActionListener(new ActionListener() {
1196            public void actionPerformed(ActionEvent e) {
1197              visualizeClusterAssignments(vp);
1198            }
1199          });
1200     
1201    } else {
1202      visClusts.setEnabled(false);
1203    }
1204    resultListMenu.add(visClusts);
1205
1206    JMenuItem visTree = new JMenuItem("Visualize tree");
1207    if (grph != null) {
1208      visTree.addActionListener(new ActionListener() {
1209          public void actionPerformed(ActionEvent e) {
1210            String title;
1211            if (vp != null) title = vp.getName();
1212            else title = selectedName;
1213            visualizeTree(grph, title);
1214          }
1215        });
1216    } else {
1217      visTree.setEnabled(false);
1218    }
1219    resultListMenu.add(visTree);
1220
1221   
1222    // visualization plugins
1223    JMenu visPlugins = new JMenu("Plugins");
1224    boolean availablePlugins = false;
1225   
1226    // trees
1227    if (grph != null) {
1228      // trees
1229      Vector pluginsVector = GenericObjectEditor.getClassnames(TreeVisualizePlugin.class.getName());
1230      for (int i = 0; i < pluginsVector.size(); i++) {
1231        String className = (String) (pluginsVector.elementAt(i));
1232        try {
1233          TreeVisualizePlugin plugin = (TreeVisualizePlugin) Class.forName(className).newInstance();
1234          if (plugin == null)
1235            continue;
1236          availablePlugins = true;
1237          JMenuItem pluginMenuItem = plugin.getVisualizeMenuItem(grph, selectedName);
1238          Version version = new Version();
1239          if (pluginMenuItem != null) {
1240            if (version.compareTo(plugin.getMinVersion()) < 0)
1241              pluginMenuItem.setText(pluginMenuItem.getText() + " (weka outdated)");
1242            if (version.compareTo(plugin.getMaxVersion()) >= 0)
1243              pluginMenuItem.setText(pluginMenuItem.getText() + " (plugin outdated)");
1244            visPlugins.add(pluginMenuItem);
1245          }
1246        }
1247        catch (Exception e) {
1248          //e.printStackTrace();
1249        }
1250      }
1251    }
1252
1253    if (availablePlugins)
1254      resultListMenu.add(visPlugins);
1255   
1256    resultListMenu.show(m_History.getList(), x, y);
1257  }
1258 
1259  /**
1260   * Save the currently selected clusterer output to a file.
1261   * @param name the name of the buffer to save
1262   */
1263  protected void saveBuffer(String name) {
1264    StringBuffer sb = m_History.getNamedBuffer(name);
1265    if (sb != null) {
1266      if (m_SaveOut.save(sb)) {
1267        m_Log.logMessage("Save successful.");
1268      }
1269    }
1270  }
1271
1272  private void setIgnoreColumns() {
1273    ListSelectorDialog jd = new ListSelectorDialog(null, m_ignoreKeyList);
1274
1275    // Open the dialog
1276    int result = jd.showDialog();
1277   
1278    if (result != ListSelectorDialog.APPROVE_OPTION) {
1279      // clear selected indices
1280      m_ignoreKeyList.clearSelection();
1281    }
1282  }
1283
1284  /**
1285   * Saves the currently selected clusterer
1286   */
1287  protected void saveClusterer(String name, Clusterer clusterer,
1288                               Instances trainHeader, int[] ignoredAtts) {
1289
1290    File sFile = null;
1291    boolean saveOK = true;
1292
1293    int returnVal = m_FileChooser.showSaveDialog(this);
1294    if (returnVal == JFileChooser.APPROVE_OPTION) {
1295      sFile = m_FileChooser.getSelectedFile();
1296      if (!sFile.getName().toLowerCase().endsWith(MODEL_FILE_EXTENSION)) {
1297        sFile = new File(sFile.getParent(), sFile.getName() 
1298                         + MODEL_FILE_EXTENSION);
1299      }
1300      m_Log.statusMessage("Saving model to file...");
1301     
1302      try {
1303        OutputStream os = new FileOutputStream(sFile);
1304        if (sFile.getName().endsWith(".gz")) {
1305          os = new GZIPOutputStream(os);
1306        }
1307        ObjectOutputStream objectOutputStream = new ObjectOutputStream(os);
1308        objectOutputStream.writeObject(clusterer);
1309        if (trainHeader != null) objectOutputStream.writeObject(trainHeader);
1310        if (ignoredAtts != null) objectOutputStream.writeObject(ignoredAtts);
1311        objectOutputStream.flush();
1312        objectOutputStream.close();
1313      } catch (Exception e) {
1314       
1315        JOptionPane.showMessageDialog(null, e, "Save Failed",
1316                                      JOptionPane.ERROR_MESSAGE);
1317        saveOK = false;
1318      }
1319      if (saveOK)
1320        m_Log.logMessage("Saved model (" + name
1321                         + ") to file '" + sFile.getName() + "'");
1322      m_Log.statusMessage("OK");
1323    }
1324  }
1325
1326  /**
1327   * Loads a clusterer
1328   */
1329  protected void loadClusterer() {
1330
1331    int returnVal = m_FileChooser.showOpenDialog(this);
1332    if (returnVal == JFileChooser.APPROVE_OPTION) {
1333      File selected = m_FileChooser.getSelectedFile();
1334      Clusterer clusterer = null;
1335      Instances trainHeader = null;
1336      int[] ignoredAtts = null;
1337
1338      m_Log.statusMessage("Loading model from file...");
1339
1340      try {
1341        InputStream is = new FileInputStream(selected);
1342        if (selected.getName().endsWith(".gz")) {
1343          is = new GZIPInputStream(is);
1344        }
1345        ObjectInputStream objectInputStream = new ObjectInputStream(is);
1346        clusterer = (Clusterer) objectInputStream.readObject();
1347        try { // see if we can load the header & ignored attribute info
1348          trainHeader = (Instances) objectInputStream.readObject();
1349          ignoredAtts = (int[]) objectInputStream.readObject();
1350        } catch (Exception e) {} // don't fuss if we can't
1351        objectInputStream.close();
1352      } catch (Exception e) {
1353       
1354        JOptionPane.showMessageDialog(null, e, "Load Failed",
1355                                      JOptionPane.ERROR_MESSAGE);
1356      } 
1357
1358      m_Log.statusMessage("OK");
1359     
1360      if (clusterer != null) {
1361        m_Log.logMessage("Loaded model from file '" + selected.getName()+ "'");
1362        String name = (new SimpleDateFormat("HH:mm:ss - ")).format(new Date());
1363        String cname = clusterer.getClass().getName();
1364        if (cname.startsWith("weka.clusterers."))
1365          cname = cname.substring("weka.clusterers.".length());
1366        name += cname + " from file '" + selected.getName() + "'";
1367        StringBuffer outBuff = new StringBuffer();
1368
1369        outBuff.append("=== Model information ===\n\n");
1370        outBuff.append("Filename:     " + selected.getName() + "\n");
1371        outBuff.append("Scheme:       " + clusterer.getClass().getName());
1372        if (clusterer instanceof OptionHandler) {
1373          String [] o = ((OptionHandler) clusterer).getOptions();
1374          outBuff.append(" " + Utils.joinOptions(o));
1375        }
1376        outBuff.append("\n");
1377
1378        if (trainHeader != null) {
1379
1380          outBuff.append("Relation:     " + trainHeader.relationName() + '\n');
1381          outBuff.append("Attributes:   " + trainHeader.numAttributes() + '\n');
1382          if (trainHeader.numAttributes() < 100) {
1383            boolean [] selectedAtts = new boolean [trainHeader.numAttributes()];
1384            for (int i = 0; i < trainHeader.numAttributes(); i++) {
1385              selectedAtts[i] = true;
1386            }
1387           
1388            if (ignoredAtts != null)
1389              for (int i=0; i<ignoredAtts.length; i++)
1390                selectedAtts[ignoredAtts[i]] = false;
1391           
1392            for (int i = 0; i < trainHeader.numAttributes(); i++) {
1393              if (selectedAtts[i]) {
1394                outBuff.append("              " + trainHeader.attribute(i).name()
1395                               + '\n');
1396              }
1397            }
1398            if (ignoredAtts != null) {
1399              outBuff.append("Ignored:\n");
1400              for (int i=0; i<ignoredAtts.length; i++)
1401                outBuff.append("              "
1402                               + trainHeader.attribute(ignoredAtts[i]).name()
1403                               + '\n');
1404            }
1405          } else {
1406            outBuff.append("              [list of attributes omitted]\n");
1407          }
1408        } else {
1409          outBuff.append("\nTraining data unknown\n");
1410        } 
1411       
1412        outBuff.append("\n=== Clustering model ===\n\n");
1413        outBuff.append(clusterer.toString() + "\n");
1414
1415        m_History.addResult(name, outBuff);
1416        m_History.setSingle(name);
1417        FastVector vv = new FastVector();
1418        vv.addElement(clusterer);
1419        if (trainHeader != null) vv.addElement(trainHeader);
1420        if (ignoredAtts != null) vv.addElement(ignoredAtts);
1421        // allow visualization of graphable classifiers
1422        String grph = null;
1423        if (clusterer instanceof Drawable) {
1424          try {
1425            grph = ((Drawable)clusterer).graph();
1426          } catch (Exception ex) {
1427          }
1428        }
1429        if (grph != null) vv.addElement(grph);
1430       
1431        m_History.addObject(name, vv);
1432       
1433      }
1434    }
1435  }
1436
1437  /**
1438   * Re-evaluates the named clusterer with the current test set. Unpredictable
1439   * things will happen if the data set is not compatible with the clusterer.
1440   *
1441   * @param name the name of the clusterer entry
1442   * @param clusterer the clusterer to evaluate
1443   * @param trainHeader the header of the training set
1444   * @param ignoredAtts ignored attributes
1445   */
1446  protected void reevaluateModel(final String name, 
1447                                 final Clusterer clusterer,
1448                                 final Instances trainHeader, 
1449                                 final int[] ignoredAtts) {
1450
1451    if (m_RunThread == null) {
1452      m_StartBut.setEnabled(false);
1453      m_StopBut.setEnabled(true);
1454      m_ignoreBut.setEnabled(false);
1455      m_RunThread = new Thread() {
1456          public void run() {
1457            // Copy the current state of things
1458            m_Log.statusMessage("Setting up...");
1459
1460            StringBuffer outBuff = m_History.getNamedBuffer(name);
1461            Instances userTest = null;
1462
1463            ClustererAssignmentsPlotInstances plotInstances = ExplorerDefaults.getClustererAssignmentsPlotInstances();
1464            plotInstances.setClusterer(clusterer);
1465            if (m_TestInstances != null) {
1466              userTest = new Instances(m_TestInstances);
1467            }
1468   
1469            boolean saveVis = m_StorePredictionsBut.isSelected();
1470            String grph = null;
1471
1472            try {
1473              if (userTest == null) {
1474                throw new Exception("No user test set has been opened");
1475              }
1476              if (trainHeader != null && !trainHeader.equalHeaders(userTest)) {
1477                throw new Exception("Train and test set are not compatible\n" + trainHeader.equalHeadersMsg(userTest));
1478              }
1479
1480              m_Log.statusMessage("Evaluating on test data...");
1481              m_Log.logMessage("Re-evaluating clusterer (" + name + ") on test set");
1482
1483              m_Log.logMessage("Started reevaluate model");
1484              if (m_Log instanceof TaskLogger) {
1485                ((TaskLogger)m_Log).taskStarted();
1486              }
1487              ClusterEvaluation eval = new ClusterEvaluation();
1488              eval.setClusterer(clusterer);
1489   
1490              Instances userTestT = new Instances(userTest);
1491              if (ignoredAtts != null) {
1492                userTestT = removeIgnoreCols(userTestT, ignoredAtts);
1493              }
1494
1495              eval.evaluateClusterer(userTestT);
1496     
1497              plotInstances.setClusterEvaluation(eval);
1498              plotInstances.setInstances(userTest);
1499              plotInstances.setUp();
1500
1501              outBuff.append("\n=== Re-evaluation on test set ===\n\n");
1502              outBuff.append("User supplied test set\n"); 
1503              outBuff.append("Relation:     " + userTest.relationName() + '\n');
1504              outBuff.append("Instances:    " + userTest.numInstances() + '\n');
1505              outBuff.append("Attributes:   " + userTest.numAttributes() + "\n\n");
1506              if (trainHeader == null)
1507                outBuff.append("NOTE - if test set is not compatible then results are "
1508                               + "unpredictable\n\n");
1509     
1510              outBuff.append(eval.clusterResultsToString());
1511              outBuff.append("\n");
1512              m_History.updateResult(name);
1513              m_Log.logMessage("Finished re-evaluation");
1514              m_Log.statusMessage("OK");
1515            } catch (Exception ex) {
1516              ex.printStackTrace();
1517              m_Log.logMessage(ex.getMessage());
1518              JOptionPane.showMessageDialog(ClustererPanel.this,
1519                                            "Problem evaluating clusterer:\n"
1520                                            + ex.getMessage(),
1521                                            "Evaluate clusterer",
1522                                            JOptionPane.ERROR_MESSAGE);
1523              m_Log.statusMessage("Problem evaluating clusterer");
1524
1525            } finally {
1526              if (plotInstances != null) {
1527                m_CurrentVis = new VisualizePanel();
1528                m_CurrentVis.setName(name+" ("+userTest.relationName()+")");
1529                m_CurrentVis.setLog(m_Log);
1530                try {
1531                  m_CurrentVis.addPlot(plotInstances.getPlotData(name));
1532                } catch (Exception ex) {
1533                  System.err.println(ex);
1534                }
1535       
1536                FastVector vv = new FastVector();
1537                vv.addElement(clusterer);
1538                if (trainHeader != null) vv.addElement(trainHeader);
1539                if (ignoredAtts != null) vv.addElement(ignoredAtts);
1540                if (saveVis) {
1541                  vv.addElement(m_CurrentVis);
1542                  if (grph != null) {
1543                    vv.addElement(grph);
1544                  }
1545         
1546                }
1547                m_History.addObject(name, vv);
1548
1549              }
1550              if (isInterrupted()) {
1551                m_Log.logMessage("Interrupted reevaluate model");
1552                m_Log.statusMessage("See error log");
1553              }
1554              m_RunThread = null;
1555              m_StartBut.setEnabled(true);
1556              m_StopBut.setEnabled(false);
1557              m_ignoreBut.setEnabled(true);
1558              if (m_Log instanceof TaskLogger) {
1559                ((TaskLogger)m_Log).taskFinished();
1560              }
1561            }
1562          }
1563     
1564        };
1565      m_RunThread.setPriority(Thread.MIN_PRIORITY);
1566      m_RunThread.start();
1567    }
1568  }
1569 
1570  /**
1571   * updates the capabilities filter of the GOE
1572   *
1573   * @param filter      the new filter to use
1574   */
1575  protected void updateCapabilitiesFilter(Capabilities filter) {
1576    Instances           tempInst;
1577    Capabilities        filterClass;
1578
1579    if (filter == null) {
1580      m_ClustererEditor.setCapabilitiesFilter(new Capabilities(null));
1581      return;
1582    }
1583   
1584    if (!ExplorerDefaults.getInitGenericObjectEditorFilter())
1585      tempInst = new Instances(m_Instances, 0);
1586    else
1587      tempInst = new Instances(m_Instances);
1588    tempInst.setClassIndex(-1);
1589
1590    try {
1591      filterClass = Capabilities.forInstances(tempInst);
1592    }
1593    catch (Exception e) {
1594      filterClass = new Capabilities(null);
1595    }
1596   
1597    m_ClustererEditor.setCapabilitiesFilter(filterClass);
1598   
1599    // check capabilities
1600    m_StartBut.setEnabled(true);
1601    Capabilities currentFilter = m_ClustererEditor.getCapabilitiesFilter();
1602    Clusterer clusterer = (Clusterer) m_ClustererEditor.getValue();
1603    Capabilities currentSchemeCapabilities =  null;
1604    if (clusterer != null && currentFilter != null && 
1605        (clusterer instanceof CapabilitiesHandler)) {
1606      currentSchemeCapabilities = ((CapabilitiesHandler)clusterer).getCapabilities();
1607     
1608      if (!currentSchemeCapabilities.supportsMaybe(currentFilter) &&
1609          !currentSchemeCapabilities.supports(currentFilter)) {
1610        m_StartBut.setEnabled(false);
1611      }
1612    }
1613  }
1614 
1615  /**
1616   * method gets called in case of a change event
1617   *
1618   * @param e           the associated change event
1619   */
1620  public void capabilitiesFilterChanged(CapabilitiesFilterChangeEvent e) {
1621    if (e.getFilter() == null)
1622      updateCapabilitiesFilter(null);
1623    else
1624      updateCapabilitiesFilter((Capabilities) e.getFilter().clone());
1625  }
1626
1627  /**
1628   * Sets the Explorer to use as parent frame (used for sending notifications
1629   * about changes in the data)
1630   *
1631   * @param parent      the parent frame
1632   */
1633  public void setExplorer(Explorer parent) {
1634    m_Explorer = parent;
1635  }
1636 
1637  /**
1638   * returns the parent Explorer frame
1639   *
1640   * @return            the parent
1641   */
1642  public Explorer getExplorer() {
1643    return m_Explorer;
1644  }
1645 
1646  /**
1647   * Returns the title for the tab in the Explorer
1648   *
1649   * @return            the title of this tab
1650   */
1651  public String getTabTitle() {
1652    return "Cluster";
1653  }
1654 
1655  /**
1656   * Returns the tooltip for the tab in the Explorer
1657   *
1658   * @return            the tooltip of this tab
1659   */
1660  public String getTabTitleToolTip() {
1661    return "Identify instance clusters";
1662  }
1663
1664  /**
1665   * Tests out the clusterer panel from the command line.
1666   *
1667   * @param args may optionally contain the name of a dataset to load.
1668   */
1669  public static void main(String [] args) {
1670
1671    try {
1672      final javax.swing.JFrame jf =
1673        new javax.swing.JFrame("Weka Explorer: Cluster");
1674      jf.getContentPane().setLayout(new BorderLayout());
1675      final ClustererPanel sp = new ClustererPanel();
1676      jf.getContentPane().add(sp, BorderLayout.CENTER);
1677      weka.gui.LogPanel lp = new weka.gui.LogPanel();
1678      sp.setLog(lp);
1679      jf.getContentPane().add(lp, BorderLayout.SOUTH);
1680      jf.addWindowListener(new java.awt.event.WindowAdapter() {
1681        public void windowClosing(java.awt.event.WindowEvent e) {
1682          jf.dispose();
1683          System.exit(0);
1684        }
1685      });
1686      jf.pack();
1687      jf.setSize(800, 600);
1688      jf.setVisible(true);
1689      if (args.length == 1) {
1690        System.err.println("Loading instances from " + args[0]);
1691        java.io.Reader r = new java.io.BufferedReader(
1692                           new java.io.FileReader(args[0]));
1693        Instances i = new Instances(r);
1694        sp.setInstances(i);
1695      }
1696    } catch (Exception ex) {
1697      ex.printStackTrace();
1698      System.err.println(ex.getMessage());
1699    }
1700  }
1701}
Note: See TracBrowser for help on using the repository browser.