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 | * BoundaryVisualizer.java |
---|
19 | * Copyright (C) 2002 University of Waikato, Hamilton, New Zealand |
---|
20 | * |
---|
21 | */ |
---|
22 | |
---|
23 | package weka.gui.boundaryvisualizer; |
---|
24 | |
---|
25 | import weka.classifiers.Classifier; |
---|
26 | import weka.classifiers.AbstractClassifier; |
---|
27 | import weka.core.Attribute; |
---|
28 | import weka.core.FastVector; |
---|
29 | import weka.core.Instances; |
---|
30 | import weka.core.DenseInstance; |
---|
31 | import weka.core.TechnicalInformation; |
---|
32 | import weka.core.TechnicalInformationHandler; |
---|
33 | import weka.core.Utils; |
---|
34 | import weka.core.TechnicalInformation.Field; |
---|
35 | import weka.core.TechnicalInformation.Type; |
---|
36 | import weka.gui.ExtensionFileFilter; |
---|
37 | import weka.gui.GenericObjectEditor; |
---|
38 | import weka.gui.PropertyPanel; |
---|
39 | import weka.gui.visualize.ClassPanel; |
---|
40 | |
---|
41 | import java.awt.BorderLayout; |
---|
42 | import java.awt.Color; |
---|
43 | import java.awt.Dimension; |
---|
44 | import java.awt.Font; |
---|
45 | import java.awt.FontMetrics; |
---|
46 | import java.awt.Graphics; |
---|
47 | import java.awt.GridBagConstraints; |
---|
48 | import java.awt.GridBagLayout; |
---|
49 | import java.awt.GridLayout; |
---|
50 | import java.awt.event.ActionEvent; |
---|
51 | import java.awt.event.ActionListener; |
---|
52 | import java.awt.event.ItemEvent; |
---|
53 | import java.awt.event.ItemListener; |
---|
54 | import java.awt.event.MouseAdapter; |
---|
55 | import java.awt.event.MouseEvent; |
---|
56 | import java.beans.PropertyChangeEvent; |
---|
57 | import java.beans.PropertyChangeListener; |
---|
58 | import java.io.File; |
---|
59 | import java.io.FileOutputStream; |
---|
60 | import java.io.ObjectOutputStream; |
---|
61 | import java.util.Vector; |
---|
62 | |
---|
63 | import javax.swing.BorderFactory; |
---|
64 | import javax.swing.BoxLayout; |
---|
65 | import javax.swing.ButtonGroup; |
---|
66 | import javax.swing.DefaultComboBoxModel; |
---|
67 | import javax.swing.JButton; |
---|
68 | import javax.swing.JCheckBox; |
---|
69 | import javax.swing.JComboBox; |
---|
70 | import javax.swing.JFileChooser; |
---|
71 | import javax.swing.JLabel; |
---|
72 | import javax.swing.JOptionPane; |
---|
73 | import javax.swing.JPanel; |
---|
74 | import javax.swing.JRadioButton; |
---|
75 | import javax.swing.JTextField; |
---|
76 | |
---|
77 | /** |
---|
78 | * BoundaryVisualizer. Allows the visualization of classifier decision |
---|
79 | * boundaries in two dimensions. A supplied classifier is first |
---|
80 | * trained on supplied training data, then a data generator (currently |
---|
81 | * using kernels) is used to generate new instances at points fixed in |
---|
82 | * the two visualization dimensions but random in the other |
---|
83 | * dimensions. These instances are classified by the classifier and |
---|
84 | * plotted as points with colour corresponding to the probability |
---|
85 | * distribution predicted by the classifier. At present, 2 * 2^(# |
---|
86 | * non-fixed dimensions) points are generated from each kernel per |
---|
87 | * pixel in the display. In practice, fewer points than this are |
---|
88 | * actually classified because kernels are weighted (on a per-pixel |
---|
89 | * basis) according to the fixexd dimensions and kernels corresponding |
---|
90 | * to the lowest 1% of the weight mass are discarded. Predicted |
---|
91 | * probability distributions are weighted (acording to the fixed |
---|
92 | * visualization dimensions) and averaged to produce an RGB value for |
---|
93 | * the pixel. For more information, see<p> |
---|
94 | * |
---|
95 | * Eibe Frank and Mark Hall (2003). Visualizing Class Probability |
---|
96 | * Estimators. Working Paper 02/03, Department of Computer Science, |
---|
97 | * University of Waikato. |
---|
98 | * |
---|
99 | * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a> |
---|
100 | * @version $Revision: 5987 $ |
---|
101 | * @since 1.0 |
---|
102 | * @see JPanel |
---|
103 | */ |
---|
104 | public class BoundaryVisualizer |
---|
105 | extends JPanel implements TechnicalInformationHandler { |
---|
106 | |
---|
107 | /** for serialization */ |
---|
108 | private static final long serialVersionUID = 3933877580074013208L; |
---|
109 | |
---|
110 | /** |
---|
111 | * Inner class to handle rendering the axis |
---|
112 | * |
---|
113 | * @author <a href="mailto:mhall@cs.waikato.ac.nz">Mark Hall</a> |
---|
114 | * @version $Revision: 5987 $ |
---|
115 | * @since 1.0 |
---|
116 | * @see JPanel |
---|
117 | */ |
---|
118 | private class AxisPanel |
---|
119 | extends JPanel { |
---|
120 | |
---|
121 | /** for serialization */ |
---|
122 | private static final long serialVersionUID = -7421022416674492712L; |
---|
123 | |
---|
124 | private static final int MAX_PRECISION = 10; |
---|
125 | private boolean m_vertical = false; |
---|
126 | private final int PAD = 5; |
---|
127 | private FontMetrics m_fontMetrics; |
---|
128 | private int m_fontHeight; |
---|
129 | |
---|
130 | public AxisPanel(boolean vertical) { |
---|
131 | m_vertical = vertical; |
---|
132 | this.setBackground(Color.black); |
---|
133 | // Graphics g = this.getGraphics(); |
---|
134 | String fontFamily = this.getFont().getFamily(); |
---|
135 | Font newFont = new Font(fontFamily, Font.PLAIN, 10); |
---|
136 | this.setFont(newFont); |
---|
137 | } |
---|
138 | |
---|
139 | public Dimension getPreferredSize() { |
---|
140 | if (m_fontMetrics == null) { |
---|
141 | Graphics g = this.getGraphics(); |
---|
142 | m_fontMetrics = g.getFontMetrics(); |
---|
143 | m_fontHeight = m_fontMetrics.getHeight(); |
---|
144 | } |
---|
145 | if (!m_vertical) { |
---|
146 | return new Dimension(this.getSize().width, PAD+2+m_fontHeight); |
---|
147 | } |
---|
148 | return new Dimension(50, this.getSize().height); |
---|
149 | } |
---|
150 | |
---|
151 | public void paintComponent(Graphics g) { |
---|
152 | super.paintComponent(g); |
---|
153 | this.setBackground(Color.black); |
---|
154 | if (m_fontMetrics == null) { |
---|
155 | m_fontMetrics = g.getFontMetrics(); |
---|
156 | m_fontHeight = m_fontMetrics.getHeight(); |
---|
157 | } |
---|
158 | |
---|
159 | Dimension d = this.getSize(); |
---|
160 | Dimension d2 = m_boundaryPanel.getSize(); |
---|
161 | g.setColor(Color.gray); |
---|
162 | int hf = m_fontMetrics.getAscent(); |
---|
163 | if (!m_vertical) { |
---|
164 | g.drawLine(d.width, PAD, d.width-d2.width, PAD); |
---|
165 | // try and draw some scale values |
---|
166 | if (getInstances() != null) { |
---|
167 | int precisionXmax = 1; |
---|
168 | int precisionXmin = 1; |
---|
169 | int whole = (int)Math.abs(m_maxX); |
---|
170 | double decimal = Math.abs(m_maxX) - whole; |
---|
171 | int nondecimal; |
---|
172 | nondecimal = (whole > 0) |
---|
173 | ? (int)(Math.log(whole) / Math.log(10)) |
---|
174 | : 1; |
---|
175 | |
---|
176 | precisionXmax = (decimal > 0) |
---|
177 | ? (int)Math.abs(((Math.log(Math.abs(m_maxX)) / |
---|
178 | Math.log(10))))+2 |
---|
179 | : 1; |
---|
180 | if (precisionXmax > MAX_PRECISION) { |
---|
181 | precisionXmax = 1; |
---|
182 | } |
---|
183 | String maxStringX = Utils.doubleToString(m_maxX, |
---|
184 | nondecimal+1+precisionXmax |
---|
185 | ,precisionXmax); |
---|
186 | |
---|
187 | whole = (int)Math.abs(m_minX); |
---|
188 | decimal = Math.abs(m_minX) - whole; |
---|
189 | nondecimal = (whole > 0) |
---|
190 | ? (int)(Math.log(whole) / Math.log(10)) |
---|
191 | : 1; |
---|
192 | precisionXmin = (decimal > 0) |
---|
193 | ? (int)Math.abs(((Math.log(Math.abs(m_minX)) / |
---|
194 | Math.log(10))))+2 |
---|
195 | : 1; |
---|
196 | if (precisionXmin > MAX_PRECISION) { |
---|
197 | precisionXmin = 1; |
---|
198 | } |
---|
199 | |
---|
200 | String minStringX = Utils.doubleToString(m_minX, |
---|
201 | nondecimal+1+precisionXmin, |
---|
202 | precisionXmin); |
---|
203 | g.drawString(minStringX, d.width-d2.width, PAD+hf+2); |
---|
204 | int maxWidth = m_fontMetrics.stringWidth(maxStringX); |
---|
205 | g.drawString(maxStringX, d.width-maxWidth, PAD+hf+2); |
---|
206 | } |
---|
207 | } else { |
---|
208 | g.drawLine(d.width-PAD, 0, d.width-PAD, d2.height); |
---|
209 | // try and draw some scale values |
---|
210 | if (getInstances() != null) { |
---|
211 | int precisionYmax = 1; |
---|
212 | int precisionYmin = 1; |
---|
213 | int whole = (int)Math.abs(m_maxY); |
---|
214 | double decimal = Math.abs(m_maxY) - whole; |
---|
215 | int nondecimal; |
---|
216 | nondecimal = (whole > 0) |
---|
217 | ? (int)(Math.log(whole) / Math.log(10)) |
---|
218 | : 1; |
---|
219 | |
---|
220 | precisionYmax = (decimal > 0) |
---|
221 | ? (int)Math.abs(((Math.log(Math.abs(m_maxY)) / |
---|
222 | Math.log(10))))+2 |
---|
223 | : 1; |
---|
224 | if (precisionYmax > MAX_PRECISION) { |
---|
225 | precisionYmax = 1; |
---|
226 | } |
---|
227 | String maxStringY = Utils.doubleToString(m_maxY, |
---|
228 | nondecimal+1+precisionYmax |
---|
229 | ,precisionYmax); |
---|
230 | |
---|
231 | whole = (int)Math.abs(m_minY); |
---|
232 | decimal = Math.abs(m_minY) - whole; |
---|
233 | nondecimal = (whole > 0) |
---|
234 | ? (int)(Math.log(whole) / Math.log(10)) |
---|
235 | : 1; |
---|
236 | precisionYmin = (decimal > 0) |
---|
237 | ? (int)Math.abs(((Math.log(Math.abs(m_minY)) / |
---|
238 | Math.log(10))))+2 |
---|
239 | : 1; |
---|
240 | if (precisionYmin > MAX_PRECISION) { |
---|
241 | precisionYmin = 1; |
---|
242 | } |
---|
243 | |
---|
244 | String minStringY = Utils.doubleToString(m_minY, |
---|
245 | nondecimal+1+precisionYmin, |
---|
246 | precisionYmin); |
---|
247 | int maxWidth = m_fontMetrics.stringWidth(minStringY); |
---|
248 | g.drawString(minStringY, d.width-PAD-maxWidth-2, d2.height); |
---|
249 | maxWidth = m_fontMetrics.stringWidth(maxStringY); |
---|
250 | g.drawString(maxStringY, d.width-PAD-maxWidth-2, hf); |
---|
251 | } |
---|
252 | } |
---|
253 | } |
---|
254 | } |
---|
255 | |
---|
256 | /** the number of visualizer windows we have open. */ |
---|
257 | protected static int m_WindowCount = 0; |
---|
258 | |
---|
259 | /** whether the exit if there are no more windows open */ |
---|
260 | protected static boolean m_ExitIfNoWindowsOpen = true; |
---|
261 | |
---|
262 | /** the training instances */ |
---|
263 | private Instances m_trainingInstances; |
---|
264 | |
---|
265 | /** the classifier to use */ |
---|
266 | private Classifier m_classifier; |
---|
267 | |
---|
268 | // plot area dimensions |
---|
269 | protected int m_plotAreaWidth = 384; |
---|
270 | //protected int m_plotAreaHeight = 384; |
---|
271 | protected int m_plotAreaHeight = 384; |
---|
272 | |
---|
273 | /** the plotting panel */ |
---|
274 | protected BoundaryPanel m_boundaryPanel; |
---|
275 | |
---|
276 | // combo boxes for selecting the class attribute, class values (for |
---|
277 | // colouring pixels), and visualization attributes |
---|
278 | protected JComboBox m_classAttBox = new JComboBox(); |
---|
279 | protected JComboBox m_xAttBox = new JComboBox(); |
---|
280 | protected JComboBox m_yAttBox = new JComboBox(); |
---|
281 | |
---|
282 | protected Dimension COMBO_SIZE = |
---|
283 | new Dimension((int)(m_plotAreaWidth * 0.75), |
---|
284 | m_classAttBox.getPreferredSize().height); |
---|
285 | |
---|
286 | protected JButton m_startBut = new JButton("Start"); |
---|
287 | |
---|
288 | protected JCheckBox m_plotTrainingData = new JCheckBox("Plot training data"); |
---|
289 | |
---|
290 | protected JPanel m_controlPanel; |
---|
291 | |
---|
292 | protected ClassPanel m_classPanel = new ClassPanel(); |
---|
293 | |
---|
294 | // separate panels for rendering axis information |
---|
295 | private AxisPanel m_xAxisPanel; |
---|
296 | private AxisPanel m_yAxisPanel; |
---|
297 | |
---|
298 | // min and max values for visualization dimensions |
---|
299 | private double m_maxX; |
---|
300 | private double m_maxY; |
---|
301 | private double m_minX; |
---|
302 | private double m_minY; |
---|
303 | |
---|
304 | private int m_xIndex; |
---|
305 | private int m_yIndex; |
---|
306 | |
---|
307 | /* Kernel density estimator/generator */ |
---|
308 | private KDDataGenerator m_dataGenerator; |
---|
309 | |
---|
310 | /* number of samples per pixel (fixed dimensions only) */ |
---|
311 | private int m_numberOfSamplesFromEachRegion; |
---|
312 | |
---|
313 | /** base for sampling in the non-fixed dimensions */ |
---|
314 | private int m_generatorSamplesBase; |
---|
315 | |
---|
316 | /** Set the kernel bandwidth to cover this many nearest neighbours */ |
---|
317 | private int m_kernelBandwidth; |
---|
318 | |
---|
319 | private JTextField m_regionSamplesText = |
---|
320 | new JTextField(""+0); |
---|
321 | |
---|
322 | private JTextField m_generatorSamplesText = |
---|
323 | new JTextField(""+0); |
---|
324 | |
---|
325 | private JTextField m_kernelBandwidthText = |
---|
326 | new JTextField(""+3+" "); |
---|
327 | |
---|
328 | //jimmy |
---|
329 | protected GenericObjectEditor m_classifierEditor = new GenericObjectEditor(); //the widget to select the classifier |
---|
330 | protected PropertyPanel m_ClassifierPanel = new PropertyPanel(m_classifierEditor); |
---|
331 | /** The file chooser for selecting arff files */ |
---|
332 | protected JFileChooser m_FileChooser |
---|
333 | = new JFileChooser(new File(System.getProperty("user.dir"))); |
---|
334 | protected ExtensionFileFilter m_arffFileFilter = |
---|
335 | new ExtensionFileFilter(Instances.FILE_EXTENSION, |
---|
336 | "Arff data files"); |
---|
337 | protected JLabel dataFileLabel = new JLabel(); //stores the name of the data file (currently stores relation name rather than filename) |
---|
338 | protected JPanel m_addRemovePointsPanel = new JPanel(); //a panel which contains the controls to add and remove points |
---|
339 | protected JComboBox m_classValueSelector = new JComboBox(); //a widget to select the class attribute. |
---|
340 | protected JRadioButton m_addPointsButton = new JRadioButton(); //when this is selected, clicking on the BoundaryPanel will add points. |
---|
341 | protected JRadioButton m_removePointsButton = new JRadioButton(); //when this is selected, clicking on the BoundaryPanel will remove points. |
---|
342 | protected ButtonGroup m_addRemovePointsButtonGroup = new ButtonGroup(); |
---|
343 | protected JButton removeAllButton = new JButton ("Remove all"); //button to remove all points |
---|
344 | protected JButton chooseButton = new JButton("Open File"); //button to choose a data file |
---|
345 | |
---|
346 | /* Register the property editors we need */ |
---|
347 | static { |
---|
348 | GenericObjectEditor.registerEditors(); |
---|
349 | } |
---|
350 | |
---|
351 | /** |
---|
352 | * Returns a string describing this tool |
---|
353 | * @return a description of the tool suitable for |
---|
354 | * displaying in various Weka GUIs |
---|
355 | */ |
---|
356 | public String globalInfo() { |
---|
357 | return "Class for visualizing class probability estimates.\n\n" |
---|
358 | + "For more information, see\n\n" |
---|
359 | + getTechnicalInformation().toString(); |
---|
360 | } |
---|
361 | |
---|
362 | /** |
---|
363 | * Returns an instance of a TechnicalInformation object, containing |
---|
364 | * detailed information about the technical background of this class, |
---|
365 | * e.g., paper reference or book this class is based on. |
---|
366 | * |
---|
367 | * @return the technical information about this class |
---|
368 | */ |
---|
369 | public TechnicalInformation getTechnicalInformation() { |
---|
370 | TechnicalInformation result; |
---|
371 | |
---|
372 | result = new TechnicalInformation(Type.INPROCEEDINGS); |
---|
373 | result.setValue(Field.AUTHOR, "Eibe Frank and Mark Hall"); |
---|
374 | result.setValue(Field.TITLE, "Visualizing class probability estimators"); |
---|
375 | result.setValue(Field.BOOKTITLE, "European Conference on Principles and Practice of " + |
---|
376 | "Knowledge Discovery in Databases"); |
---|
377 | result.setValue(Field.YEAR, "2003"); |
---|
378 | result.setValue(Field.PAGES, "168-169"); |
---|
379 | result.setValue(Field.PUBLISHER, "Springer-Verlag"); |
---|
380 | result.setValue(Field.ADDRESS, "Cavtat-Dubrovnik"); |
---|
381 | |
---|
382 | return result; |
---|
383 | } |
---|
384 | |
---|
385 | |
---|
386 | /** |
---|
387 | * Creates a new <code>BoundaryVisualizer</code> instance. |
---|
388 | */ |
---|
389 | public BoundaryVisualizer() { |
---|
390 | |
---|
391 | setLayout(new BorderLayout()); |
---|
392 | m_classAttBox.setMinimumSize(COMBO_SIZE); |
---|
393 | m_classAttBox.setPreferredSize(COMBO_SIZE); |
---|
394 | m_classAttBox.setMaximumSize(COMBO_SIZE); |
---|
395 | m_classAttBox.addActionListener(new ActionListener() { |
---|
396 | public void actionPerformed(ActionEvent e) { |
---|
397 | if (m_classAttBox.getItemCount() != 0) |
---|
398 | { |
---|
399 | try { |
---|
400 | m_classPanel.setCindex(m_classAttBox.getSelectedIndex()); |
---|
401 | plotTrainingData(); |
---|
402 | System.err.println("Here in class att box listener"); |
---|
403 | } catch (Exception ex) {ex.printStackTrace();} |
---|
404 | |
---|
405 | //set up the add points selector combo box. -jimmy |
---|
406 | setUpClassValueSelectorCB(); |
---|
407 | } |
---|
408 | } |
---|
409 | }); |
---|
410 | |
---|
411 | |
---|
412 | m_xAttBox.setMinimumSize(COMBO_SIZE); |
---|
413 | m_xAttBox.setPreferredSize(COMBO_SIZE); |
---|
414 | m_xAttBox.setMaximumSize(COMBO_SIZE); |
---|
415 | |
---|
416 | m_yAttBox.setMinimumSize(COMBO_SIZE); |
---|
417 | m_yAttBox.setPreferredSize(COMBO_SIZE); |
---|
418 | m_yAttBox.setMaximumSize(COMBO_SIZE); |
---|
419 | |
---|
420 | m_classPanel.setMinimumSize(new |
---|
421 | Dimension((int)COMBO_SIZE.getWidth()*2, |
---|
422 | (int)COMBO_SIZE.getHeight()*2)); |
---|
423 | m_classPanel.setPreferredSize(new |
---|
424 | Dimension((int)COMBO_SIZE.getWidth()*2, |
---|
425 | (int)COMBO_SIZE.getHeight()*2)); |
---|
426 | |
---|
427 | |
---|
428 | m_controlPanel = new JPanel(); |
---|
429 | m_controlPanel.setLayout(new BorderLayout()); |
---|
430 | |
---|
431 | //jimmy |
---|
432 | JPanel dataChooseHolder = new JPanel(new BorderLayout()); |
---|
433 | dataChooseHolder.setBorder(BorderFactory.createTitledBorder("Dataset")); |
---|
434 | dataChooseHolder.add(dataFileLabel, BorderLayout.WEST); |
---|
435 | |
---|
436 | m_FileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); |
---|
437 | m_FileChooser.addChoosableFileFilter(m_arffFileFilter); |
---|
438 | chooseButton.addActionListener(new ActionListener() { |
---|
439 | public void actionPerformed(ActionEvent e) { |
---|
440 | try { |
---|
441 | setInstancesFromFileQ(); |
---|
442 | int classIndex = m_classAttBox.getSelectedIndex(); |
---|
443 | if (m_trainingInstances != null && m_classifier != null && (m_trainingInstances.attribute(classIndex).isNominal())) { |
---|
444 | m_startBut.setEnabled(true); |
---|
445 | // plotTrainingData(); |
---|
446 | } |
---|
447 | |
---|
448 | |
---|
449 | } catch (Exception ex) { |
---|
450 | ex.printStackTrace(System.out); |
---|
451 | System.err.println("exception"); |
---|
452 | } |
---|
453 | |
---|
454 | } |
---|
455 | }); |
---|
456 | dataChooseHolder.add(chooseButton, BorderLayout.EAST); |
---|
457 | |
---|
458 | JPanel classifierHolder = new JPanel(); |
---|
459 | classifierHolder.setBorder(BorderFactory.createTitledBorder("Classifier")); |
---|
460 | classifierHolder.setLayout(new BorderLayout()); |
---|
461 | m_classifierEditor.setClassType(weka.classifiers.Classifier.class); |
---|
462 | |
---|
463 | m_classifierEditor.addPropertyChangeListener(new PropertyChangeListener() { |
---|
464 | public void propertyChange(PropertyChangeEvent evt) { |
---|
465 | m_classifier = (Classifier)m_classifierEditor.getValue(); |
---|
466 | try { |
---|
467 | int classIndex = m_classAttBox.getSelectedIndex(); |
---|
468 | if (m_trainingInstances != null && m_classifier != null && (m_trainingInstances.attribute(classIndex).isNominal())) { |
---|
469 | m_startBut.setEnabled(true); |
---|
470 | } |
---|
471 | } catch (Exception ex) {}; |
---|
472 | } |
---|
473 | }); |
---|
474 | classifierHolder.add(m_ClassifierPanel, BorderLayout.CENTER); |
---|
475 | |
---|
476 | |
---|
477 | |
---|
478 | JPanel cHolder = new JPanel(); |
---|
479 | cHolder.setBorder(BorderFactory.createTitledBorder("Class Attribute")); |
---|
480 | cHolder.add(m_classAttBox); |
---|
481 | |
---|
482 | JPanel vAttHolder = new JPanel(); |
---|
483 | vAttHolder.setLayout(new GridLayout(2,1)); |
---|
484 | vAttHolder.setBorder(BorderFactory. |
---|
485 | createTitledBorder("Visualization Attributes")); |
---|
486 | vAttHolder.add(m_xAttBox); |
---|
487 | vAttHolder.add(m_yAttBox); |
---|
488 | |
---|
489 | JPanel colOne = new JPanel(); |
---|
490 | colOne.setLayout(new BorderLayout()); |
---|
491 | colOne.add(dataChooseHolder, BorderLayout.NORTH); //jimmy |
---|
492 | colOne.add(cHolder, BorderLayout.CENTER); |
---|
493 | //colOne.add(vAttHolder, BorderLayout.SOUTH); |
---|
494 | |
---|
495 | JPanel tempPanel = new JPanel(); |
---|
496 | tempPanel.setBorder(BorderFactory. |
---|
497 | createTitledBorder("Sampling control")); |
---|
498 | tempPanel.setLayout(new GridLayout(3,1)); |
---|
499 | |
---|
500 | JPanel colTwo = new JPanel(); |
---|
501 | colTwo.setLayout(new BorderLayout()); |
---|
502 | JPanel gsP = new JPanel(); gsP.setLayout(new BorderLayout()); |
---|
503 | gsP.add(new JLabel(" Base for sampling (r)"), BorderLayout.CENTER); |
---|
504 | gsP.add(m_generatorSamplesText, BorderLayout.WEST); |
---|
505 | tempPanel.add(gsP); |
---|
506 | |
---|
507 | JPanel rsP = new JPanel(); rsP.setLayout(new BorderLayout()); |
---|
508 | rsP.add(new JLabel(" Num. locations per pixel"), BorderLayout.CENTER); |
---|
509 | rsP.add(m_regionSamplesText, BorderLayout.WEST); |
---|
510 | tempPanel.add(rsP); |
---|
511 | |
---|
512 | JPanel ksP = new JPanel(); ksP.setLayout(new BorderLayout()); |
---|
513 | ksP.add(new JLabel(" Kernel bandwidth (k)"), BorderLayout.CENTER); |
---|
514 | ksP.add(m_kernelBandwidthText, BorderLayout.WEST); |
---|
515 | tempPanel.add(ksP); |
---|
516 | |
---|
517 | colTwo.add(classifierHolder,BorderLayout.NORTH);//jimmy |
---|
518 | //colTwo.add(tempPanel, BorderLayout.CENTER); |
---|
519 | colTwo.add(vAttHolder, BorderLayout.CENTER); |
---|
520 | |
---|
521 | JPanel startPanel = new JPanel(); |
---|
522 | startPanel.setBorder(BorderFactory. |
---|
523 | createTitledBorder("Plotting")); |
---|
524 | startPanel.setLayout(new BorderLayout()); |
---|
525 | startPanel.add(m_startBut, BorderLayout.CENTER); |
---|
526 | startPanel.add(m_plotTrainingData, BorderLayout.WEST); |
---|
527 | |
---|
528 | //colTwo.add(startPanel, BorderLayout.SOUTH); |
---|
529 | |
---|
530 | m_controlPanel.add(colOne, BorderLayout.WEST); |
---|
531 | m_controlPanel.add(colTwo, BorderLayout.CENTER); |
---|
532 | JPanel classHolder = new JPanel(); |
---|
533 | classHolder.setLayout(new BorderLayout()); //jimmy |
---|
534 | classHolder.setBorder(BorderFactory.createTitledBorder("Class color")); |
---|
535 | classHolder.add(m_classPanel, BorderLayout.CENTER); |
---|
536 | m_controlPanel.add(classHolder, BorderLayout.SOUTH); |
---|
537 | |
---|
538 | JPanel aboutAndControlP = new JPanel(); |
---|
539 | aboutAndControlP.setLayout(new BorderLayout()); |
---|
540 | aboutAndControlP.add(m_controlPanel, BorderLayout.SOUTH); |
---|
541 | |
---|
542 | weka.gui.PropertySheetPanel psp = new weka.gui.PropertySheetPanel(); |
---|
543 | psp.setTarget(BoundaryVisualizer.this); |
---|
544 | JPanel aboutPanel = psp.getAboutPanel(); |
---|
545 | |
---|
546 | aboutAndControlP.add(aboutPanel, BorderLayout.NORTH); |
---|
547 | |
---|
548 | add(aboutAndControlP, BorderLayout.NORTH); |
---|
549 | |
---|
550 | //classHolder.add(newWindowButton, BorderLayout.EAST); |
---|
551 | |
---|
552 | // set up the add-remove points widgets |
---|
553 | m_addRemovePointsPanel.setBorder(BorderFactory.createTitledBorder("Add / remove data points")); |
---|
554 | m_addRemovePointsPanel.setLayout(new GridBagLayout()); |
---|
555 | GridBagConstraints constraints = new GridBagConstraints(); |
---|
556 | constraints.weightx = 1.0; |
---|
557 | constraints.weighty = 1.0; |
---|
558 | constraints.gridx = 0; |
---|
559 | constraints.gridy = 0; |
---|
560 | constraints.fill = GridBagConstraints.BOTH; |
---|
561 | m_addRemovePointsPanel.add(m_addPointsButton); |
---|
562 | constraints.gridx = 1; |
---|
563 | m_addRemovePointsPanel.add(new JLabel("Add points"), constraints); |
---|
564 | constraints.gridx = 2; |
---|
565 | m_addRemovePointsPanel.add(m_classValueSelector); |
---|
566 | constraints.gridx = 0; |
---|
567 | constraints.gridy = 1; |
---|
568 | m_addRemovePointsPanel.add(m_removePointsButton, constraints); |
---|
569 | constraints.gridx = 1; |
---|
570 | m_addRemovePointsPanel.add(new JLabel("Remove points"),constraints); |
---|
571 | |
---|
572 | |
---|
573 | removeAllButton.addActionListener(new ActionListener() { |
---|
574 | public void actionPerformed(ActionEvent e) { |
---|
575 | if (m_trainingInstances != null) |
---|
576 | { |
---|
577 | if (m_startBut.getText().equals("Stop")) //we are plotting |
---|
578 | return; |
---|
579 | m_boundaryPanel.removeAllInstances(); |
---|
580 | computeBounds(); |
---|
581 | m_xAxisPanel.repaint(0,0,0,m_xAxisPanel.getWidth(), m_xAxisPanel.getHeight()); |
---|
582 | m_yAxisPanel.repaint(0,0,0,m_yAxisPanel.getWidth(), m_yAxisPanel.getHeight()); |
---|
583 | |
---|
584 | try {m_boundaryPanel.plotTrainingData(); } catch (Exception ex) {} |
---|
585 | } |
---|
586 | } |
---|
587 | }); |
---|
588 | constraints.gridx = 2; |
---|
589 | m_addRemovePointsPanel.add(removeAllButton, constraints); |
---|
590 | |
---|
591 | // m_addRemovePointsPanel.add(addPointsFrame, BorderLayout.NORTH); |
---|
592 | // m_addRemovePointsPanel.add(removePointsFrame, BorderLayout.CENTER); |
---|
593 | //m_addRemovePointsPanel.add(removeAllButton, BorderLayout.SOUTH); |
---|
594 | |
---|
595 | |
---|
596 | m_addRemovePointsButtonGroup.add(m_addPointsButton); |
---|
597 | m_addRemovePointsButtonGroup.add(m_removePointsButton); |
---|
598 | m_addPointsButton.setSelected(true); |
---|
599 | |
---|
600 | //classHolder.add(m_addRemovePointsPanel, BorderLayout.SOUTH); |
---|
601 | |
---|
602 | |
---|
603 | m_boundaryPanel = new BoundaryPanel(m_plotAreaWidth, m_plotAreaHeight); |
---|
604 | m_numberOfSamplesFromEachRegion = m_boundaryPanel.getNumSamplesPerRegion(); |
---|
605 | m_regionSamplesText.setText(""+m_numberOfSamplesFromEachRegion+" "); |
---|
606 | m_generatorSamplesBase = (int)m_boundaryPanel.getGeneratorSamplesBase(); |
---|
607 | m_generatorSamplesText.setText(""+m_generatorSamplesBase+" "); |
---|
608 | |
---|
609 | m_dataGenerator = new KDDataGenerator(); |
---|
610 | m_kernelBandwidth = m_dataGenerator.getKernelBandwidth(); |
---|
611 | m_kernelBandwidthText.setText(""+m_kernelBandwidth+" "); |
---|
612 | m_boundaryPanel.setDataGenerator(m_dataGenerator); |
---|
613 | |
---|
614 | |
---|
615 | JPanel gfxPanel = new JPanel(); |
---|
616 | gfxPanel.setLayout(new BorderLayout()); |
---|
617 | gfxPanel.setBorder(BorderFactory.createEtchedBorder()); |
---|
618 | //add(gfxPanel, BorderLayout.CENTER); |
---|
619 | |
---|
620 | // gfxPanel.add(m_addRemovePointsPanel, BorderLayout.NORTH); |
---|
621 | gfxPanel.add(m_boundaryPanel, BorderLayout.CENTER); |
---|
622 | m_xAxisPanel = new AxisPanel(false); |
---|
623 | gfxPanel.add(m_xAxisPanel, BorderLayout.SOUTH); |
---|
624 | m_yAxisPanel = new AxisPanel(true); |
---|
625 | gfxPanel.add(m_yAxisPanel, BorderLayout.WEST); |
---|
626 | |
---|
627 | JPanel containerPanel = new JPanel(); |
---|
628 | containerPanel.setLayout(new BorderLayout()); |
---|
629 | containerPanel.add(gfxPanel, BorderLayout.CENTER); |
---|
630 | add(containerPanel, BorderLayout.WEST); |
---|
631 | |
---|
632 | JPanel rightHandToolsPanel = new JPanel(); //this panel contains the widgets to the right of the BoundaryPanel. |
---|
633 | rightHandToolsPanel.setLayout(new BoxLayout(rightHandToolsPanel, BoxLayout.PAGE_AXIS)); |
---|
634 | |
---|
635 | rightHandToolsPanel.add(m_addRemovePointsPanel); |
---|
636 | |
---|
637 | JButton newWindowButton = new JButton("Open a new window"); //the button for spawning a new window for the program. |
---|
638 | //newWindowButton.setMaximumSize(new Dimension(100, 100)); |
---|
639 | //newWindowButton.setPreferredSize(new Dimension(120, m_addRemovePointsPanel.getHeight())); |
---|
640 | newWindowButton.addActionListener(new ActionListener() { |
---|
641 | public void actionPerformed(ActionEvent e) { |
---|
642 | try { |
---|
643 | Instances newTrainingData = null; |
---|
644 | Classifier newClassifier = null; |
---|
645 | if (m_trainingInstances != null) |
---|
646 | newTrainingData = new Instances(m_trainingInstances); |
---|
647 | if (m_classifier != null) |
---|
648 | newClassifier = AbstractClassifier.makeCopy(m_classifier); |
---|
649 | createNewVisualizerWindow(newClassifier, newTrainingData); |
---|
650 | } catch (Exception ex) { ex.printStackTrace();} |
---|
651 | } |
---|
652 | }); |
---|
653 | JPanel newWindowHolder = new JPanel(); |
---|
654 | newWindowHolder.add(newWindowButton); |
---|
655 | rightHandToolsPanel.add(newWindowHolder); |
---|
656 | rightHandToolsPanel.add(tempPanel); |
---|
657 | rightHandToolsPanel.add(startPanel); |
---|
658 | |
---|
659 | containerPanel.add(rightHandToolsPanel, BorderLayout.EAST); |
---|
660 | |
---|
661 | /*add(m_boundaryPanel, BorderLayout.CENTER); |
---|
662 | |
---|
663 | m_xAxisPanel = new AxisPanel(false); |
---|
664 | add(m_xAxisPanel, BorderLayout.SOUTH); |
---|
665 | m_yAxisPanel = new AxisPanel(true); |
---|
666 | add(m_yAxisPanel, BorderLayout.WEST);*/ |
---|
667 | |
---|
668 | m_startBut.setEnabled(false); |
---|
669 | m_startBut.addActionListener(new ActionListener() { |
---|
670 | public void actionPerformed(ActionEvent e) { |
---|
671 | if (m_startBut.getText().equals("Start")) { |
---|
672 | if (m_trainingInstances != null && m_classifier != null) { |
---|
673 | try { |
---|
674 | |
---|
675 | int BPSuccessCode = setUpBoundaryPanel(); //set up the boundary panel, find out if it was successful or not. |
---|
676 | |
---|
677 | if (BPSuccessCode == 1) |
---|
678 | JOptionPane.showMessageDialog(null,"Error: Kernel Bandwidth can't be less than zero!"); |
---|
679 | else if (BPSuccessCode == 2) { |
---|
680 | JOptionPane.showMessageDialog(null,"Error: Kernel Bandwidth must be less than the number of training instances!"); |
---|
681 | } else { |
---|
682 | m_boundaryPanel.start(); |
---|
683 | m_startBut.setText("Stop"); |
---|
684 | setControlEnabledStatus(false); |
---|
685 | } |
---|
686 | } catch (Exception ex) { |
---|
687 | ex.printStackTrace(); |
---|
688 | } |
---|
689 | } |
---|
690 | } else { |
---|
691 | m_boundaryPanel.stopPlotting(); |
---|
692 | m_startBut.setText("Start"); |
---|
693 | setControlEnabledStatus(true); |
---|
694 | } |
---|
695 | } |
---|
696 | }); |
---|
697 | |
---|
698 | m_boundaryPanel.addActionListener(new ActionListener() { |
---|
699 | public void actionPerformed(ActionEvent e) { |
---|
700 | m_startBut.setText("Start"); |
---|
701 | setControlEnabledStatus(true); |
---|
702 | } |
---|
703 | }); |
---|
704 | |
---|
705 | m_classPanel.addActionListener(new ActionListener() { |
---|
706 | public void actionPerformed(ActionEvent e) { |
---|
707 | |
---|
708 | try { |
---|
709 | // save color vector to a file |
---|
710 | FastVector colors = m_boundaryPanel.getColors(); |
---|
711 | FileOutputStream fos = new FileOutputStream("colors.ser"); |
---|
712 | ObjectOutputStream oos = new ObjectOutputStream(fos); |
---|
713 | oos.writeObject(colors); |
---|
714 | oos.flush(); |
---|
715 | oos.close(); |
---|
716 | } catch (Exception ex) {} |
---|
717 | |
---|
718 | m_boundaryPanel.replot(); |
---|
719 | |
---|
720 | } |
---|
721 | }); |
---|
722 | |
---|
723 | //set up a mouse listener for the boundary panel. |
---|
724 | m_boundaryPanel.addMouseListener(new MouseAdapter() { |
---|
725 | public void mouseClicked(MouseEvent e) { |
---|
726 | // System.err.println("boundary panel mouseClick " + e.getX() + " " + e.getY()); |
---|
727 | if (m_trainingInstances != null) { |
---|
728 | if (m_startBut.getText().equals("Stop")) //we are plotting |
---|
729 | return; |
---|
730 | |
---|
731 | if (m_addPointsButton.isSelected()) {//we are in add mode |
---|
732 | double classVal = 0; |
---|
733 | boolean validInput = true; |
---|
734 | if (m_trainingInstances.attribute(m_classAttBox.getSelectedIndex()).isNominal()) //class is nominal |
---|
735 | classVal = (double)m_classValueSelector.getSelectedIndex(); |
---|
736 | else { |
---|
737 | String indexStr = ""; |
---|
738 | try { |
---|
739 | indexStr = (String)m_classValueSelector.getSelectedItem(); |
---|
740 | classVal = Double.parseDouble(indexStr); |
---|
741 | } catch (Exception ex) { |
---|
742 | if (indexStr == null) indexStr = ""; |
---|
743 | JOptionPane.showMessageDialog(null,"Error adding a point: \"" + indexStr + "\"" |
---|
744 | + " is not a valid class value."); |
---|
745 | validInput = false; |
---|
746 | } |
---|
747 | } |
---|
748 | //System.err.println("classVal is " + classVal); |
---|
749 | if (validInput) |
---|
750 | m_boundaryPanel.addTrainingInstanceFromMouseLocation(e.getX(), e.getY(), m_classAttBox.getSelectedIndex(), classVal); |
---|
751 | } |
---|
752 | else { //remove mode |
---|
753 | m_boundaryPanel.removeTrainingInstanceFromMouseLocation(e.getX(), e.getY()); |
---|
754 | } |
---|
755 | try{ plotTrainingData(); } catch (Exception ex) {} //jimmy |
---|
756 | m_xAxisPanel.repaint(0,0,0,m_xAxisPanel.getWidth(), m_xAxisPanel.getHeight()); |
---|
757 | m_yAxisPanel.repaint(0,0,0,m_yAxisPanel.getWidth(), m_yAxisPanel.getHeight()); |
---|
758 | } |
---|
759 | } |
---|
760 | }); |
---|
761 | } |
---|
762 | |
---|
763 | /** |
---|
764 | * Set the enabled status of the controls |
---|
765 | * |
---|
766 | * @param status a <code>boolean</code> value |
---|
767 | */ |
---|
768 | private void setControlEnabledStatus(boolean status) { |
---|
769 | m_classAttBox.setEnabled(status); |
---|
770 | m_xAttBox.setEnabled(status); |
---|
771 | m_yAttBox.setEnabled(status); |
---|
772 | m_regionSamplesText.setEnabled(status); |
---|
773 | m_generatorSamplesText.setEnabled(status); |
---|
774 | m_kernelBandwidthText.setEnabled(status); |
---|
775 | m_plotTrainingData.setEnabled(status); |
---|
776 | removeAllButton.setEnabled(status); |
---|
777 | m_classValueSelector.setEnabled(status); |
---|
778 | m_addPointsButton.setEnabled(status); |
---|
779 | m_removePointsButton.setEnabled(status); |
---|
780 | m_FileChooser.setEnabled(status); |
---|
781 | chooseButton.setEnabled(status); |
---|
782 | } |
---|
783 | |
---|
784 | /** |
---|
785 | * Set a classifier to use |
---|
786 | * |
---|
787 | * @param newClassifier the classifier to use |
---|
788 | * @exception Exception if an error occurs |
---|
789 | */ |
---|
790 | public void setClassifier(Classifier newClassifier) throws Exception { |
---|
791 | |
---|
792 | m_classifier = newClassifier; |
---|
793 | |
---|
794 | try { |
---|
795 | int classIndex = m_classAttBox.getSelectedIndex(); |
---|
796 | |
---|
797 | if ((m_classifier != null) && (m_trainingInstances != null) && |
---|
798 | (m_trainingInstances.attribute(classIndex).isNominal())) { |
---|
799 | m_startBut.setEnabled(true); |
---|
800 | } |
---|
801 | else |
---|
802 | m_startBut.setEnabled(false); |
---|
803 | } catch (Exception e) {} |
---|
804 | |
---|
805 | } |
---|
806 | |
---|
807 | /** Sets up the bounds on our x and y axes to fit the dataset. |
---|
808 | Also repaints the x and y axes. |
---|
809 | */ |
---|
810 | private void computeBounds() { |
---|
811 | |
---|
812 | m_boundaryPanel.computeMinMaxAtts(); //delegate to the BoundaryPanel |
---|
813 | |
---|
814 | String xName = (String)m_xAttBox.getSelectedItem(); |
---|
815 | if (xName == null) { |
---|
816 | return; |
---|
817 | } |
---|
818 | xName = Utils.removeSubstring(xName, "X: "); |
---|
819 | xName = Utils.removeSubstring(xName, " (Num)"); |
---|
820 | String yName = (String)m_yAttBox.getSelectedItem(); |
---|
821 | yName = Utils.removeSubstring(yName, "Y: "); |
---|
822 | yName = Utils.removeSubstring(yName, " (Num)"); |
---|
823 | |
---|
824 | m_xIndex = -1; |
---|
825 | m_yIndex = -1; |
---|
826 | for (int i = 0; i < m_trainingInstances.numAttributes(); i++) { |
---|
827 | if (m_trainingInstances.attribute(i).name().equals(xName)) { |
---|
828 | m_xIndex = i; |
---|
829 | } |
---|
830 | if (m_trainingInstances.attribute(i).name().equals(yName)) { |
---|
831 | m_yIndex = i; |
---|
832 | } |
---|
833 | } |
---|
834 | |
---|
835 | m_minX = m_boundaryPanel.getMinXBound(); |
---|
836 | m_minY = m_boundaryPanel.getMinYBound(); |
---|
837 | m_maxX = m_boundaryPanel.getMaxXBound(); |
---|
838 | m_maxY = m_boundaryPanel.getMaxYBound(); |
---|
839 | //System.err.println("setting bounds to " + m_minX + " " + m_minY + " " + m_maxX + " " + m_maxY); |
---|
840 | m_xAxisPanel.repaint(0,0,0,m_xAxisPanel.getWidth(), m_xAxisPanel.getHeight()); |
---|
841 | m_yAxisPanel.repaint(0,0,0,m_yAxisPanel.getWidth(), m_yAxisPanel.getHeight()); |
---|
842 | } |
---|
843 | |
---|
844 | /** |
---|
845 | * Get the training instances |
---|
846 | * |
---|
847 | * @return the training instances |
---|
848 | */ |
---|
849 | public Instances getInstances() { |
---|
850 | return m_trainingInstances; |
---|
851 | } |
---|
852 | |
---|
853 | /** |
---|
854 | * Set the training instances |
---|
855 | * |
---|
856 | * @param inst the instances to use |
---|
857 | */ |
---|
858 | public void setInstances(Instances inst) throws Exception { |
---|
859 | if (inst == null) { |
---|
860 | m_trainingInstances = inst; |
---|
861 | m_classPanel.setInstances(m_trainingInstances); |
---|
862 | return; |
---|
863 | } |
---|
864 | |
---|
865 | // count the number of numeric attributes |
---|
866 | int numCount = 0; |
---|
867 | for (int i = 0; i < inst.numAttributes(); i++) { |
---|
868 | if (inst.attribute(i).isNumeric()) { |
---|
869 | numCount++; |
---|
870 | } |
---|
871 | } |
---|
872 | |
---|
873 | if (numCount < 2) { |
---|
874 | JOptionPane.showMessageDialog(null,"We need at least two numeric " + |
---|
875 | "attributes in order to visualize!"); |
---|
876 | return; |
---|
877 | } |
---|
878 | |
---|
879 | m_trainingInstances = inst; |
---|
880 | m_classPanel.setInstances(m_trainingInstances); |
---|
881 | // setup combo boxes |
---|
882 | String [] classAttNames = new String [m_trainingInstances.numAttributes()]; |
---|
883 | final Vector xAttNames = new Vector(); |
---|
884 | Vector yAttNames = new Vector(); |
---|
885 | |
---|
886 | for (int i = 0; i < m_trainingInstances.numAttributes(); i++) { |
---|
887 | classAttNames[i] = m_trainingInstances.attribute(i).name(); |
---|
888 | String type = ""; |
---|
889 | switch (m_trainingInstances.attribute(i).type()) { |
---|
890 | case Attribute.NOMINAL: |
---|
891 | type = " (Nom)"; |
---|
892 | break; |
---|
893 | case Attribute.NUMERIC: |
---|
894 | type = " (Num)"; |
---|
895 | break; |
---|
896 | case Attribute.STRING: |
---|
897 | type = " (Str)"; |
---|
898 | break; |
---|
899 | case Attribute.DATE: |
---|
900 | type = " (Dat)"; |
---|
901 | break; |
---|
902 | case Attribute.RELATIONAL: |
---|
903 | type = " (Rel)"; |
---|
904 | break; |
---|
905 | default: |
---|
906 | type = " (???)"; |
---|
907 | } |
---|
908 | classAttNames[i] += type; |
---|
909 | if (m_trainingInstances.attribute(i).isNumeric()) { |
---|
910 | xAttNames.addElement("X: "+classAttNames[i]); |
---|
911 | yAttNames.addElement("Y: "+classAttNames[i]); |
---|
912 | } |
---|
913 | } |
---|
914 | |
---|
915 | m_classAttBox.setModel(new DefaultComboBoxModel(classAttNames)); |
---|
916 | m_xAttBox.setModel(new DefaultComboBoxModel(xAttNames)); |
---|
917 | m_yAttBox.setModel(new DefaultComboBoxModel(yAttNames)); |
---|
918 | if (xAttNames.size() > 1) { |
---|
919 | m_yAttBox.setSelectedIndex(1); |
---|
920 | } |
---|
921 | |
---|
922 | m_classAttBox.addActionListener(new ActionListener() { |
---|
923 | public void actionPerformed(ActionEvent e) { |
---|
924 | configureForClassAttribute(); |
---|
925 | } |
---|
926 | }); |
---|
927 | |
---|
928 | m_xAttBox.addItemListener(new ItemListener() { |
---|
929 | public void itemStateChanged(ItemEvent e) { |
---|
930 | if (e.getStateChange() == ItemEvent.SELECTED) { |
---|
931 | /* if (xAttNames.size() > 1) { |
---|
932 | if (m_xAttBox.getSelectedIndex() == |
---|
933 | m_yAttBox.getSelectedIndex()) { |
---|
934 | m_xAttBox.setSelectedIndex((m_xAttBox.getSelectedIndex() + 1) % |
---|
935 | xAttNames.size()); |
---|
936 | } |
---|
937 | } */ |
---|
938 | computeBounds(); |
---|
939 | repaint(); |
---|
940 | try{ plotTrainingData(); } catch (Exception ex) {ex.printStackTrace();} //jimmy |
---|
941 | } |
---|
942 | } |
---|
943 | }); |
---|
944 | |
---|
945 | m_yAttBox.addItemListener(new ItemListener() { |
---|
946 | public void itemStateChanged(ItemEvent e) { |
---|
947 | if (e.getStateChange() == ItemEvent.SELECTED) { |
---|
948 | /* if (xAttNames.size() > 1) { |
---|
949 | if (m_yAttBox.getSelectedIndex() == |
---|
950 | m_xAttBox.getSelectedIndex()) { |
---|
951 | m_yAttBox.setSelectedIndex((m_yAttBox.getSelectedIndex() + 1) % |
---|
952 | xAttNames.size()); |
---|
953 | } |
---|
954 | } */ |
---|
955 | computeBounds(); |
---|
956 | repaint(); |
---|
957 | try{ plotTrainingData(); } catch (Exception ex) {ex.printStackTrace();} |
---|
958 | } |
---|
959 | } |
---|
960 | }); |
---|
961 | |
---|
962 | if (classAttNames.length > 0) |
---|
963 | m_classAttBox.setSelectedIndex(classAttNames.length - 1); //select last attribute as class by default. -jimmy |
---|
964 | |
---|
965 | //set up the add points selector combo box |
---|
966 | setUpClassValueSelectorCB(); |
---|
967 | |
---|
968 | configureForClassAttribute(); |
---|
969 | |
---|
970 | m_classPanel.setCindex(m_classAttBox.getSelectedIndex()); |
---|
971 | plotTrainingData(); |
---|
972 | computeBounds(); |
---|
973 | revalidate(); |
---|
974 | repaint(); |
---|
975 | |
---|
976 | if (getTopLevelAncestor() instanceof java.awt.Window) { |
---|
977 | ((java.awt.Window)getTopLevelAncestor()).pack(); |
---|
978 | } |
---|
979 | } |
---|
980 | |
---|
981 | /** Set up the combo box that chooses which class values to use when adding data points. |
---|
982 | */ |
---|
983 | private void setUpClassValueSelectorCB() { |
---|
984 | m_classValueSelector.removeAllItems(); |
---|
985 | int classAttribute = m_classAttBox.getSelectedIndex(); |
---|
986 | //System.err.println(m_trainingInstances.numClasses() + " classes"); |
---|
987 | m_trainingInstances.setClassIndex(classAttribute); |
---|
988 | if (m_trainingInstances.attribute(classAttribute).isNominal()) { |
---|
989 | m_classValueSelector.setEditable(false); |
---|
990 | for (int i = 0; i < /*m_trainingInstances.numDistinctValues(classAttribute)*/m_trainingInstances.numClasses(); i++) |
---|
991 | m_classValueSelector.insertItemAt(m_trainingInstances.attribute(classAttribute).value(i) , i); |
---|
992 | m_classValueSelector.setSelectedIndex(0); |
---|
993 | } |
---|
994 | else { |
---|
995 | m_classValueSelector.setEditable(true); |
---|
996 | } |
---|
997 | } |
---|
998 | |
---|
999 | /** |
---|
1000 | * Set up the class values combo boxes |
---|
1001 | */ |
---|
1002 | private void configureForClassAttribute() { |
---|
1003 | int classIndex = m_classAttBox.getSelectedIndex(); |
---|
1004 | if (classIndex >= 0) { |
---|
1005 | // see if this is a nominal attribute |
---|
1006 | if (!m_trainingInstances.attribute(classIndex).isNominal() || m_classifier == null) { |
---|
1007 | m_startBut.setEnabled(false); |
---|
1008 | } else { |
---|
1009 | m_startBut.setEnabled(true); |
---|
1010 | } |
---|
1011 | // set up class colours |
---|
1012 | FastVector colors = new FastVector(); |
---|
1013 | if (!m_trainingInstances.attribute(m_classAttBox.getSelectedIndex()).isNominal()) //this if by jimmy |
---|
1014 | { |
---|
1015 | for (int i = 0; i < BoundaryPanel.DEFAULT_COLORS.length; i++) |
---|
1016 | colors.addElement(BoundaryPanel.DEFAULT_COLORS[i]); |
---|
1017 | } |
---|
1018 | else { |
---|
1019 | for (int i = 0; i < |
---|
1020 | m_trainingInstances.attribute(classIndex).numValues(); i++) { |
---|
1021 | colors.addElement(BoundaryPanel. |
---|
1022 | DEFAULT_COLORS[i % BoundaryPanel.DEFAULT_COLORS.length]); |
---|
1023 | // m_classPanel.setColours(colors); |
---|
1024 | // m_boundaryPanel.setColors(colors); |
---|
1025 | } |
---|
1026 | } |
---|
1027 | m_classPanel.setColours(colors); //jimmy |
---|
1028 | m_boundaryPanel.setColors(colors); |
---|
1029 | } |
---|
1030 | } |
---|
1031 | |
---|
1032 | |
---|
1033 | /** |
---|
1034 | * Queries the user for a file to load instances from, then loads the |
---|
1035 | * instances in a background process. This is done in the IO |
---|
1036 | * thread, and an error message is popped up if the IO thread is busy. |
---|
1037 | */ |
---|
1038 | public void setInstancesFromFileQ() { |
---|
1039 | |
---|
1040 | // if (m_IOThread == null) { |
---|
1041 | int returnVal = m_FileChooser.showOpenDialog(this); |
---|
1042 | if (returnVal == JFileChooser.APPROVE_OPTION) { |
---|
1043 | File selected = m_FileChooser.getSelectedFile(); |
---|
1044 | |
---|
1045 | try |
---|
1046 | { |
---|
1047 | java.io.Reader r = new java.io.BufferedReader( |
---|
1048 | new java.io.FileReader(selected)); |
---|
1049 | Instances i = new Instances(r); |
---|
1050 | setInstances(i); |
---|
1051 | |
---|
1052 | //dataFileLabel.setText(selected.getName()); |
---|
1053 | dataFileLabel.setText(i.relationName()); |
---|
1054 | } catch (Exception e) |
---|
1055 | { |
---|
1056 | JOptionPane.showMessageDialog(this,"Can't load at this time,\n" |
---|
1057 | + "currently busy with other IO", |
---|
1058 | "Load Instances", |
---|
1059 | JOptionPane.WARNING_MESSAGE); |
---|
1060 | e.printStackTrace(); |
---|
1061 | |
---|
1062 | } |
---|
1063 | } |
---|
1064 | } |
---|
1065 | |
---|
1066 | /** Sets up the BoundaryPanel object so that it is ready for plotting. |
---|
1067 | * @return an error code:<br/> |
---|
1068 | * 0 - SUCCESS<br/> |
---|
1069 | * 1 - ERROR - Kernel bandwidth < 0<br/> |
---|
1070 | * 2 - ERROR - Kernel bandwidth >= number of training instances. |
---|
1071 | */ |
---|
1072 | public int setUpBoundaryPanel() throws Exception { |
---|
1073 | int returner = 0; //OK code. |
---|
1074 | int tempSamples = m_numberOfSamplesFromEachRegion; |
---|
1075 | try { |
---|
1076 | tempSamples = |
---|
1077 | Integer.parseInt(m_regionSamplesText.getText().trim()); |
---|
1078 | } catch (Exception ex) { |
---|
1079 | m_regionSamplesText.setText(""+tempSamples); |
---|
1080 | } |
---|
1081 | m_numberOfSamplesFromEachRegion = tempSamples; |
---|
1082 | m_boundaryPanel. |
---|
1083 | setNumSamplesPerRegion(tempSamples); |
---|
1084 | |
---|
1085 | tempSamples = m_generatorSamplesBase; |
---|
1086 | try { |
---|
1087 | tempSamples = |
---|
1088 | Integer.parseInt(m_generatorSamplesText.getText().trim()); |
---|
1089 | } catch (Exception ex) { |
---|
1090 | m_generatorSamplesText.setText(""+tempSamples); |
---|
1091 | } |
---|
1092 | m_generatorSamplesBase = tempSamples; |
---|
1093 | m_boundaryPanel.setGeneratorSamplesBase((double)tempSamples); |
---|
1094 | |
---|
1095 | tempSamples = m_kernelBandwidth; |
---|
1096 | try { |
---|
1097 | tempSamples = |
---|
1098 | Integer.parseInt(m_kernelBandwidthText.getText().trim()); |
---|
1099 | } catch (Exception ex) { |
---|
1100 | m_kernelBandwidthText.setText(""+tempSamples); |
---|
1101 | } |
---|
1102 | m_kernelBandwidth = tempSamples; |
---|
1103 | m_dataGenerator.setKernelBandwidth(tempSamples); |
---|
1104 | |
---|
1105 | if (m_kernelBandwidth < 0) returner = 1; |
---|
1106 | if (m_kernelBandwidth >= m_trainingInstances.numInstances()) returner = 2; |
---|
1107 | |
---|
1108 | m_trainingInstances. |
---|
1109 | setClassIndex(m_classAttBox.getSelectedIndex()); |
---|
1110 | m_boundaryPanel.setClassifier(m_classifier); |
---|
1111 | m_boundaryPanel.setTrainingData(m_trainingInstances); |
---|
1112 | m_boundaryPanel.setXAttribute(m_xIndex); |
---|
1113 | m_boundaryPanel.setYAttribute(m_yIndex); |
---|
1114 | m_boundaryPanel. |
---|
1115 | setPlotTrainingData(m_plotTrainingData.isSelected()); |
---|
1116 | |
---|
1117 | return returner; |
---|
1118 | } |
---|
1119 | |
---|
1120 | /** Plots the training data on-screen. Also does all of the setup required |
---|
1121 | * for this to work. |
---|
1122 | */ |
---|
1123 | public void plotTrainingData() throws Exception { |
---|
1124 | m_boundaryPanel.initialize(); |
---|
1125 | setUpBoundaryPanel(); |
---|
1126 | computeBounds(); |
---|
1127 | m_boundaryPanel.plotTrainingData(); |
---|
1128 | } |
---|
1129 | |
---|
1130 | /** Stops the plotting thread. |
---|
1131 | */ |
---|
1132 | public void stopPlotting() { |
---|
1133 | m_boundaryPanel.stopPlotting(); |
---|
1134 | } |
---|
1135 | |
---|
1136 | /** |
---|
1137 | * Sets whether System.exit gets called when no more windows are open. |
---|
1138 | * |
---|
1139 | * @param value if TRUE then a System.exit call is ossued after the |
---|
1140 | * last window gets closed. |
---|
1141 | */ |
---|
1142 | public static void setExitIfNoWindowsOpen(boolean value) { |
---|
1143 | m_ExitIfNoWindowsOpen = value; |
---|
1144 | } |
---|
1145 | |
---|
1146 | /** |
---|
1147 | * Gets whether System.exit gets called after the last window gets closed |
---|
1148 | * |
---|
1149 | * @return TRUE if System.exit gets called after last window |
---|
1150 | * got closed. |
---|
1151 | */ |
---|
1152 | public static boolean getExitIfNoWindowsOpen() { |
---|
1153 | return m_ExitIfNoWindowsOpen; |
---|
1154 | } |
---|
1155 | |
---|
1156 | /** Creates a new GUI window with all of the BoundaryVisualizer trappings, |
---|
1157 | * @param classifier The classifier to use in the new window. May be null. |
---|
1158 | * @param instances The dataset to visualize on in the new window. May be null. |
---|
1159 | */ |
---|
1160 | public static void createNewVisualizerWindow(Classifier classifier, Instances instances) throws Exception { |
---|
1161 | m_WindowCount++; |
---|
1162 | |
---|
1163 | final javax.swing.JFrame jf = |
---|
1164 | new javax.swing.JFrame("Weka classification boundary visualizer"); |
---|
1165 | jf.getContentPane().setLayout(new BorderLayout()); |
---|
1166 | final BoundaryVisualizer bv = new BoundaryVisualizer(); |
---|
1167 | jf.getContentPane().add(bv, BorderLayout.CENTER); |
---|
1168 | jf.setSize(bv.getMinimumSize()); |
---|
1169 | jf.addWindowListener(new java.awt.event.WindowAdapter() { |
---|
1170 | public void windowClosing(java.awt.event.WindowEvent e) { |
---|
1171 | m_WindowCount--; |
---|
1172 | bv.stopPlotting(); |
---|
1173 | jf.dispose(); |
---|
1174 | if ((m_WindowCount == 0) && m_ExitIfNoWindowsOpen) { |
---|
1175 | System.exit(0); |
---|
1176 | } |
---|
1177 | } |
---|
1178 | }); |
---|
1179 | |
---|
1180 | jf.pack(); |
---|
1181 | jf.setVisible(true); |
---|
1182 | jf.setResizable(false); |
---|
1183 | |
---|
1184 | if (classifier == null) |
---|
1185 | bv.setClassifier(null); |
---|
1186 | else { |
---|
1187 | bv.setClassifier(classifier); |
---|
1188 | bv.m_classifierEditor.setValue(classifier); |
---|
1189 | } |
---|
1190 | |
---|
1191 | if (instances == null) |
---|
1192 | bv.setInstances(null); |
---|
1193 | else |
---|
1194 | { |
---|
1195 | bv.setInstances(instances); |
---|
1196 | |
---|
1197 | try{ |
---|
1198 | bv.dataFileLabel.setText(instances.relationName()); |
---|
1199 | bv.plotTrainingData(); |
---|
1200 | bv.m_classPanel.setCindex(bv.m_classAttBox.getSelectedIndex()); |
---|
1201 | bv.repaint(0,0,0,bv.getWidth(), bv.getHeight()); |
---|
1202 | } catch (Exception ex) {} |
---|
1203 | } |
---|
1204 | |
---|
1205 | } |
---|
1206 | |
---|
1207 | /** |
---|
1208 | * Main method for testing this class |
---|
1209 | * |
---|
1210 | * @param args a <code>String[]</code> value |
---|
1211 | */ |
---|
1212 | public static void main(String [] args) { |
---|
1213 | weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started"); |
---|
1214 | try { |
---|
1215 | if (args.length < 2) { |
---|
1216 | createNewVisualizerWindow(null, null); |
---|
1217 | } |
---|
1218 | else { |
---|
1219 | String [] argsR = null; |
---|
1220 | if (args.length > 2) { |
---|
1221 | argsR = new String [args.length-2]; |
---|
1222 | for (int j = 2; j < args.length; j++) { |
---|
1223 | argsR[j-2] = args[j]; |
---|
1224 | } |
---|
1225 | } |
---|
1226 | Classifier c = AbstractClassifier.forName(args[1], argsR); |
---|
1227 | |
---|
1228 | System.err.println("Loading instances from : "+args[0]); |
---|
1229 | java.io.Reader r = new java.io.BufferedReader( |
---|
1230 | new java.io.FileReader(args[0])); |
---|
1231 | Instances i = new Instances(r); |
---|
1232 | |
---|
1233 | createNewVisualizerWindow(c, i); |
---|
1234 | } |
---|
1235 | |
---|
1236 | } catch (Exception ex) { |
---|
1237 | ex.printStackTrace(); |
---|
1238 | } |
---|
1239 | } |
---|
1240 | } |
---|
1241 | |
---|
1242 | |
---|