001: /*
002: * This program is free software; you can redistribute it and/or modify
003: * it under the terms of the GNU General Public License as published by
004: * the Free Software Foundation; either version 2 of the License, or
005: * (at your option) any later version.
006: *
007: * This program is distributed in the hope that it will be useful,
008: * but WITHOUT ANY WARRANTY; without even the implied warranty of
009: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: * GNU General Public License for more details.
011: *
012: * You should have received a copy of the GNU General Public License
013: * along with this program; if not, write to the Free Software
014: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
015: */
016:
017: /*
018: * AttributeSummaryPanel.java
019: * Copyright (C) 1999 University of Waikato, Hamilton, New Zealand
020: *
021: */
022:
023: package weka.gui;
024:
025: import weka.core.Attribute;
026: import weka.core.AttributeStats;
027: import weka.core.Instances;
028: import weka.core.Utils;
029:
030: import java.awt.BorderLayout;
031: import java.awt.GridBagConstraints;
032: import java.awt.GridBagLayout;
033:
034: import javax.swing.BorderFactory;
035: import javax.swing.JLabel;
036: import javax.swing.JPanel;
037: import javax.swing.JScrollPane;
038: import javax.swing.JTable;
039: import javax.swing.ListSelectionModel;
040: import javax.swing.SwingConstants;
041: import javax.swing.SwingUtilities;
042: import javax.swing.table.DefaultTableModel;
043:
044: /**
045: * This panel displays summary statistics about an attribute: name, type
046: * number/% of missing/unique values, number of distinct values. For
047: * numeric attributes gives some other stats (mean/std dev), for nominal
048: * attributes gives counts for each attribute value.
049: *
050: * @author Len Trigg (trigg@cs.waikato.ac.nz)
051: * @version $Revision: 1.11 $
052: */
053: public class AttributeSummaryPanel extends JPanel {
054:
055: /** for serialization */
056: static final long serialVersionUID = -5434987925737735880L;
057:
058: /** Message shown when no instances have been loaded and no attribute set */
059: protected static final String NO_SOURCE = "None";
060:
061: /** Displays the name of the relation */
062: protected JLabel m_AttributeNameLab = new JLabel(NO_SOURCE);
063:
064: /** Displays the type of attribute */
065: protected JLabel m_AttributeTypeLab = new JLabel(NO_SOURCE);
066:
067: /** Displays the number of missing values */
068: protected JLabel m_MissingLab = new JLabel(NO_SOURCE);
069:
070: /** Displays the number of unique values */
071: protected JLabel m_UniqueLab = new JLabel(NO_SOURCE);
072:
073: /** Displays the number of distinct values */
074: protected JLabel m_DistinctLab = new JLabel(NO_SOURCE);
075:
076: /** Displays other stats in a table */
077: protected JTable m_StatsTable = new JTable() {
078: /** for serialization */
079: private static final long serialVersionUID = 7165142874670048578L;
080:
081: /**
082: * returns always false, since it's just information for the user
083: *
084: * @param row the row
085: * @param column the column
086: * @return always false, i.e., the whole table is not editable
087: */
088: public boolean isCellEditable(int row, int column) {
089: return false;
090: }
091: };
092:
093: /** The instances we're playing with */
094: protected Instances m_Instances;
095:
096: /** Cached stats on the attributes we've summarized so far */
097: protected AttributeStats[] m_AttributeStats;
098:
099: /**
100: * Creates the instances panel with no initial instances.
101: */
102: public AttributeSummaryPanel() {
103:
104: JPanel simple = new JPanel();
105: GridBagLayout gbL = new GridBagLayout();
106: simple.setLayout(gbL);
107: JLabel lab = new JLabel("Name:", SwingConstants.RIGHT);
108: lab.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
109: GridBagConstraints gbC = new GridBagConstraints();
110: gbC.anchor = GridBagConstraints.EAST;
111: gbC.fill = GridBagConstraints.HORIZONTAL;
112: gbC.gridy = 0;
113: gbC.gridx = 0;
114: gbL.setConstraints(lab, gbC);
115: simple.add(lab);
116: gbC = new GridBagConstraints();
117: gbC.anchor = GridBagConstraints.WEST;
118: gbC.fill = GridBagConstraints.HORIZONTAL;
119: gbC.gridy = 0;
120: gbC.gridx = 1;
121: gbC.weightx = 100;
122: gbC.gridwidth = 3;
123: gbL.setConstraints(m_AttributeNameLab, gbC);
124: simple.add(m_AttributeNameLab);
125: m_AttributeNameLab.setBorder(BorderFactory.createEmptyBorder(0,
126: 5, 0, 10));
127:
128: lab = new JLabel("Type:", SwingConstants.RIGHT);
129: lab.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
130: gbC = new GridBagConstraints();
131: gbC.anchor = GridBagConstraints.EAST;
132: gbC.fill = GridBagConstraints.HORIZONTAL;
133: gbC.gridy = 0;
134: gbC.gridx = 4;
135: gbL.setConstraints(lab, gbC);
136: simple.add(lab);
137: gbC = new GridBagConstraints();
138: gbC.anchor = GridBagConstraints.WEST;
139: gbC.fill = GridBagConstraints.HORIZONTAL;
140: gbC.gridy = 0;
141: gbC.gridx = 5;
142: gbC.weightx = 100;
143: gbL.setConstraints(m_AttributeTypeLab, gbC);
144: simple.add(m_AttributeTypeLab);
145: m_AttributeTypeLab.setBorder(BorderFactory.createEmptyBorder(0,
146: 5, 0, 10));
147:
148: // Put into a separate panel?
149: lab = new JLabel("Missing:", SwingConstants.RIGHT);
150: lab.setBorder(BorderFactory.createEmptyBorder(0, 10, 5, 0));
151: gbC = new GridBagConstraints();
152: gbC.anchor = GridBagConstraints.EAST;
153: gbC.fill = GridBagConstraints.HORIZONTAL;
154: gbC.gridy = 1;
155: gbC.gridx = 0;
156: gbL.setConstraints(lab, gbC);
157: simple.add(lab);
158: gbC = new GridBagConstraints();
159: gbC.anchor = GridBagConstraints.WEST;
160: gbC.fill = GridBagConstraints.HORIZONTAL;
161: gbC.gridy = 1;
162: gbC.gridx = 1;
163: gbC.weightx = 100;
164: gbL.setConstraints(m_MissingLab, gbC);
165: simple.add(m_MissingLab);
166: m_MissingLab.setBorder(BorderFactory.createEmptyBorder(0, 5, 5,
167: 10));
168:
169: lab = new JLabel("Distinct:", SwingConstants.RIGHT);
170: lab.setBorder(BorderFactory.createEmptyBorder(0, 10, 5, 0));
171: gbC = new GridBagConstraints();
172: gbC.anchor = GridBagConstraints.EAST;
173: gbC.fill = GridBagConstraints.HORIZONTAL;
174: gbC.gridy = 1;
175: gbC.gridx = 2;
176: gbL.setConstraints(lab, gbC);
177: simple.add(lab);
178: gbC = new GridBagConstraints();
179: gbC.anchor = GridBagConstraints.WEST;
180: gbC.fill = GridBagConstraints.HORIZONTAL;
181: gbC.gridy = 1;
182: gbC.gridx = 3;
183: gbC.weightx = 100;
184: gbL.setConstraints(m_DistinctLab, gbC);
185: simple.add(m_DistinctLab);
186: m_DistinctLab.setBorder(BorderFactory.createEmptyBorder(0, 5,
187: 5, 10));
188:
189: lab = new JLabel("Unique:", SwingConstants.RIGHT);
190: lab.setBorder(BorderFactory.createEmptyBorder(0, 10, 5, 0));
191: gbC = new GridBagConstraints();
192: gbC.anchor = GridBagConstraints.EAST;
193: gbC.fill = GridBagConstraints.HORIZONTAL;
194: gbC.gridy = 1;
195: gbC.gridx = 4;
196: gbL.setConstraints(lab, gbC);
197: simple.add(lab);
198: gbC = new GridBagConstraints();
199: gbC.anchor = GridBagConstraints.WEST;
200: gbC.fill = GridBagConstraints.HORIZONTAL;
201: gbC.gridy = 1;
202: gbC.gridx = 5;
203: gbC.weightx = 100;
204: gbL.setConstraints(m_UniqueLab, gbC);
205: simple.add(m_UniqueLab);
206: m_UniqueLab.setBorder(BorderFactory.createEmptyBorder(0, 5, 5,
207: 10));
208:
209: setLayout(new BorderLayout());
210: add(simple, BorderLayout.NORTH);
211: add(new JScrollPane(m_StatsTable), BorderLayout.CENTER);
212: m_StatsTable.getSelectionModel().setSelectionMode(
213: ListSelectionModel.SINGLE_SELECTION);
214: }
215:
216: /**
217: * Tells the panel to use a new set of instances.
218: *
219: * @param inst a set of Instances
220: */
221: public void setInstances(Instances inst) {
222:
223: m_Instances = inst;
224: m_AttributeStats = new AttributeStats[inst.numAttributes()];
225: m_AttributeNameLab.setText(NO_SOURCE);
226: m_AttributeTypeLab.setText(NO_SOURCE);
227: m_MissingLab.setText(NO_SOURCE);
228: m_UniqueLab.setText(NO_SOURCE);
229: m_DistinctLab.setText(NO_SOURCE);
230: m_StatsTable.setModel(new DefaultTableModel());
231: }
232:
233: /**
234: * Sets the attribute that statistics will be displayed for.
235: *
236: * @param index the index of the attribute to display
237: */
238: public void setAttribute(final int index) {
239:
240: setHeader(index);
241: if (m_AttributeStats[index] == null) {
242: Thread t = new Thread() {
243: public void run() {
244: m_AttributeStats[index] = m_Instances
245: .attributeStats(index);
246: SwingUtilities.invokeLater(new Runnable() {
247: public void run() {
248: setDerived(index);
249: m_StatsTable.sizeColumnsToFit(-1);
250: m_StatsTable.revalidate();
251: m_StatsTable.repaint();
252: }
253: });
254: }
255: };
256: t.setPriority(Thread.MIN_PRIORITY);
257: t.start();
258: } else {
259: setDerived(index);
260: }
261: }
262:
263: /**
264: * Sets the gui elements for fields that are stored in the AttributeStats
265: * structure.
266: *
267: * @param index the index of the attribute
268: */
269: protected void setDerived(int index) {
270:
271: AttributeStats as = m_AttributeStats[index];
272: long percent = Math.round(100.0 * as.missingCount
273: / as.totalCount);
274: m_MissingLab.setText("" + as.missingCount + " (" + percent
275: + "%)");
276: percent = Math.round(100.0 * as.uniqueCount / as.totalCount);
277: m_UniqueLab
278: .setText("" + as.uniqueCount + " (" + percent + "%)");
279: m_DistinctLab.setText("" + as.distinctCount);
280: setTable(as, index);
281: }
282:
283: /**
284: * Creates a tablemodel for the attribute being displayed
285: *
286: * @param as the attribute statistics
287: * @param index the index of the attribute
288: */
289: protected void setTable(AttributeStats as, int index) {
290:
291: if (as.nominalCounts != null) {
292: Attribute att = m_Instances.attribute(index);
293: Object[] colNames = { "Label", "Count" };
294: Object[][] data = new Object[as.nominalCounts.length][2];
295: for (int i = 0; i < as.nominalCounts.length; i++) {
296: data[i][0] = att.value(i);
297: data[i][1] = new Integer(as.nominalCounts[i]);
298: }
299: m_StatsTable
300: .setModel(new DefaultTableModel(data, colNames));
301: } else if (as.numericStats != null) {
302: Object[] colNames = { "Statistic", "Value" };
303: Object[][] data = new Object[4][2];
304: data[0][0] = "Minimum";
305: data[0][1] = Utils.doubleToString(as.numericStats.min, 3);
306: data[1][0] = "Maximum";
307: data[1][1] = Utils.doubleToString(as.numericStats.max, 3);
308: data[2][0] = "Mean";
309: data[2][1] = Utils.doubleToString(as.numericStats.mean, 3);
310: data[3][0] = "StdDev";
311: data[3][1] = Utils
312: .doubleToString(as.numericStats.stdDev, 3);
313: m_StatsTable
314: .setModel(new DefaultTableModel(data, colNames));
315: } else {
316: m_StatsTable.setModel(new DefaultTableModel());
317: }
318: }
319:
320: /**
321: * Sets the labels for fields we can determine just from the instance
322: * header.
323: *
324: * @param index the index of the attribute
325: */
326: protected void setHeader(int index) {
327:
328: Attribute att = m_Instances.attribute(index);
329: m_AttributeNameLab.setText(att.name());
330: switch (att.type()) {
331: case Attribute.NOMINAL:
332: m_AttributeTypeLab.setText("Nominal");
333: break;
334: case Attribute.NUMERIC:
335: m_AttributeTypeLab.setText("Numeric");
336: break;
337: case Attribute.STRING:
338: m_AttributeTypeLab.setText("String");
339: break;
340: case Attribute.DATE:
341: m_AttributeTypeLab.setText("Date");
342: break;
343: case Attribute.RELATIONAL:
344: m_AttributeTypeLab.setText("Relational");
345: break;
346: default:
347: m_AttributeTypeLab.setText("Unknown");
348: break;
349: }
350: m_MissingLab.setText("...");
351: m_UniqueLab.setText("...");
352: m_DistinctLab.setText("...");
353: }
354:
355: /**
356: * Tests out the attribute summary panel from the command line.
357: *
358: * @param args optional name of dataset to load
359: */
360: public static void main(String[] args) {
361:
362: try {
363: final javax.swing.JFrame jf = new javax.swing.JFrame(
364: "Attribute Panel");
365: jf.getContentPane().setLayout(new BorderLayout());
366: final AttributeSummaryPanel p = new AttributeSummaryPanel();
367: p.setBorder(BorderFactory.createTitledBorder("Attribute"));
368: jf.getContentPane().add(p, BorderLayout.CENTER);
369: final javax.swing.JComboBox j = new javax.swing.JComboBox();
370: j.setEnabled(false);
371: j.addActionListener(new java.awt.event.ActionListener() {
372: public void actionPerformed(java.awt.event.ActionEvent e) {
373: p.setAttribute(j.getSelectedIndex());
374: }
375: });
376: jf.getContentPane().add(j, BorderLayout.NORTH);
377: jf.addWindowListener(new java.awt.event.WindowAdapter() {
378: public void windowClosing(java.awt.event.WindowEvent e) {
379: jf.dispose();
380: System.exit(0);
381: }
382: });
383: jf.pack();
384: jf.setVisible(true);
385: if (args.length == 1) {
386: java.io.Reader r = new java.io.BufferedReader(
387: new java.io.FileReader(args[0]));
388: Instances inst = new Instances(r);
389: p.setInstances(inst);
390: p.setAttribute(0);
391: String[] names = new String[inst.numAttributes()];
392: for (int i = 0; i < names.length; i++) {
393: names[i] = inst.attribute(i).name();
394: }
395: j.setModel(new javax.swing.DefaultComboBoxModel(names));
396: j.setEnabled(true);
397: }
398: } catch (Exception ex) {
399: ex.printStackTrace();
400: System.err.println(ex.getMessage());
401: }
402: }
403: }
|