📄 decisiontreerulesview.java
字号:
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Created on 2005-1-14
*
* $Author$
* $Date$
* $Revision$
*
*/
package eti.bi.alphaminer.patch.standard.operation.result.view;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.io.File;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import weka.classifiers.trees.J48;
import weka.classifiers.trees.j48.ClassifierSplitModel;
import weka.classifiers.trees.j48.ClassifierTree;
import weka.classifiers.trees.j48.Distribution;
import weka.core.Utils;
import com.prudsys.pdm.Core.CategoricalAttribute;
import com.prudsys.pdm.Core.MiningAttribute;
import eti.bi.alphaminer.operation.result.ResultView;
import eti.bi.alphaminer.operation.result.datamodel.SortingDataGridModel;
import eti.bi.alphaminer.operation.result.export.ExcelExporter;
import eti.bi.common.Locale.Resource;
import eti.bi.exception.AppException;
import eti.bi.exception.SysException;
import eti.bi.util.NumberFormatter;
/**
*
* Take J48 classifier and the target MiningAttribute as inputs. Build all the
* rules recursively from the classifier. Calculate the supporting records
* (number of recors in the bag) and confidence rate (number of correct reocrds
* in the class / supporting records. Output a JPanel (JTable) showing these
* infor.
*
* Note: Need first call J48.toString() to build the tree node label before
* executing this class. Also there are many aspects can be improved. See
* comments below.
*
* @author TWang On Jan 26, 2005.
*
*/
public class DecisionTreeRulesView extends ResultView {
/**
*
*/
private static final long serialVersionUID = 1L;
// JTable that shows the data
private JTable m_DataTable;
private String[] m_DataTableHeader = { Resource.srcStr("DECISIONTREE_RULE_NO"),
Resource.srcStr("DECISIONTREE_RULE"), Resource.srcStr("DECISIONTREE_CLASS"),
Resource.srcStr("DECISIONTREE_CONFIDENCE"), Resource.srcStr("DECISIONTREE_SUPPORT") };
private Object[][] m_DataTableContent;
private Class[] m_DataTableType;
// JScrollPane that contains JTable
private JScrollPane m_ScrollPane;
private J48 m_J48Classifier;
// The target attribute's classes.
private ArrayList m_ClassValues;
private int m_RowNum;
private int m_ColoumNum;
private ArrayList<String> m_RuleLists;
private ArrayList<Double> m_ConfLists;
private ArrayList<Double> m_SuppLists;
private ArrayList m_ClassNameLists;
public final String SMALLER_THAN = "<";
public final String SMALLER_THAN_EQUAL_TO = "<=";
public final String LARGER_THAN = ">";
public final String LARGER_THAN_EQUAL_TO = ">=";
public final String CONNECTOR = " AND ";
public final String CONNECTOR_COLOR = "<font color=CC0033> AND </font>";
public DecisionTreeRulesView(J48 a_classifier, MiningAttribute a_TargetMiningAttribute) throws SysException {
super(Resource.srcStr("DECISIONTREE_VIEW_RULE"));
m_ViewType = ResultView.TYPE_TABLE;
if (a_classifier == null) {
throw new SysException("The J48 Classifier in the DecisionOperator is NULL.");
}
m_J48Classifier = a_classifier;
if (!(a_TargetMiningAttribute instanceof CategoricalAttribute)) {
throw new SysException("The target attribute is not categorical.");
}
m_ClassValues = ((CategoricalAttribute) a_TargetMiningAttribute).getValues();
m_ScrollPane = new JScrollPane();
m_RuleLists = new ArrayList<String>();
m_ConfLists = new ArrayList<Double>();
m_SuppLists = new ArrayList<Double>();
m_ClassNameLists = new ArrayList();
this.setLayout(new BorderLayout());
this.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
this.add(m_ScrollPane, BorderLayout.CENTER);
createDataTable();
m_ScrollPane.setPreferredSize(new Dimension(200, 70));
m_ScrollPane.getViewport().add(m_DataTable);
m_ScrollPane.getViewport().setBackground(Color.WHITE);
}
public void createDataTable() {
// Generate the rule recursively
ClassifierTree classifierTree = m_J48Classifier.getRoot();
StringBuffer ruleContent = new StringBuffer();
generateRules(classifierTree, ruleContent);
preprocessRules(m_RuleLists);
m_RowNum = m_RuleLists.size();
m_ColoumNum = m_DataTableHeader.length;
// Create JTable class type according to the table header
m_DataTableType = new Class[m_ColoumNum];
m_DataTableType[0] = Integer.class;
m_DataTableType[1] = String.class;
m_DataTableType[2] = String.class;
m_DataTableType[3] = Double.class;
m_DataTableType[4] = Double.class;
// Create JTable content
m_DataTableContent = new Object[m_RowNum][m_ColoumNum];
for (int i = 0; i < m_RowNum; i++) {
// The first element is Index
m_DataTableContent[i][0] = new Integer(i + 1);
m_DataTableContent[i][1] = m_RuleLists.get(i);
// m_DataTableContent[i][1] = new
// JLabel("<html>"+createOneRule(i)+"</html>");
m_DataTableContent[i][2] = m_ClassNameLists.get(i);
m_DataTableContent[i][3] = m_ConfLists.get(i);
m_DataTableContent[i][4] = m_SuppLists.get(i);
}
// Create Table
m_DataTable = new JTable();
// m_DataTable.setModel(new DataGridModel(m_DataTableContent,
// m_DataTableHeader, m_DataTableType));
SortingDataGridModel model = new SortingDataGridModel(m_DataTableContent, m_DataTableHeader, m_DataTableType);
m_DataTable.setModel(model);
// JLabelRenderer labelRender = new JLabelRenderer();
// m_DataTable.setDefaultRenderer(JLabel.class, labelRender);
// Commet it if no label rendering twang.
// m_DataTable.getColumn("Rule").setCellRenderer(new JLabelRenderer());
model.addMouseListenerToHeader(m_DataTable);
setColumnWidth();
}
/**
* Preprocess items before outputing to table. Only reduce redundent items
* in one direction (e.g., <, <= OR >, >=). TWang. On June 1, 2005.
*
* @param a_RulesList
*/
private void preprocessRules(ArrayList<String> a_RulesList) {
ArrayList done = new ArrayList();
for (int i = 0; i < a_RulesList.size(); i++) {
processRule(i, a_RulesList, this.SMALLER_THAN, done);
processRule(i, a_RulesList, this.LARGER_THAN, done);
}
}
/**
* Process the i-th rule. The rule must in the format of "ABC < xx AND DEF <
* yy ...".
*
* @param a_index
* @param a_RulesList
* @param a_Comparator
*/
@SuppressWarnings("unchecked")
private void processRule(int a_index, ArrayList<String> a_RulesList, String a_Comparator, ArrayList a_Done) {
String rule = a_RulesList.get(a_index);
String prefix = null;
String postfix = null;
String postfix_new = null;
String comparator = a_Comparator;
ArrayList done = a_Done;
StringBuffer newRule = new StringBuffer("");
int itemIndex;
String[] items = rule.split(this.CONNECTOR);
done.clear();
itemIndex = -1;
/**
* process the j-th item. Find all compatible items. Convert to one
* item. Make sure all non-compatible items are in place. Compatible
* items are: same prefix with < or <= ; OR same prefix with > or >=
*/
for (int j = 0; j < items.length; j++) {
itemIndex = j;
int comparatorIndex = items[j].indexOf(comparator);
int comparatorLength = comparator.length();
if (comparatorIndex != -1) {
prefix = items[j].substring(0, comparatorIndex);
if (done.contains(prefix + "" + comparator)) {
continue;
} else {
done.add(prefix + "" + comparator);
if (items[j].indexOf(this.SMALLER_THAN_EQUAL_TO) != -1) {
comparatorLength = this.SMALLER_THAN_EQUAL_TO.length();
}
postfix = items[j].substring(comparatorIndex + comparatorLength + 1);
for (int z = j + 1; z < items.length; z++) {
if (items[z].indexOf(prefix) != -1) {
int innerIndex = items[z].indexOf(comparator);
if (innerIndex != -1) {
if (items[z].indexOf(this.SMALLER_THAN_EQUAL_TO) != -1) {
comparatorLength = this.SMALLER_THAN_EQUAL_TO.length();
}
postfix_new = items[z].substring(comparatorIndex + comparatorLength + 1);
if (comparator.equals(this.SMALLER_THAN)
&& Float.parseFloat(postfix_new) < Float.parseFloat(postfix)) {
itemIndex = z;
} else if (comparator.equals(this.LARGER_THAN)
&& Float.parseFloat(postfix_new) > Float.parseFloat(postfix)) {
itemIndex = z;
}
}
}
}
newRule.append(items[itemIndex] + this.CONNECTOR);
}
} else {
newRule.append(items[itemIndex] + this.CONNECTOR);
}
}
int modified = newRule.lastIndexOf(this.CONNECTOR);
if (modified != -1) {
newRule.delete(modified + 1, newRule.length());
a_RulesList.remove(a_index);
a_RulesList.add(a_index, newRule.toString());
}
}
/**
* Generate the rules recursively from the ClassifierTree. "AND" can be
* displayed in red to improve the readibility in the future. Some
* limitations are listed in the algorithm too.
*
* @param a_ClassifierTree
* @param a_rule
*/
@SuppressWarnings("unchecked")
private void generateRules(ClassifierTree a_ClassifierTree, StringBuffer a_rule) {
ClassifierTree[] childClassifierTrees = a_ClassifierTree.getClassifierTreeChildren();
if (childClassifierTrees != null) {
for (int i = 0; i < childClassifierTrees.length; i++) {
StringBuffer myString = new StringBuffer(a_rule.toString());
if (childClassifierTrees[i].isLeaf()) {
// Add rule content. Remove the result part. According to
// dumpModel() method
// in the ClassifierSplitModel, it starts from a semicolon
// ":". However, if
// there is a ":" in the conclusion part, part of the
// conclusion will also be
// included. To be improved in the future.
myString.append(childClassifierTrees[i].getM_NodeContent());
m_RuleLists.add(myString.substring(0, myString.lastIndexOf(":")));
ClassifierSplitModel classifierSplitModel = childClassifierTrees[i].getClassifierSplitModel();
Distribution distribution = classifierSplitModel.distribution();
// Add other columns. I assume that the number of records
// per bag >0, otherwise, there
// should be no this SUBTREE at all. Since the leaf has no
// subtree, we always call .perBag(0)
int classIndex = distribution.maxClass();
m_SuppLists.add(new Double(Utils.roundDouble(distribution.perBag(0),
NumberFormatter.MAX_FRACTION_DIGIT)));
m_ConfLists.add(new Double(Utils.roundDouble(distribution.numCorrect(0) / distribution.perBag(0)
* 100, NumberFormatter.MAX_FRACTION_DIGIT)));
m_ClassNameLists.add(m_ClassValues.get(classIndex));
} else {
myString.append(childClassifierTrees[i].getM_NodeContent() + this.CONNECTOR);
generateRules(childClassifierTrees[i], myString);
}
}
}
}
public void setColumnWidth() {
TableColumnModel tcm = m_DataTable.getColumnModel();
for (int i = 1; i < tcm.getColumnCount(); i++) {
tcm.getColumn(i).setMinWidth(90);
}
tcm.getColumn(1).setMinWidth(250);
m_DataTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tcm.getColumn(0).setCellRenderer(m_DataTable.getTableHeader().getDefaultRenderer());
}
/**
* Exort the data table into an excel file.
*
* Called by the OperatorResult class. The subclass of OperatorResult must
* call m_SelectedView.export() explictly if it overwirtes the export()
* function of OperatorResult.
*/
public void export() throws AppException, SysException {
// Use user home directory
File directory = new File(System.getProperty("user.dir"));
// Create and initialize file chooser for excel
JFileChooser chooser = new JFileChooser(directory);
chooser.setDialogTitle(Resource.srcStr("FileExport"));
chooser.setFileFilter(ExcelExporter.FILTER);
chooser.setSelectedFile(ExcelExporter.DEFAULT_FILE);
// pop up the file chooser dialog and return the file value
int returnVal = chooser.showSaveDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File exportFile = chooser.getSelectedFile();
// <<tyleung 20/4/2005
if (exportFile.exists()) {
int option = JOptionPane.showConfirmDialog((Component) this, "The file \"" + exportFile.getName()
+ "\"" + " already exists. Do you want to replace the existing file?",//
"AlphaMiner", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (option != JOptionPane.CANCEL_OPTION) {
if (option == JOptionPane.YES_OPTION) {
// Create the excel exporter with the excel file
// extension enforced to be .xls
ExcelExporter aExporter = new ExcelExporter(m_DataTable, exportFile, true);
aExporter.export();
} else {
returnVal = chooser.showSaveDialog(this);
}
}
} else {
// Create the excel exporter with the excel file extension
// enforced to be .xls
ExcelExporter aExporter = new ExcelExporter(m_DataTable, exportFile, true);
aExporter.export();
}
}
// tyleung 20/4/2005>>
}
/**
* A JLabel like JTable render.
*
* @author twang
*/
class JLabelRenderer extends JLabel implements TableCellRenderer {
/**
*
*/
private static final long serialVersionUID = 1L;
public JLabelRenderer() {
// setFont(getFont().deriveFont(Font.BOLD));
// setOpaque(true); // To make background color show.
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
// setText(((JLabel)table.getValueAt(row, column)).toString());
JLabel label = (JLabel) value;
label.setAutoscrolls(false);
// label.setPreferredSize(new Dimension(2, 30));
return (Component) label;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -