source: src/main/java/weka/gui/explorer/AttributeSelectionPanel.java @ 28

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

Import di weka.

File size: 39.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 *    AttributeSelectionPanel.java
19 *    Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.explorer;
24
25import weka.attributeSelection.ASEvaluation;
26import weka.attributeSelection.ASSearch;
27import weka.attributeSelection.AttributeEvaluator;
28import weka.attributeSelection.AttributeSelection;
29import weka.attributeSelection.AttributeTransformer;
30import weka.attributeSelection.Ranker;
31import weka.core.Attribute;
32import weka.core.Capabilities;
33import weka.core.CapabilitiesHandler;
34import weka.core.FastVector;
35import weka.core.Instances;
36import weka.core.OptionHandler;
37import weka.core.Utils;
38import weka.gui.ExtensionFileFilter;
39import weka.gui.GenericObjectEditor;
40import weka.gui.Logger;
41import weka.gui.PropertyPanel;
42import weka.gui.ResultHistoryPanel;
43import weka.gui.SaveBuffer;
44import weka.gui.SysErrLog;
45import weka.gui.TaskLogger;
46import weka.gui.explorer.Explorer.CapabilitiesFilterChangeEvent;
47import weka.gui.explorer.Explorer.CapabilitiesFilterChangeListener;
48import weka.gui.explorer.Explorer.ExplorerPanel;
49import weka.gui.explorer.Explorer.LogHandler;
50import weka.gui.visualize.MatrixPanel;
51
52import java.awt.BorderLayout;
53import java.awt.Dimension;
54import java.awt.Font;
55import java.awt.GridBagConstraints;
56import java.awt.GridBagLayout;
57import java.awt.GridLayout;
58import java.awt.Insets;
59import java.awt.Point;
60import java.awt.event.ActionEvent;
61import java.awt.event.ActionListener;
62import java.awt.event.InputEvent;
63import java.awt.event.MouseAdapter;
64import java.awt.event.MouseEvent;
65import java.beans.PropertyChangeEvent;
66import java.beans.PropertyChangeListener;
67import java.io.BufferedWriter;
68import java.io.FileWriter;
69import java.text.SimpleDateFormat;
70import java.util.Date;
71import java.util.Random;
72import java.util.Vector;
73
74import javax.swing.BorderFactory;
75import javax.swing.ButtonGroup;
76import javax.swing.DefaultComboBoxModel;
77import javax.swing.JButton;
78import javax.swing.JComboBox;
79import javax.swing.JFileChooser;
80import javax.swing.JLabel;
81import javax.swing.JMenuItem;
82import javax.swing.JOptionPane;
83import javax.swing.JPanel;
84import javax.swing.JPopupMenu;
85import javax.swing.JRadioButton;
86import javax.swing.JScrollPane;
87import javax.swing.JTextArea;
88import javax.swing.JTextField;
89import javax.swing.JViewport;
90import javax.swing.SwingConstants;
91import javax.swing.event.ChangeEvent;
92import javax.swing.event.ChangeListener;
93
94/**
95 * This panel allows the user to select and configure an attribute
96 * evaluator and a search method, set the
97 * attribute of the current dataset to be used as the class, and perform
98 * attribute selection using one of two  selection modes (select using all the
99 * training data or perform a n-fold cross validation---on each trial
100 * selecting features using n-1 folds of the data).
101 * The results of attribute selection runs are stored in a results history
102 * so that previous results are accessible.
103 *
104 * @author Mark Hall (mhall@cs.waikato.ac.nz)
105 * @version $Revision: 5385 $
106 */
107public class AttributeSelectionPanel 
108  extends JPanel
109  implements CapabilitiesFilterChangeListener, ExplorerPanel, LogHandler {
110 
111  /** for serialization */
112  static final long serialVersionUID = 5627185966993476142L;
113
114  /** the parent frame */
115  protected Explorer m_Explorer = null;
116
117  /** Lets the user configure the attribute evaluator */
118  protected GenericObjectEditor m_AttributeEvaluatorEditor =
119    new GenericObjectEditor();
120
121  /** Lets the user configure the search method */
122  protected GenericObjectEditor m_AttributeSearchEditor = 
123    new GenericObjectEditor();
124
125  /** The panel showing the current attribute evaluation method */
126  protected PropertyPanel m_AEEPanel = 
127    new PropertyPanel(m_AttributeEvaluatorEditor);
128
129  /** The panel showing the current search method */
130  protected PropertyPanel m_ASEPanel = 
131    new PropertyPanel(m_AttributeSearchEditor);
132 
133  /** The output area for attribute selection results */
134  protected JTextArea m_OutText = new JTextArea(20, 40);
135
136  /** The destination for log/status messages */
137  protected Logger m_Log = new SysErrLog();
138
139  /** The buffer saving object for saving output */
140  SaveBuffer m_SaveOut = new SaveBuffer(m_Log, this);
141
142  /** A panel controlling results viewing */
143  protected ResultHistoryPanel m_History = new ResultHistoryPanel(m_OutText);
144
145  /** Lets the user select the class column */
146  protected JComboBox m_ClassCombo = new JComboBox();
147
148  /** Click to set evaluation mode to cross-validation */
149  protected JRadioButton m_CVBut = new JRadioButton("Cross-validation");
150
151  /** Click to set test mode to test on training data */
152  protected JRadioButton m_TrainBut = 
153    new JRadioButton("Use full training set");
154
155  /** Label by where the cv folds are entered */
156  protected JLabel m_CVLab = new JLabel("Folds", SwingConstants.RIGHT);
157
158  /** The field where the cv folds are entered */
159  protected JTextField m_CVText = new JTextField("10");
160
161  /** Label by where cv random seed is entered */
162  protected JLabel m_SeedLab = new JLabel("Seed",SwingConstants.RIGHT);
163
164  /** The field where the seed value is entered */
165  protected JTextField m_SeedText = new JTextField("1");
166
167  /**
168   * Alters the enabled/disabled status of elements associated with each
169   * radio button
170   */
171  ActionListener m_RadioListener = new ActionListener() {
172    public void actionPerformed(ActionEvent e) {
173      updateRadioLinks();
174    }
175  };
176
177  /** Click to start running the attribute selector */
178  protected JButton m_StartBut = new JButton("Start");
179
180  /** Click to stop a running classifier */
181  protected JButton m_StopBut = new JButton("Stop");
182
183 /** Stop the class combo from taking up to much space */
184  private Dimension COMBO_SIZE = new Dimension(150, m_StartBut
185                                               .getPreferredSize().height);
186 
187  /** The main set of instances we're playing with */
188  protected Instances m_Instances;
189
190  /** A thread that attribute selection runs in */
191  protected Thread m_RunThread;
192
193  /* Register the property editors we need */
194  static {
195     GenericObjectEditor.registerEditors();
196  }
197 
198  /**
199   * Creates the classifier panel
200   */
201  public AttributeSelectionPanel() {
202
203    // Connect / configure the components
204    m_OutText.setEditable(false);
205    m_OutText.setFont(new Font("Monospaced", Font.PLAIN, 12));
206    m_OutText.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
207    m_OutText.addMouseListener(new MouseAdapter() {
208      public void mouseClicked(MouseEvent e) {
209        if ((e.getModifiers() & InputEvent.BUTTON1_MASK)
210            != InputEvent.BUTTON1_MASK) {
211          m_OutText.selectAll();
212        }
213      }
214    });
215    m_History.setBorder(BorderFactory.createTitledBorder("Result list (right-click for options)"));
216    m_AttributeEvaluatorEditor.setClassType(ASEvaluation.class);
217    m_AttributeEvaluatorEditor.setValue(ExplorerDefaults.getASEvaluator());
218    m_AttributeEvaluatorEditor.
219      addPropertyChangeListener(new PropertyChangeListener() {
220      public void propertyChange(PropertyChangeEvent e) {
221        if (m_AttributeEvaluatorEditor.getValue() instanceof AttributeEvaluator) {
222          if (!(m_AttributeSearchEditor.getValue() instanceof Ranker)) {
223            Object backup = m_AttributeEvaluatorEditor.getBackup();
224            int result = 
225              JOptionPane.showConfirmDialog(null, "You must use use the Ranker search method "
226                                            +"in order to use\n"
227                                            +m_AttributeEvaluatorEditor.getValue().getClass().getName()
228                                            +".\nShould I select the Ranker search method for you?",
229                                            "Alert!", JOptionPane.YES_NO_OPTION);
230            if (result == JOptionPane.YES_OPTION) {
231              m_AttributeSearchEditor.setValue(new Ranker());
232            } else {
233              // restore to what was there previously (if possible)
234              if (backup != null) {
235                m_AttributeEvaluatorEditor.setValue(backup);
236              }             
237            }
238          }
239        } else {
240          if (m_AttributeSearchEditor.getValue() instanceof Ranker) {
241            Object backup = m_AttributeEvaluatorEditor.getBackup();
242            int result = 
243              JOptionPane.showConfirmDialog(null, "You must use use a search method that explores \n"
244                                            +"the space of attribute subsets (such as GreedyStepwise) in "
245                                            +"order to use\n"
246                                            +m_AttributeEvaluatorEditor.getValue().getClass().getName()
247                                            +".\nShould I select the GreedyStepwise search method for "
248                                            +"you?\n(you can always switch to a different method afterwards)",
249                                            "Alert!", JOptionPane.YES_NO_OPTION);
250            if (result == JOptionPane.YES_OPTION) {
251              m_AttributeSearchEditor.setValue(new weka.attributeSelection.GreedyStepwise());
252            } else {
253              // restore to what was there previously (if possible)
254              if (backup != null) {
255                m_AttributeEvaluatorEditor.setValue(backup);
256              } 
257            }
258          }
259        }
260        updateRadioLinks();
261       
262        m_StartBut.setEnabled(true);
263        // check capabilities...
264        Capabilities currentFilter = m_AttributeEvaluatorEditor.getCapabilitiesFilter();
265        ASEvaluation evaluator = (ASEvaluation) m_AttributeEvaluatorEditor.getValue();
266        Capabilities currentSchemeCapabilities =  null;
267        if (evaluator != null && currentFilter != null && 
268            (evaluator instanceof CapabilitiesHandler)) {
269          currentSchemeCapabilities = ((CapabilitiesHandler)evaluator).getCapabilities();
270         
271          if (!currentSchemeCapabilities.supportsMaybe(currentFilter) &&
272              !currentSchemeCapabilities.supports(currentFilter)) {
273            m_StartBut.setEnabled(false);
274          }
275        }
276        repaint();
277      }
278    });
279
280    m_AttributeSearchEditor.setClassType(ASSearch.class);
281    m_AttributeSearchEditor.setValue(ExplorerDefaults.getASSearch());
282    m_AttributeSearchEditor.
283      addPropertyChangeListener(new PropertyChangeListener() {
284        public void propertyChange(PropertyChangeEvent e) {
285          if (m_AttributeSearchEditor.getValue() instanceof Ranker) {
286            if (!(m_AttributeEvaluatorEditor.getValue() instanceof AttributeEvaluator)) {
287              Object backup = m_AttributeSearchEditor.getBackup();
288              int result = 
289                JOptionPane.showConfirmDialog(null, "You must use use an evaluator that evaluates\n"
290                                              +"single attributes (such as InfoGain) in order to use\n"
291                                              +"the Ranker. Should I select the InfoGain evaluator "
292                                              +"for you?\n"
293                                              +"(You can always switch to a different method afterwards)" ,
294                                              "Alert!", JOptionPane.YES_NO_OPTION);
295              if (result == JOptionPane.YES_OPTION) {
296                m_AttributeEvaluatorEditor.setValue(new weka.attributeSelection.InfoGainAttributeEval());
297              } else {
298                // restore to what was there previously (if possible)
299                if (backup != null) {
300                  m_AttributeSearchEditor.setValue(backup);
301                }             
302              }
303            }
304          } else {
305            if (m_AttributeEvaluatorEditor.getValue() instanceof AttributeEvaluator) {
306              Object backup = m_AttributeSearchEditor.getBackup();
307              int result = 
308                JOptionPane.showConfirmDialog(null, "You must use use an evaluator that evaluates\n"
309                                              +"subsets of attributes (such as CFS) in order to use\n"
310                                              +m_AttributeEvaluatorEditor.getValue().getClass().getName()
311                                              +".\nShould I select the CFS subset evaluator for you?"
312                                              +"\n(you can always switch to a different method afterwards)",
313                                              "Alert!", JOptionPane.YES_NO_OPTION);
314
315              if (result == JOptionPane.YES_OPTION) {
316                m_AttributeEvaluatorEditor.setValue(new weka.attributeSelection.CfsSubsetEval());
317              } else {
318                // restore to what was there previously (if possible)
319                if (backup != null) {
320                  m_AttributeSearchEditor.setValue(backup);
321                }             
322              }
323            }
324          }
325          repaint();
326        }
327      });
328   
329    m_ClassCombo.addActionListener(new ActionListener() {
330      public void actionPerformed(ActionEvent e) {
331        updateCapabilitiesFilter(m_AttributeEvaluatorEditor.getCapabilitiesFilter());
332      }
333    });
334
335    m_ClassCombo.setToolTipText("Select the attribute to use as the class");
336    m_TrainBut.setToolTipText("select attributes using the full training "
337                              + "dataset");
338    m_CVBut.setToolTipText("Perform a n-fold cross-validation");
339
340    m_StartBut.setToolTipText("Starts attribute selection");
341    m_StopBut.setToolTipText("Stops a attribute selection task");
342   
343    m_ClassCombo.setPreferredSize(COMBO_SIZE);
344    m_ClassCombo.setMaximumSize(COMBO_SIZE);
345    m_ClassCombo.setMinimumSize(COMBO_SIZE);
346    m_History.setPreferredSize(COMBO_SIZE);
347    m_History.setMaximumSize(COMBO_SIZE);
348    m_History.setMinimumSize(COMBO_SIZE);
349   
350    m_ClassCombo.setEnabled(false);
351    m_TrainBut.setSelected(ExplorerDefaults.getASTestMode() == 0);
352    m_CVBut.setSelected(ExplorerDefaults.getASTestMode() == 1);
353    updateRadioLinks();
354    ButtonGroup bg = new ButtonGroup();
355    bg.add(m_TrainBut);
356    bg.add(m_CVBut);
357
358    m_TrainBut.addActionListener(m_RadioListener);
359    m_CVBut.addActionListener(m_RadioListener);
360
361    m_CVText.setText("" + ExplorerDefaults.getASCrossvalidationFolds());
362    m_SeedText.setText("" + ExplorerDefaults.getASRandomSeed());
363   
364    m_StartBut.setEnabled(false);
365    m_StopBut.setEnabled(false);
366
367    m_StartBut.addActionListener(new ActionListener() {
368      public void actionPerformed(ActionEvent e) {
369        startAttributeSelection();
370      }
371    });
372    m_StopBut.addActionListener(new ActionListener() {
373      public void actionPerformed(ActionEvent e) {
374        stopAttributeSelection();
375      }
376    });
377   
378    m_History.setHandleRightClicks(false);
379    // see if we can popup a menu for the selected result
380    m_History.getList().addMouseListener(new MouseAdapter() {
381        public void mouseClicked(MouseEvent e) {
382          if (((e.getModifiers() & InputEvent.BUTTON1_MASK)
383               != InputEvent.BUTTON1_MASK) || e.isAltDown()) {
384            int index = m_History.getList().locationToIndex(e.getPoint());
385            if (index != -1) {
386              String name = m_History.getNameAtIndex(index);
387              visualize(name, e.getX(), e.getY());
388            } else {
389              visualize(null, e.getX(), e.getY());
390            }
391          }
392        }
393      });
394
395    // Layout the GUI
396    JPanel p1 = new JPanel();
397    p1.setBorder(BorderFactory.createCompoundBorder(
398                 BorderFactory.createTitledBorder("Attribute Evaluator"),
399                 BorderFactory.createEmptyBorder(0, 5, 5, 5)
400                 ));
401    p1.setLayout(new BorderLayout());
402    p1.add(m_AEEPanel, BorderLayout.NORTH);
403
404    JPanel p1_1 = new JPanel();
405    p1_1.setBorder(BorderFactory.createCompoundBorder(
406                 BorderFactory.createTitledBorder("Search Method"),
407                 BorderFactory.createEmptyBorder(0, 5, 5, 5)
408                 ));
409    p1_1.setLayout(new BorderLayout());
410    p1_1.add(m_ASEPanel, BorderLayout.NORTH);
411
412    JPanel p_new = new JPanel();
413    p_new.setLayout(new BorderLayout());
414    p_new.add(p1, BorderLayout.NORTH);
415    p_new.add(p1_1, BorderLayout.CENTER);
416
417    JPanel p2 = new JPanel();
418    GridBagLayout gbL = new GridBagLayout();
419    p2.setLayout(gbL);
420    p2.setBorder(BorderFactory.createCompoundBorder(
421                 BorderFactory.createTitledBorder("Attribute Selection Mode"),
422                 BorderFactory.createEmptyBorder(0, 5, 5, 5)
423                 ));
424    GridBagConstraints gbC = new GridBagConstraints();
425    gbC.anchor = GridBagConstraints.WEST;
426    gbC.gridy = 2;     gbC.gridx = 0;
427    gbL.setConstraints(m_TrainBut, gbC);
428    p2.add(m_TrainBut);
429
430    gbC = new GridBagConstraints();
431    gbC.anchor = GridBagConstraints.WEST;
432    gbC.gridy = 4;     gbC.gridx = 0;
433    gbL.setConstraints(m_CVBut, gbC);
434    p2.add(m_CVBut);
435
436    gbC = new GridBagConstraints();
437    gbC.anchor = GridBagConstraints.EAST;
438    gbC.fill = GridBagConstraints.HORIZONTAL;
439    gbC.gridy = 4;     gbC.gridx = 1;
440    gbC.insets = new Insets(2, 10, 2, 10);
441    gbL.setConstraints(m_CVLab, gbC);
442    p2.add(m_CVLab);
443
444    gbC = new GridBagConstraints();
445    gbC.anchor = GridBagConstraints.EAST;
446    gbC.fill = GridBagConstraints.HORIZONTAL;
447    gbC.gridy = 4;     gbC.gridx = 2;  gbC.weightx = 100;
448    gbC.ipadx = 20;
449    gbL.setConstraints(m_CVText, gbC);
450    p2.add(m_CVText);
451
452    gbC = new GridBagConstraints();
453    gbC.anchor = GridBagConstraints.EAST;
454    gbC.fill = GridBagConstraints.HORIZONTAL;
455    gbC.gridy = 6;     gbC.gridx = 1;
456    gbC.insets = new Insets(2, 10, 2, 10);
457    gbL.setConstraints(m_SeedLab, gbC);
458    p2.add(m_SeedLab);
459
460    gbC = new GridBagConstraints();
461    gbC.anchor = GridBagConstraints.EAST;
462    gbC.fill = GridBagConstraints.HORIZONTAL;
463    gbC.gridy = 6;     gbC.gridx = 2;  gbC.weightx = 100;
464    gbC.ipadx = 20;
465    gbL.setConstraints(m_SeedText, gbC);
466    p2.add(m_SeedText);
467
468
469    JPanel buttons = new JPanel();
470    buttons.setLayout(new GridLayout(2, 2));
471    buttons.add(m_ClassCombo);
472    m_ClassCombo.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
473    JPanel ssButs = new JPanel();
474    ssButs.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
475    ssButs.setLayout(new GridLayout(1, 2, 5, 5));
476    ssButs.add(m_StartBut);
477    ssButs.add(m_StopBut);
478    buttons.add(ssButs);
479   
480    JPanel p3 = new JPanel();
481    p3.setBorder(BorderFactory.
482                 createTitledBorder("Attribute selection output"));
483    p3.setLayout(new BorderLayout());
484    final JScrollPane js = new JScrollPane(m_OutText);
485    p3.add(js, BorderLayout.CENTER);
486    js.getViewport().addChangeListener(new ChangeListener() {
487      private int lastHeight;
488      public void stateChanged(ChangeEvent e) {
489        JViewport vp = (JViewport)e.getSource();
490        int h = vp.getViewSize().height; 
491        if (h != lastHeight) { // i.e. an addition not just a user scrolling
492          lastHeight = h;
493          int x = h - vp.getExtentSize().height;
494          vp.setViewPosition(new Point(0, x));
495        }
496      }
497    });
498
499    JPanel mondo = new JPanel();
500    gbL = new GridBagLayout();
501    mondo.setLayout(gbL);
502    gbC = new GridBagConstraints();
503    gbC.fill = GridBagConstraints.HORIZONTAL;
504    gbC.gridy = 0;     gbC.gridx = 0; gbC.weightx = 0;
505    gbL.setConstraints(p2, gbC);
506    mondo.add(p2);
507    gbC = new GridBagConstraints();
508    gbC.anchor = GridBagConstraints.NORTH;
509    gbC.fill = GridBagConstraints.HORIZONTAL;
510    gbC.gridy = 1;     gbC.gridx = 0; gbC.weightx = 0;
511    gbL.setConstraints(buttons, gbC);
512    mondo.add(buttons);
513    gbC = new GridBagConstraints();
514    gbC.fill = GridBagConstraints.BOTH;
515    gbC.gridy = 2;     gbC.gridx = 0; gbC.weightx = 0; gbC.weighty = 100;
516    gbL.setConstraints(m_History, gbC);
517    mondo.add(m_History);
518    gbC = new GridBagConstraints();
519    gbC.fill = GridBagConstraints.BOTH;
520    gbC.gridy = 0;     gbC.gridx = 1;
521    gbC.gridheight = 3;
522    gbC.weightx = 100; gbC.weighty = 100;
523    gbL.setConstraints(p3, gbC);
524    mondo.add(p3);
525
526    setLayout(new BorderLayout());
527    add(p_new, BorderLayout.NORTH);
528    add(mondo, BorderLayout.CENTER);
529  }
530
531 
532  /**
533   * Updates the enabled status of the input fields and labels.
534   */
535  protected void updateRadioLinks() {
536    m_CVBut.setEnabled(true);
537    m_CVText.setEnabled(m_CVBut.isSelected());
538    m_CVLab.setEnabled(m_CVBut.isSelected());
539    m_SeedText.setEnabled(m_CVBut.isSelected());
540    m_SeedLab.setEnabled(m_CVBut.isSelected());
541   
542    if (m_AttributeEvaluatorEditor.getValue() 
543        instanceof AttributeTransformer) {
544      m_CVBut.setSelected(false);
545      m_CVBut.setEnabled(false);
546      m_CVText.setEnabled(false);
547      m_CVLab.setEnabled(false);
548      m_SeedText.setEnabled(false);
549      m_SeedLab.setEnabled(false);
550      m_TrainBut.setSelected(true);
551    }
552  }
553 
554  /**
555   * Sets the Logger to receive informational messages
556   *
557   * @param newLog the Logger that will now get info messages
558   */
559  public void setLog(Logger newLog) {
560
561    m_Log = newLog;
562  }
563 
564  /**
565   * Tells the panel to use a new set of instances.
566   *
567   * @param inst a set of Instances
568   */
569  public void setInstances(Instances inst) {
570   
571    m_Instances = inst;
572    String [] attribNames = new String [m_Instances.numAttributes()];
573    for (int i = 0; i < attribNames.length; i++) {
574      String type = "";
575      switch (m_Instances.attribute(i).type()) {
576      case Attribute.NOMINAL:
577        type = "(Nom) ";
578        break;
579      case Attribute.NUMERIC:
580        type = "(Num) ";
581        break;
582      case Attribute.STRING:
583        type = "(Str) ";
584        break;
585      case Attribute.DATE:
586        type = "(Dat) ";
587        break;
588      case Attribute.RELATIONAL:
589        type = "(Rel) ";
590        break;
591      default:
592        type = "(???) ";
593      }
594      String attnm = m_Instances.attribute(i).name();
595     
596      attribNames[i] = type + attnm;
597    }
598    m_StartBut.setEnabled(m_RunThread == null);
599    m_StopBut.setEnabled(m_RunThread != null);
600    m_ClassCombo.setModel(new DefaultComboBoxModel(attribNames));
601    if (inst.classIndex() == -1)
602      m_ClassCombo.setSelectedIndex(attribNames.length - 1);
603    else
604      m_ClassCombo.setSelectedIndex(inst.classIndex());
605    m_ClassCombo.setEnabled(true);
606  }
607 
608  /**
609   * Starts running the currently configured attribute evaluator and
610   * search method. This is run in a separate thread, and will only start if
611   * there is no attribute selection  already running. The attribute selection
612   * output is sent to the results history panel.
613   */
614  protected void startAttributeSelection() {
615
616    if (m_RunThread == null) {
617      m_StartBut.setEnabled(false);
618      m_StopBut.setEnabled(true);
619      m_RunThread = new Thread() {
620        public void run() {
621          // Copy the current state of things
622          m_Log.statusMessage("Setting up...");
623          Instances inst = new Instances(m_Instances);
624
625          int testMode = 0;
626          int numFolds = 10;
627          int seed = 1;
628          int classIndex = m_ClassCombo.getSelectedIndex();
629          ASEvaluation evaluator = 
630             (ASEvaluation) m_AttributeEvaluatorEditor.getValue();
631
632          ASSearch search = (ASSearch) m_AttributeSearchEditor.getValue();
633
634          StringBuffer outBuff = new StringBuffer();
635          String name = (new SimpleDateFormat("HH:mm:ss - "))
636          .format(new Date());
637          String sname = search.getClass().getName();
638          if (sname.startsWith("weka.attributeSelection.")) {
639            name += sname.substring("weka.attributeSelection.".length());
640          } else {
641            name += sname;
642          }
643          String ename = evaluator.getClass().getName();
644          if (ename.startsWith("weka.attributeSelection.")) {
645            name += (" + "
646                     +ename.substring("weka.attributeSelection.".length()));
647          } else {
648            name += (" + "+ename);
649          }
650         
651          // assemble commands
652          String cmd;
653          String cmdFilter;
654          String cmdClassifier;
655         
656          // 1. attribute selection command
657          Vector<String> list = new Vector<String>();
658          list.add("-s");
659          if (search instanceof OptionHandler)
660            list.add(sname + " " + Utils.joinOptions(((OptionHandler) search).getOptions()));
661          else
662            list.add(sname);
663          if (evaluator instanceof OptionHandler) {
664            String[] opt = ((OptionHandler) evaluator).getOptions();
665            for (int i = 0; i < opt.length; i++)
666              list.add(opt[i]);
667          }
668          cmd =   ename + " " 
669                + Utils.joinOptions(list.toArray(new String[list.size()]));
670
671          // 2. filter command
672          weka.filters.supervised.attribute.AttributeSelection filter = 
673            new weka.filters.supervised.attribute.AttributeSelection();
674          filter.setEvaluator((ASEvaluation) m_AttributeEvaluatorEditor.getValue());
675          filter.setSearch((ASSearch) m_AttributeSearchEditor.getValue());
676          cmdFilter =   filter.getClass().getName() + " " 
677                      + Utils.joinOptions(((OptionHandler) filter).getOptions());
678
679          // 3. meta-classifier command
680          weka.classifiers.meta.AttributeSelectedClassifier cls = 
681            new weka.classifiers.meta.AttributeSelectedClassifier();
682          cls.setEvaluator((ASEvaluation) m_AttributeEvaluatorEditor.getValue());
683          cls.setSearch((ASSearch) m_AttributeSearchEditor.getValue());
684          cmdClassifier =   cls.getClass().getName() + " " 
685                          + Utils.joinOptions(cls.getOptions());
686         
687          AttributeSelection eval = null;
688
689          try {
690            if (m_CVBut.isSelected()) {
691              testMode = 1;
692              numFolds = Integer.parseInt(m_CVText.getText());
693              seed = Integer.parseInt(m_SeedText.getText());
694              if (numFolds <= 1) {
695                throw new Exception("Number of folds must be greater than 1");
696              }
697            } 
698            inst.setClassIndex(classIndex);
699
700            // Output some header information
701            m_Log.logMessage("Started " + ename);
702            m_Log.logMessage("Command: " + cmd);
703            m_Log.logMessage("Filter command: " + cmdFilter);
704            m_Log.logMessage("Meta-classifier command: " + cmdClassifier);
705            if (m_Log instanceof TaskLogger) {
706              ((TaskLogger)m_Log).taskStarted();
707            }
708            outBuff.append("=== Run information ===\n\n");
709            outBuff.append("Evaluator:    " + ename);
710            if (evaluator instanceof OptionHandler) {
711              String [] o = ((OptionHandler) evaluator).getOptions();
712              outBuff.append(" " + Utils.joinOptions(o));
713            }
714            outBuff.append("\nSearch:       " + sname);
715            if (search instanceof OptionHandler) {
716              String [] o = ((OptionHandler) search).getOptions();
717              outBuff.append(" " + Utils.joinOptions(o));
718            }
719            outBuff.append("\n");
720            outBuff.append("Relation:     " + inst.relationName() + '\n');
721            outBuff.append("Instances:    " + inst.numInstances() + '\n');
722            outBuff.append("Attributes:   " + inst.numAttributes() + '\n');
723            if (inst.numAttributes() < 100) {
724              for (int i = 0; i < inst.numAttributes(); i++) {
725                outBuff.append("              " + inst.attribute(i).name()
726                               + '\n');
727              }
728            } else {
729              outBuff.append("              [list of attributes omitted]\n");
730            }
731            outBuff.append("Evaluation mode:    ");
732            switch (testMode) {
733              case 0: // select using all training
734              outBuff.append("evaluate on all training data\n");
735              break;
736              case 1: // CV mode
737              outBuff.append("" + numFolds + "-fold cross-validation\n");
738              break;
739            }
740            outBuff.append("\n");
741            m_History.addResult(name, outBuff);
742            m_History.setSingle(name);
743           
744            // Do the feature selection and output the results.
745            m_Log.statusMessage("Doing feature selection...");
746            m_History.updateResult(name);
747           
748            eval = new AttributeSelection();
749            eval.setEvaluator(evaluator);
750            eval.setSearch(search);
751            eval.setFolds(numFolds);
752            eval.setSeed(seed);
753            if (testMode == 1) {
754              eval.setXval(true);
755            }
756                   
757            switch (testMode) {
758              case 0: // select using training
759              m_Log.statusMessage("Evaluating on training data...");
760              eval.SelectAttributes(inst);
761              break;
762
763              case 1: // CV mode
764              m_Log.statusMessage("Randomizing instances...");
765              Random random = new Random(seed);
766              inst.randomize(random);
767              if (inst.attribute(classIndex).isNominal()) {
768                m_Log.statusMessage("Stratifying instances...");
769                inst.stratify(numFolds);
770              }
771              for (int fold = 0; fold < numFolds;fold++) {
772                m_Log.statusMessage("Creating splits for fold "
773                                    + (fold + 1) + "...");
774                Instances train = inst.trainCV(numFolds, fold, random);
775                m_Log.statusMessage("Selecting attributes using all but fold "
776                                    + (fold + 1) + "...");
777               
778                eval.selectAttributesCVSplit(train);
779              }
780              break;
781              default:
782              throw new Exception("Test mode not implemented");
783            }
784
785            if (testMode == 0) {
786              outBuff.append(eval.toResultsString());
787            } else {
788              outBuff.append(eval.CVResultsString());
789            }
790         
791            outBuff.append("\n");
792            m_History.updateResult(name);
793            m_Log.logMessage("Finished " + ename+" "+sname);
794            m_Log.statusMessage("OK");
795          } catch (Exception ex) {
796            m_Log.logMessage(ex.getMessage());
797            m_Log.statusMessage("See error log");
798          } finally {
799            if (evaluator instanceof AttributeTransformer) {
800              try {
801                Instances transformed = 
802                  ((AttributeTransformer)evaluator).transformedData(inst);
803                transformed.
804                  setRelationName("AT: "+transformed.relationName());
805
806                FastVector vv = new FastVector();
807                vv.addElement(transformed);
808                m_History.addObject(name, vv);
809              } catch (Exception ex) {
810                System.err.println(ex);
811                ex.printStackTrace();
812              }
813            } else if (testMode == 0) {
814              try {
815                Instances reducedInst = eval.reduceDimensionality(inst);
816                FastVector vv = new FastVector();
817                vv.addElement(reducedInst);
818                m_History.addObject(name, vv);
819              } catch (Exception ex) {
820                ex.printStackTrace();
821              }
822            }
823            if (isInterrupted()) {
824              m_Log.logMessage("Interrupted " + ename+" "+sname);
825              m_Log.statusMessage("See error log");
826            }
827            m_RunThread = null;
828            m_StartBut.setEnabled(true);
829            m_StopBut.setEnabled(false);
830            if (m_Log instanceof TaskLogger) {
831              ((TaskLogger)m_Log).taskFinished();
832            }
833          }
834        }
835      };
836      m_RunThread.setPriority(Thread.MIN_PRIORITY);
837      m_RunThread.start();
838    }
839  }
840 
841  /**
842   * Stops the currently running attribute selection (if any).
843   */
844  protected void stopAttributeSelection() {
845
846    if (m_RunThread != null) {
847      m_RunThread.interrupt();
848     
849      // This is deprecated (and theoretically the interrupt should do).
850      m_RunThread.stop();
851     
852    }
853  }
854 
855  /**
856   * Save the named buffer to a file.
857   * @param name the name of the buffer to be saved.
858   */
859  protected void saveBuffer(String name) {
860    StringBuffer sb = m_History.getNamedBuffer(name);
861    if (sb != null) {
862      if (m_SaveOut.save(sb)) {
863        m_Log.logMessage("Save succesful.");
864      }
865    }
866  }
867
868  /**
869   * Popup a visualize panel for viewing transformed data
870   *
871   * @param ti          the Instances to display
872   */
873  protected void visualizeTransformedData(Instances ti) {
874    if (ti != null) {
875      MatrixPanel mp = new MatrixPanel();
876      mp.setInstances(ti);
877      String plotName = ti.relationName();
878      final javax.swing.JFrame jf = 
879        new javax.swing.JFrame("Weka Attribute Selection Visualize: "
880                               +plotName);
881      jf.setSize(800,600);
882      jf.getContentPane().setLayout(new BorderLayout());
883      jf.getContentPane().add(mp, BorderLayout.CENTER);
884      jf.addWindowListener(new java.awt.event.WindowAdapter() {
885          public void windowClosing(java.awt.event.WindowEvent e) {
886            jf.dispose();
887          }
888        });
889
890      jf.setVisible(true);
891    }
892  }
893
894  /**
895   * Popup a SaveDialog for saving the transformed data
896   *
897   * @param ti          the Instances to display
898   */
899  protected void saveTransformedData(Instances ti) {
900    JFileChooser        fc;
901    int                 retVal;
902    BufferedWriter      writer;
903    ExtensionFileFilter filter;
904
905    fc     = new JFileChooser();
906    filter = new ExtensionFileFilter(".arff", "ARFF data files");
907    fc.setFileFilter(filter);
908    retVal = fc.showSaveDialog(this);
909
910    if (retVal == JFileChooser.APPROVE_OPTION) {
911      try {
912        writer = new BufferedWriter(new FileWriter(fc.getSelectedFile()));
913        writer.write(ti.toString());
914        writer.flush();
915        writer.close();
916      }
917      catch (Exception e) {
918        e.printStackTrace();
919        m_Log.logMessage("Problem saving data: " + e.getMessage());
920        JOptionPane.showMessageDialog(
921            this, 
922            "Problem saving data:\n" + e.getMessage(), 
923            "Error", 
924            JOptionPane.ERROR_MESSAGE);
925      }
926    }
927  }
928
929  /**
930   * Handles constructing a popup menu with visualization options
931   * @param name the name of the result history list entry clicked on by
932   * the user.
933   * @param x the x coordinate for popping up the menu
934   * @param y the y coordinate for popping up the menu
935   */
936  protected void visualize(String name, int x, int y) {
937    final String selectedName = name;
938    JPopupMenu resultListMenu = new JPopupMenu();
939
940    JMenuItem visMainBuffer = new JMenuItem("View in main window");
941    if (selectedName != null) {
942      visMainBuffer.addActionListener(new ActionListener() {
943          public void actionPerformed(ActionEvent e) {
944            m_History.setSingle(selectedName);
945          }
946        });
947    } else {
948      visMainBuffer.setEnabled(false);
949    }
950    resultListMenu.add(visMainBuffer);
951
952    JMenuItem visSepBuffer = new JMenuItem("View in separate window");
953    if (selectedName != null) {
954    visSepBuffer.addActionListener(new ActionListener() {
955        public void actionPerformed(ActionEvent e) {
956          m_History.openFrame(selectedName);
957        }
958      });
959    } else {
960      visSepBuffer.setEnabled(false);
961    }
962    resultListMenu.add(visSepBuffer);
963
964    JMenuItem saveOutput = new JMenuItem("Save result buffer");
965    if (selectedName != null) {
966    saveOutput.addActionListener(new ActionListener() {
967        public void actionPerformed(ActionEvent e) {
968          saveBuffer(selectedName);
969        }
970      });
971    } else {
972      saveOutput.setEnabled(false);
973    }
974    resultListMenu.add(saveOutput);
975   
976    JMenuItem deleteOutput = new JMenuItem("Delete result buffer");
977    if (selectedName != null) {
978      deleteOutput.addActionListener(new ActionListener() {
979        public void actionPerformed(ActionEvent e) {
980          m_History.removeResult(selectedName);
981        }
982      });
983    } else {
984      deleteOutput.setEnabled(false);
985    }
986    resultListMenu.add(deleteOutput);
987
988
989    FastVector o = null;
990    if (selectedName != null) {
991      o = (FastVector)m_History.getNamedObject(selectedName);
992    }   
993
994    //    VisualizePanel temp_vp = null;
995    Instances tempTransformed = null;
996
997    if (o != null) {
998      for (int i = 0; i < o.size(); i++) {
999        Object temp = o.elementAt(i);
1000        //      if (temp instanceof VisualizePanel) {
1001        if (temp instanceof Instances) {
1002          //      temp_vp = (VisualizePanel)temp;
1003          tempTransformed = (Instances) temp;
1004        } 
1005      }
1006    }
1007   
1008    //    final VisualizePanel vp = temp_vp;
1009    final Instances ti = tempTransformed;
1010    JMenuItem visTrans = null;
1011   
1012    if (ti != null) {
1013      if (ti.relationName().startsWith("AT:")) {
1014        visTrans = new JMenuItem("Visualize transformed data");
1015      } else {
1016        visTrans = new JMenuItem("Visualize reduced data");
1017      }
1018      resultListMenu.addSeparator();
1019    }
1020
1021    //    JMenuItem visTrans = new JMenuItem("Visualize transformed data");
1022    if (ti != null && visTrans != null) {
1023      visTrans.addActionListener(new ActionListener() {
1024          public void actionPerformed(ActionEvent e) {
1025            visualizeTransformedData(ti);
1026          }
1027        });
1028    }
1029     
1030    if (visTrans != null) {
1031      resultListMenu.add(visTrans);
1032    }
1033   
1034    JMenuItem saveTrans = null;
1035    if (ti != null) {
1036      if (ti.relationName().startsWith("AT:"))
1037        saveTrans = new JMenuItem("Save transformed data...");
1038      else
1039        saveTrans = new JMenuItem("Save reduced data...");
1040    }
1041    if (saveTrans != null) {
1042      saveTrans.addActionListener(new ActionListener() {
1043          public void actionPerformed(ActionEvent e) {
1044            saveTransformedData(ti);
1045          }
1046      });
1047      resultListMenu.add(saveTrans);
1048    }
1049   
1050    resultListMenu.show(m_History.getList(), x, y);
1051  }
1052 
1053  /**
1054   * updates the capabilities filter of the GOE
1055   *
1056   * @param filter      the new filter to use
1057   */
1058  protected void updateCapabilitiesFilter(Capabilities filter) {
1059    Instances           tempInst;
1060    Capabilities        filterClass;
1061
1062    if (filter == null) {
1063      m_AttributeEvaluatorEditor.setCapabilitiesFilter(new Capabilities(null));
1064      m_AttributeSearchEditor.setCapabilitiesFilter(new Capabilities(null));
1065      return;
1066    }
1067   
1068    if (!ExplorerDefaults.getInitGenericObjectEditorFilter())
1069      tempInst = new Instances(m_Instances, 0);
1070    else
1071      tempInst = new Instances(m_Instances);
1072    tempInst.setClassIndex(m_ClassCombo.getSelectedIndex());
1073
1074    try {
1075      filterClass = Capabilities.forInstances(tempInst);
1076    }
1077    catch (Exception e) {
1078      filterClass = new Capabilities(null);
1079    }
1080   
1081    // set new filter
1082    m_AttributeEvaluatorEditor.setCapabilitiesFilter(filterClass);
1083    m_AttributeSearchEditor.setCapabilitiesFilter(filterClass);
1084   
1085    m_StartBut.setEnabled(true);
1086    // check capabilities...
1087    Capabilities currentFilter = m_AttributeEvaluatorEditor.getCapabilitiesFilter();
1088    ASEvaluation evaluator = (ASEvaluation) m_AttributeEvaluatorEditor.getValue();
1089    Capabilities currentSchemeCapabilities =  null;
1090    if (evaluator != null && currentFilter != null && 
1091        (evaluator instanceof CapabilitiesHandler)) {
1092      currentSchemeCapabilities = ((CapabilitiesHandler)evaluator).getCapabilities();
1093     
1094      if (!currentSchemeCapabilities.supportsMaybe(currentFilter) &&
1095          !currentSchemeCapabilities.supports(currentFilter)) {
1096        m_StartBut.setEnabled(false);
1097      }
1098    }
1099  }
1100 
1101  /**
1102   * method gets called in case of a change event
1103   *
1104   * @param e           the associated change event
1105   */
1106  public void capabilitiesFilterChanged(CapabilitiesFilterChangeEvent e) {
1107    if (e.getFilter() == null)
1108      updateCapabilitiesFilter(null);
1109    else
1110      updateCapabilitiesFilter((Capabilities) e.getFilter().clone());
1111  }
1112
1113  /**
1114   * Sets the Explorer to use as parent frame (used for sending notifications
1115   * about changes in the data)
1116   *
1117   * @param parent      the parent frame
1118   */
1119  public void setExplorer(Explorer parent) {
1120    m_Explorer = parent;
1121  }
1122 
1123  /**
1124   * returns the parent Explorer frame
1125   *
1126   * @return            the parent
1127   */
1128  public Explorer getExplorer() {
1129    return m_Explorer;
1130  }
1131 
1132  /**
1133   * Returns the title for the tab in the Explorer
1134   *
1135   * @return            the title of this tab
1136   */
1137  public String getTabTitle() {
1138    return "Select attributes";
1139  }
1140 
1141  /**
1142   * Returns the tooltip for the tab in the Explorer
1143   *
1144   * @return            the tooltip of this tab
1145   */
1146  public String getTabTitleToolTip() {
1147    return "Determine relevance of attributes";
1148  }
1149
1150  /**
1151   * Tests out the attribute selection panel from the command line.
1152   *
1153   * @param args may optionally contain the name of a dataset to load.
1154   */
1155  public static void main(String [] args) {
1156
1157    try {
1158      final javax.swing.JFrame jf =
1159        new javax.swing.JFrame("Weka Explorer: Select attributes");
1160      jf.getContentPane().setLayout(new BorderLayout());
1161      final AttributeSelectionPanel sp = new AttributeSelectionPanel();
1162      jf.getContentPane().add(sp, BorderLayout.CENTER);
1163      weka.gui.LogPanel lp = new weka.gui.LogPanel();
1164      sp.setLog(lp);
1165      jf.getContentPane().add(lp, BorderLayout.SOUTH);
1166      jf.addWindowListener(new java.awt.event.WindowAdapter() {
1167        public void windowClosing(java.awt.event.WindowEvent e) {
1168          jf.dispose();
1169          System.exit(0);
1170        }
1171      });
1172      jf.pack();
1173      jf.setVisible(true);
1174      if (args.length == 1) {
1175        System.err.println("Loading instances from " + args[0]);
1176        java.io.Reader r = new java.io.BufferedReader(
1177                           new java.io.FileReader(args[0]));
1178        Instances i = new Instances(r);
1179        sp.setInstances(i);
1180      }
1181    } catch (Exception ex) {
1182      ex.printStackTrace();
1183      System.err.println(ex.getMessage());
1184    }
1185  }
1186}
Note: See TracBrowser for help on using the repository browser.