RuleNode.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 26k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    RuleNode.java
  3.  *    Copyright (C) 2000 Mark Hall
  4.  *
  5.  *    This program is free software; you can redistribute it and/or modify
  6.  *    it under the terms of the GNU General Public License as published by
  7.  *    the Free Software Foundation; either version 2 of the License, or
  8.  *    (at your option) any later version.
  9.  *
  10.  *    This program is distributed in the hope that it will be useful,
  11.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *    GNU General Public License for more details.
  14.  *
  15.  *    You should have received a copy of the GNU General Public License
  16.  *    along with this program; if not, write to the Free Software
  17.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19. package weka.classifiers.trees.m5;
  20. import java.io.*;
  21. import java.util.*;
  22. import weka.core.*;
  23. import weka.classifiers.*;
  24. import weka.classifiers.functions.LinearRegression;
  25. import weka.filters.unsupervised.attribute.Remove;
  26. import weka.filters.Filter;
  27. /**
  28.  * Constructs a node for use in an m5 tree or rule
  29.  *
  30.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  31.  * @version $Revision: 1.3 $
  32.  */
  33. public class RuleNode extends Classifier {
  34.   /**
  35.    * instances reaching this node
  36.    */
  37.   private Instances    m_instances;
  38.   /**
  39.    * the class index
  40.    */
  41.   private int    m_classIndex;
  42.   /**
  43.    * the number of instances reaching this node
  44.    */
  45.   protected int    m_numInstances;
  46.   /**
  47.    * the number of attributes
  48.    */
  49.   private int    m_numAttributes;
  50.   /**
  51.    * Node is a leaf
  52.    */
  53.   private boolean    m_isLeaf;
  54.   /**
  55.    * attribute this node splits on
  56.    */
  57.   private int    m_splitAtt;
  58.   /**
  59.    * the value of the split attribute
  60.    */
  61.   private double    m_splitValue;
  62.   /**
  63.    * the linear model at this node
  64.    */
  65.   private LinearRegression m_nodeModel;
  66.   /**
  67.    * the number of paramters in the chosen model for this node---either
  68.    * the subtree model or the linear model.
  69.    * The constant term is counted as a paramter---this is for pruning
  70.    * purposes
  71.    */
  72.   public int    m_numParameters;
  73.   /**
  74.    * A copy of the instances containing only the attributes tested
  75.    * below this node---used to construct the linear model for this node
  76.    */
  77.   private Instances    m_reducedI;
  78.   /**
  79.    * the mean squared error of the model at this node (either linear or
  80.    * subtree)
  81.    */
  82.   private double    m_rootMeanSquaredError;
  83.   /**
  84.    * the Attribute filter used to remove any attributes not presented to
  85.    * the linear regression model
  86.    */
  87.   private Remove  m_attributeFilter;
  88.   /**
  89.    * child nodes
  90.    */
  91.   protected RuleNode    m_left;
  92.   protected RuleNode    m_right;
  93.   /**
  94.    * the parent of this node
  95.    */
  96.   private RuleNode    m_parent;
  97.   /**
  98.    * a node will not be split if it contains less then m_splitNum isntances
  99.    */
  100.   private double    m_splitNum = 3.5;
  101.   /**
  102.    * a node will not be split if its class standard deviation is less
  103.    * than 5% of the class standard deviation of all the instances
  104.    */
  105.   private double    m_devFraction = 0.05;
  106.   private double    m_pruningMultiplier = 2;
  107.   /**
  108.    * the number assigned to the linear model if this node is a leaf.
  109.    * = 0 if this node is not a leaf
  110.    */
  111.   private int    m_leafModelNum;
  112.   /**
  113.    * a node will not be split if the class deviation of its
  114.    * instances is less than m_devFraction of the deviation of the
  115.    * global class
  116.    */
  117.   private double    m_globalDeviation;
  118.   /**
  119.    * the absolute deviation of the global class
  120.    */
  121.   private double    m_globalAbsDeviation;
  122.   /**
  123.    * Use the original m5 smoothing procedure during prediction of
  124.    * novel test cases
  125.    */
  126.   private boolean    m_smoothPredictions;
  127.   /**
  128.    * Used to disable smoothing during pruning
  129.    */
  130.   private boolean    m_smoothingOn;
  131.   /**
  132.    * Indices of the attributes to be used in generating a linear model
  133.    * at this node
  134.    */
  135.   private int [] m_indices;
  136.     
  137.   /**
  138.    * Constant used in original m5 smoothing calculation
  139.    */
  140.   private static final double    SMOOTHING_CONSTANT = 15.0;
  141.   /**
  142.    * Node id.
  143.    */
  144.   private int m_id;
  145.   /**
  146.    * Save the instances at each node (for visualizing in the
  147.    * Explorer's treevisualizer.
  148.    */
  149.   private boolean m_saveInstances = false;
  150.   /**
  151.    * Make a regression tree instead of a model tree
  152.    */
  153.   private boolean m_regressionTree;
  154.   /**
  155.    * Creates a new <code>RuleNode</code> instance.
  156.    *
  157.    * @param globalDev the global standard deviation of the class
  158.    * @param globalAbsDev the global absolute deviation of the class
  159.    * @param parent the parent of this node
  160.    */
  161.   public RuleNode(double globalDev, double globalAbsDev, RuleNode parent) {
  162.     m_nodeModel = null;
  163.     m_right = null;
  164.     m_left = null;
  165.     m_parent = parent;
  166.     m_globalDeviation = globalDev;
  167.     m_globalAbsDeviation = globalAbsDev;
  168.     m_attributeFilter = null;
  169.     m_smoothPredictions = false;
  170.   }
  171.     
  172.   /**
  173.    * Build this node (find an attribute and split point)
  174.    *
  175.    * @param data the instances on which to build this node
  176.    * @exception Exception if an error occurs
  177.    */
  178.   public void buildClassifier(Instances data) throws Exception {
  179.     m_smoothingOn = false;
  180.     m_rootMeanSquaredError = Double.MAX_VALUE;
  181.     //    m_instances = new Instances(data);
  182.     m_instances = data;
  183.     m_classIndex = m_instances.classIndex();
  184.     m_numInstances = m_instances.numInstances();
  185.     m_numAttributes = m_instances.numAttributes();
  186.     m_nodeModel = null;
  187.     m_right = null;
  188.     m_left = null;
  189.     m_attributeFilter = null;
  190.     if ((m_numInstances < m_splitNum) 
  191. || (Rule.stdDev(m_classIndex, m_instances) 
  192.     < (m_globalDeviation * m_devFraction))) {
  193.       m_isLeaf = true;
  194.     } else {
  195.       m_isLeaf = false;
  196.     } 
  197.     split();
  198.   } 
  199.  
  200.   /**
  201.    * Classify an instance using this node. Recursively calls classifyInstance
  202.    * on child nodes.
  203.    *
  204.    * @param inst the instance to classify
  205.    * @return the prediction for this instance
  206.    * @exception Exception if an error occurs
  207.    */
  208.   public double classifyInstance(Instance inst) throws Exception {
  209.     double   pred;
  210.     double   n = 0;
  211.     Instance tempInst;
  212.     if (m_isLeaf) {
  213.       if (m_nodeModel == null) {
  214. throw new Exception("Classifier has not been built correctly.");
  215.       } 
  216.       m_attributeFilter.input(inst);
  217.       tempInst = m_attributeFilter.output();
  218.       return m_nodeModel.classifyInstance(tempInst);
  219.     } 
  220.     if (inst.value(m_splitAtt) <= m_splitValue) {
  221.       if (m_left == null) {
  222. m_attributeFilter.input(inst);
  223. tempInst = m_attributeFilter.output();
  224. return m_nodeModel.classifyInstance(tempInst);
  225.       } 
  226.       pred = m_left.classifyInstance(inst);
  227.       n = m_left.m_numInstances;
  228.     } else {
  229.       if (m_right == null) {
  230. m_attributeFilter.input(inst);
  231. tempInst = m_attributeFilter.output();
  232. return m_nodeModel.classifyInstance(tempInst);
  233.       } 
  234.       pred = m_right.classifyInstance(inst);
  235.       n = m_right.m_numInstances;
  236.     } 
  237.     if (m_smoothingOn && m_smoothPredictions) {
  238.       m_attributeFilter.input(inst);
  239.       tempInst = m_attributeFilter.output();
  240.       double supportPred = m_nodeModel.classifyInstance(tempInst);
  241.       pred = smoothingOriginal(n, pred, supportPred);
  242.     } 
  243.     return pred;
  244.   } 
  245.   /**
  246.    * Applies the m5 smoothing procedure to a prediction
  247.    *
  248.    * @param n number of instances in selected child of this node
  249.    * @param pred the prediction so far
  250.    * @param supportPred the prediction of the linear model at this node
  251.    * @return the current prediction smoothed with the prediction of the
  252.    * linear model at this node
  253.    * @exception Exception if an error occurs
  254.    */
  255.   protected static double smoothingOriginal(double n, double pred, 
  256.     double supportPred) 
  257.     throws Exception {
  258.     double   smoothed;
  259.     smoothed = 
  260.       ((n * pred) + (SMOOTHING_CONSTANT * supportPred)) /
  261.       (n + SMOOTHING_CONSTANT);
  262.     return smoothed;
  263.   } 
  264.   /**
  265.    * Finds an attribute and split point for this node
  266.    *
  267.    * @exception Exception if an error occurs
  268.    */
  269.   public void split() throws Exception {
  270.     int   i;
  271.     Instances     leftSubset, rightSubset;
  272.     SplitEvaluate bestSplit, currentSplit;
  273.     boolean[]     attsBelow;
  274.     if (!m_isLeaf) {
  275.      
  276.       bestSplit = new YongSplitInfo(0, m_numInstances - 1, -1);
  277.       currentSplit = new YongSplitInfo(0, m_numInstances - 1, -1);
  278.       // find the best attribute to split on
  279.       for (i = 0; i < m_numAttributes; i++) {
  280. if (i != m_classIndex) {
  281.   // sort the instances by this attribute
  282.   m_instances.sort(i);
  283.   currentSplit.attrSplit(i, m_instances);
  284.   if ((Math.abs(currentSplit.maxImpurity() - 
  285. bestSplit.maxImpurity()) > 1.e-6) 
  286.       && (currentSplit.maxImpurity() 
  287.   > bestSplit.maxImpurity() + 1.e-6)) {
  288.     bestSplit = currentSplit.copy();
  289.   } 
  290.       } 
  291.       // cant find a good split or split point?
  292.       if (bestSplit.splitAttr() < 0 || bestSplit.position() < 1 
  293.   || bestSplit.position() > m_numInstances - 1) {
  294. m_isLeaf = true;
  295.       } else {
  296. m_splitAtt = bestSplit.splitAttr();
  297. m_splitValue = bestSplit.splitValue();
  298. leftSubset = new Instances(m_instances, m_numInstances);
  299. rightSubset = new Instances(m_instances, m_numInstances);
  300. for (i = 0; i < m_numInstances; i++) {
  301.   if (m_instances.instance(i).value(m_splitAtt) <= m_splitValue) {
  302.     leftSubset.add(m_instances.instance(i));
  303.   } else {
  304.     rightSubset.add(m_instances.instance(i));
  305.   } 
  306. leftSubset.compactify();
  307. rightSubset.compactify();
  308. // build left and right nodes
  309. m_left = new RuleNode(m_globalDeviation, m_globalAbsDeviation, this);
  310. m_left.setRegressionTree(m_regressionTree);
  311. m_left.setSmoothing(m_smoothPredictions);
  312. m_left.setSaveInstances(m_saveInstances);
  313. m_left.buildClassifier(leftSubset);
  314. m_right = new RuleNode(m_globalDeviation, m_globalAbsDeviation, this);
  315. m_right.setRegressionTree(m_regressionTree);
  316. m_right.setSmoothing(m_smoothPredictions);
  317. m_right.setSaveInstances(m_saveInstances);
  318. m_right.buildClassifier(rightSubset);
  319. // now find out what attributes are tested in the left and right
  320. // subtrees and use them to learn a linear model for this node
  321. if (!m_regressionTree) {
  322.   attsBelow = attsTestedBelow();
  323.   attsBelow[m_classIndex] = true;
  324.   int count = 0, j;
  325.   for (j = 0; j < m_numAttributes; j++) {
  326.     if (attsBelow[j]) {
  327.       count++;
  328.     } 
  329.   } 
  330.   
  331.   int[] indices = new int[count];
  332.   count = 0;
  333.   
  334.   for (j = 0; j < m_numAttributes; j++) {
  335.     if (attsBelow[j] && (j != m_classIndex)) {
  336.       indices[count++] = j;
  337.     } 
  338.   } 
  339.   
  340.   indices[count] = m_classIndex;
  341.   m_indices = indices;
  342. } else {
  343.   m_indices = new int [1];
  344.   m_indices[0] = m_classIndex;
  345.   m_numParameters = 1;
  346. }
  347.       } 
  348.     } 
  349.     if (m_isLeaf) {
  350.       int [] indices = new int [1];
  351.       indices[0] = m_classIndex;
  352.       m_indices = indices;
  353.       m_numParameters = 1;
  354.      
  355.       // need to evaluate the model here if want correct stats for unpruned
  356.       // tree
  357.     } 
  358.   } 
  359.   /**
  360.    * Build a linear model for this node using those attributes
  361.    * specified in indices.
  362.    *
  363.    * @param indices an array of attribute indices to include in the linear
  364.    * model
  365.    */
  366.   private void buildLinearModel(int [] indices) throws Exception {
  367.     // copy the training instances and remove all but the tested
  368.     // attributes
  369.     m_reducedI = new Instances(m_instances);
  370.     m_attributeFilter = new Remove();
  371.     
  372.     m_attributeFilter.setInvertSelection(true);
  373.     m_attributeFilter.setAttributeIndicesArray(indices);
  374.     m_attributeFilter.setInputFormat(m_reducedI);
  375.     m_reducedI = Filter.useFilter(m_reducedI, m_attributeFilter);
  376.     
  377.     // build a linear regression for the training data using the
  378.     // tested attributes
  379.     m_nodeModel = new LinearRegression();
  380.     
  381.     m_nodeModel.buildClassifier(m_reducedI);
  382.   }
  383.   /**
  384.    * Returns an array containing the indexes of attributes used in tests
  385.    * above this node
  386.    *
  387.    * @return an array of attribute indexes
  388.    */
  389.   private boolean[] attsTestedAbove() {
  390.     boolean[] atts = new boolean[m_numAttributes];
  391.     boolean[] attsAbove = null;
  392.     if (m_parent != null) {
  393.       attsAbove = m_parent.attsTestedAbove();
  394.     } 
  395.     if (attsAbove != null) {
  396.       for (int i = 0; i < m_numAttributes; i++) {
  397. atts[i] = attsAbove[i];
  398.       } 
  399.     } 
  400.     atts[m_splitAtt] = true;
  401.     return atts;
  402.   } 
  403.   /**
  404.    * Returns an array containing the indexes of attributes used in tests
  405.    * below this node
  406.    *
  407.    * @return an array of attribute indexes
  408.    */
  409.   private boolean[] attsTestedBelow() {
  410.     boolean[] attsBelow = new boolean[m_numAttributes];
  411.     boolean[] attsBelowLeft = null;
  412.     boolean[] attsBelowRight = null;
  413.     if (m_right != null) {
  414.       attsBelowRight = m_right.attsTestedBelow();
  415.     } 
  416.     if (m_left != null) {
  417.       attsBelowLeft = m_left.attsTestedBelow();
  418.     } 
  419.     for (int i = 0; i < m_numAttributes; i++) {
  420.       if (attsBelowLeft != null) {
  421. attsBelow[i] = (attsBelow[i] || attsBelowLeft[i]);
  422.       } 
  423.       if (attsBelowRight != null) {
  424. attsBelow[i] = (attsBelow[i] || attsBelowRight[i]);
  425.       } 
  426.     } 
  427.     if (!m_isLeaf) {
  428.       attsBelow[m_splitAtt] = true;
  429.     } 
  430.     return attsBelow;
  431.   } 
  432.   /**
  433.    * Sets the leaves' numbers
  434.    * @param leafCounter the number of leaves counted
  435.    * @return the number of the total leaves under the node
  436.    */
  437.   public int numLeaves(int leafCounter) {
  438.     // turn smoothing on (if requested) once model has been built
  439.     if (m_smoothPredictions) {
  440.       m_smoothingOn = true;
  441.     } 
  442.     if (!m_isLeaf) {
  443.       // node
  444.       m_leafModelNum = 0;
  445.       if (m_left != null) {
  446. leafCounter = m_left.numLeaves(leafCounter);
  447.       } 
  448.       if (m_right != null) {
  449. leafCounter = m_right.numLeaves(leafCounter);
  450.       } 
  451.     } else {
  452.       // leaf
  453.       leafCounter++;
  454.       m_leafModelNum = leafCounter;
  455.     } 
  456.     return leafCounter;
  457.   } 
  458.   /**
  459.    * print the linear model at this node
  460.    */
  461.   public String toString() {
  462.     return printNodeLinearModel();
  463.   } 
  464.   /**
  465.    * print the linear model at this node
  466.    */
  467.   public String printNodeLinearModel() {
  468.     return m_nodeModel.toString();
  469.   } 
  470.   /**
  471.    * print all leaf models
  472.    */
  473.   public String printLeafModels() {
  474.     StringBuffer text = new StringBuffer();
  475.     if (m_isLeaf) {
  476.       text.append("nLM num: " + m_leafModelNum);
  477.       text.append(m_nodeModel.toString());
  478.       text.append("n");
  479.     } else {
  480.       text.append(m_left.printLeafModels());
  481.       text.append(m_right.printLeafModels());
  482.     } 
  483.     return text.toString();
  484.   } 
  485.   /**
  486.    * Returns a description of this node (debugging purposes)
  487.    *
  488.    * @return a string describing this node
  489.    */
  490.   public String nodeToString() {
  491.     StringBuffer text = new StringBuffer();
  492.     System.out.println("In to string");
  493.     text.append("Node:ntnum inst: " + m_numInstances);
  494.     if (m_isLeaf) {
  495.       text.append("ntleaf");
  496.     } else {
  497.       text.append("tnode");
  498.     }
  499.     text.append("ntSplit att: " + m_instances.attribute(m_splitAtt).name());
  500.     text.append("ntSplit val: " + Utils.doubleToString(m_splitValue, 1, 3));
  501.     text.append("ntLM num: " + m_leafModelNum);
  502.     text.append("ntLinear modeln" + m_nodeModel.toString());
  503.     text.append("nn");
  504.     if (m_left != null) {
  505.       text.append(m_left.nodeToString());
  506.     } 
  507.     if (m_right != null) {
  508.       text.append(m_right.nodeToString());
  509.     } 
  510.     return text.toString();
  511.   } 
  512.   /**
  513.    * Recursively builds a textual description of the tree
  514.    *
  515.    * @param level the level of this node
  516.    * @return string describing the tree
  517.    */
  518.   public String treeToString(int level) {
  519.     int  i;
  520.     StringBuffer text = new StringBuffer();
  521.     if (!m_isLeaf) {
  522.       text.append("n");
  523.       for (i = 1; i <= level; i++) {
  524. text.append("|   ");
  525.       } 
  526.       if (m_instances.attribute(m_splitAtt).name().charAt(0) != '[') {
  527. text.append(m_instances.attribute(m_splitAtt).name() + " <= " 
  528.     + Utils.doubleToString(m_splitValue, 1, 3) + " : ");
  529.       } else {
  530. text.append(m_instances.attribute(m_splitAtt).name() + " false : ");
  531.       } 
  532.       if (m_left != null) {
  533. text.append(m_left.treeToString(level + 1));
  534.       } else {
  535. text.append("NULLn");
  536.       }
  537.       for (i = 1; i <= level; i++) {
  538. text.append("|   ");
  539.       } 
  540.       if (m_instances.attribute(m_splitAtt).name().charAt(0) != '[') {
  541. text.append(m_instances.attribute(m_splitAtt).name() + " >  " 
  542.     + Utils.doubleToString(m_splitValue, 1, 3) + " : ");
  543.       } else {
  544. text.append(m_instances.attribute(m_splitAtt).name() + " true : ");
  545.       } 
  546.       if (m_right != null) {
  547. text.append(m_right.treeToString(level + 1));
  548.       } else {
  549. text.append("NULLn");
  550.       }
  551.     } else {
  552.       text.append("LM" + m_leafModelNum);
  553.       if (m_globalDeviation > 0.0) {
  554. text
  555.   .append(" (" + m_numInstances + "/" 
  556.   + Utils.doubleToString((100.0 * m_rootMeanSquaredError /
  557.      m_globalAbsDeviation), 1, 3) 
  558.   + "%)n");
  559.       } else {
  560. text.append(" (" + m_numInstances + ")n");
  561.       } 
  562.     } 
  563.     return text.toString();
  564.   } 
  565.   /**
  566.    * Recursively prune the tree
  567.    *
  568.    * @exception Exception if an error occurs
  569.    */
  570.   public void prune() throws Exception {
  571.     Evaluation nodeModelEval = null;
  572.     if (m_isLeaf) {
  573.       buildLinearModel(m_indices);
  574.       nodeModelEval = new Evaluation(m_reducedI);
  575.       if (m_reducedI == null) {
  576. throw new Exception("No instances at leaf!");
  577.       } 
  578.       // count the constant term as a paramter for a leaf
  579.       // Evaluate the model
  580.       nodeModelEval.evaluateModel(m_nodeModel, m_reducedI);
  581.       m_rootMeanSquaredError = nodeModelEval.rootMeanSquaredError();
  582.     } else {
  583.       // Prune the left and right subtrees
  584.       if (m_left != null) {
  585. m_left.prune();
  586.       } 
  587.       if (m_right != null) {
  588. m_right.prune();
  589.       } 
  590.       
  591.       buildLinearModel(m_indices);
  592.       nodeModelEval = new Evaluation(m_reducedI);
  593.       double rmsModel;
  594.       double adjustedErrorModel;
  595.       nodeModelEval.evaluateModel(m_nodeModel, m_reducedI);
  596.       rmsModel = nodeModelEval.rootMeanSquaredError();
  597.       adjustedErrorModel = rmsModel 
  598. * pruningFactor(m_numInstances, 
  599. m_nodeModel.numParameters() + 1);
  600.       // Evaluate this node (ie its left and right subtrees)
  601.       Evaluation nodeEval = new Evaluation(m_instances);
  602.       double     rmsSubTree;
  603.       double     adjustedErrorNode;
  604.       int  l_params = 0, r_params = 0;
  605.       nodeEval.evaluateModel(this, m_instances);
  606.       rmsSubTree = nodeEval.rootMeanSquaredError();
  607.       if (m_left != null) {
  608. l_params = m_left.numParameters();
  609.       } 
  610.       if (m_right != null) {
  611. r_params = m_right.numParameters();
  612.       } 
  613.       adjustedErrorNode = rmsSubTree 
  614. * pruningFactor(m_numInstances, 
  615. (l_params + r_params + 1));
  616.       if ((adjustedErrorModel <= adjustedErrorNode) 
  617.   || (adjustedErrorModel < (m_globalDeviation * 0.00001))) {
  618. // Choose linear model for this node rather than subtree model
  619. m_isLeaf = true;
  620. m_right = null;
  621. m_left = null;
  622. m_numParameters = m_nodeModel.numParameters() + 1;
  623. m_rootMeanSquaredError = rmsModel;
  624.       } else {
  625. m_numParameters = (l_params + r_params + 1);
  626. m_rootMeanSquaredError = rmsSubTree;
  627.       } 
  628.     }
  629.     // save space
  630.     if (!m_saveInstances) {
  631.       m_instances = new Instances(m_instances, 0);
  632.     }
  633.     m_reducedI = new Instances(m_reducedI, 0);
  634.   } 
  635.   /**
  636.    * Compute the pruning factor
  637.    *
  638.    * @param num_instances number of instances
  639.    * @param num_params number of parameters in the model
  640.    * @return the pruning factor
  641.    */
  642.   private double pruningFactor(int num_instances, int num_params) {
  643.     if (num_instances <= num_params) {
  644.       return 10.0;    // Caution says Yong in his code
  645.     } 
  646.     return ((double) (num_instances + m_pruningMultiplier * num_params) 
  647.     / (double) (num_instances - num_params));
  648.   } 
  649.   /**
  650.    * Find the leaf with greatest coverage
  651.    *
  652.    * @param maxCoverage the greatest coverage found so far
  653.    * @param bestLeaf the leaf with the greatest coverage
  654.    */
  655.   public void findBestLeaf(double[] maxCoverage, RuleNode[] bestLeaf) {
  656.     if (!m_isLeaf) {
  657.       if (m_left != null) {
  658. m_left.findBestLeaf(maxCoverage, bestLeaf);
  659.       } 
  660.       if (m_right != null) {
  661. m_right.findBestLeaf(maxCoverage, bestLeaf);
  662.       } 
  663.     } else {
  664.       if (m_numInstances > maxCoverage[0]) {
  665. maxCoverage[0] = m_numInstances;
  666. bestLeaf[0] = this;
  667.       } 
  668.     } 
  669.   } 
  670.   /**
  671.    * Return a list containing all the leaves in the tree
  672.    *
  673.    * @param v a single element array containing a vector of leaves
  674.    */
  675.   public void returnLeaves(FastVector[] v) {
  676.     if (m_isLeaf) {
  677.       v[0].addElement(this);
  678.     } else {
  679.       if (m_left != null) {
  680. m_left.returnLeaves(v);
  681.       } 
  682.       if (m_right != null) {
  683. m_right.returnLeaves(v);
  684.       } 
  685.     } 
  686.   } 
  687.   /**
  688.    * Get the parent of this node
  689.    *
  690.    * @return the parent of this node
  691.    */
  692.   public RuleNode parentNode() {
  693.     return m_parent;
  694.   } 
  695.   /**
  696.    * Get the left child of this node
  697.    *
  698.    * @return the left child of this node
  699.    */
  700.   public RuleNode leftNode() {
  701.     return m_left;
  702.   } 
  703.   /**
  704.    * Get the right child of this node
  705.    *
  706.    * @return the right child of this node
  707.    */
  708.   public RuleNode rightNode() {
  709.     return m_right;
  710.   } 
  711.   /**
  712.    * Get the index of the splitting attribute for this node
  713.    *
  714.    * @return the index of the splitting attribute
  715.    */
  716.   public int splitAtt() {
  717.     return m_splitAtt;
  718.   } 
  719.   /**
  720.    * Get the split point for this node
  721.    *
  722.    * @return the split point for this node
  723.    */
  724.   public double splitVal() {
  725.     return m_splitValue;
  726.   } 
  727.   /**
  728.    * Get the number of linear models in the tree
  729.    *
  730.    * @return the number of linear models
  731.    */
  732.   public int numberOfLinearModels() {
  733.     if (m_isLeaf) {
  734.       return 1;
  735.     } else {
  736.       return m_left.numberOfLinearModels() + m_right.numberOfLinearModels();
  737.     } 
  738.   } 
  739.   /**
  740.    * Return true if this node is a leaf
  741.    *
  742.    * @return true if this node is a leaf
  743.    */
  744.   private boolean isLeaf() {
  745.     return m_isLeaf;
  746.   } 
  747.   /**
  748.    * Get the root mean squared error at this node
  749.    *
  750.    * @return the root mean squared error
  751.    */
  752.   protected double rootMeanSquaredError() {
  753.     return m_rootMeanSquaredError;
  754.   } 
  755.   /**
  756.    * Get the linear model at this node
  757.    *
  758.    * @return the linear model at this node
  759.    */
  760.   protected LinearRegression getModel() {
  761.     return m_nodeModel;
  762.   } 
  763.   /**
  764.    * Get the number of parameters in the model at this node
  765.    *
  766.    * @return the number of parameters in the model at this node
  767.    */
  768.   private int numParameters() {
  769.     return m_numParameters;
  770.   } 
  771.   /**
  772.    * Get if smoothing is being used
  773.    *
  774.    * @param s true if smoothing is being used
  775.    */
  776.   public void setSmoothing(boolean s) {
  777.     m_smoothPredictions = s;
  778.   } 
  779.   /**
  780.    * Method declaration
  781.    *
  782.    * @return true if smoothing has been selected.
  783.    *
  784.    */
  785.   public boolean getSmoothing() {
  786.     return m_smoothPredictions;
  787.   } 
  788.   
  789.   /**
  790.    * Get the value of regressionTree.
  791.    *
  792.    * @return Value of regressionTree.
  793.    */
  794.   public boolean getRegressionTree() {
  795.     
  796.     return m_regressionTree;
  797.   }
  798.   
  799.   /**
  800.    * Set the value of regressionTree.
  801.    *
  802.    * @param newregressionTree Value to assign to regressionTree.
  803.    */
  804.   public void setRegressionTree(boolean newregressionTree) {
  805.     
  806.     m_regressionTree = newregressionTree;
  807.   }
  808.   
  809.   /**
  810.    * Apply the attribute filter at this node to a set of supplied instances
  811.    *
  812.    * @param inst the instances to apply the filter to
  813.    * @return a filtered set of instances
  814.    * @exception Exception if an error occurs
  815.    */
  816.   protected Instance applyNodeFilter(Instance inst) throws Exception {
  817.     m_attributeFilter.input(inst);
  818.     return m_attributeFilter.output();
  819.   }
  820.   
  821.   /**
  822.    * Print all the linear models at the learf (debugging purposes)
  823.    */
  824.   public void printAllModels() {
  825.     if (m_isLeaf) {
  826.       System.out.println(m_nodeModel.toString());
  827.     } else {
  828.       System.out.println(m_nodeModel.toString());
  829.       m_left.printAllModels();
  830.       m_right.printAllModels();
  831.     } 
  832.   } 
  833.   /**
  834.    * Assigns a unique identifier to each node in the tree
  835.    *
  836.    * @param lastID last id number used
  837.    * @return ID after processing child nodes
  838.    */
  839.   protected int assignIDs(int lastID) {
  840.     int currLastID = lastID + 1;
  841.     m_id = currLastID;
  842.     if (m_left != null) {
  843.       currLastID = m_left.assignIDs(currLastID);
  844.     }
  845.     if (m_right != null) {
  846.       currLastID = m_right.assignIDs(currLastID);
  847.     }
  848.     return currLastID;
  849.   }
  850.   /**
  851.    * Assign a unique identifier to each node in the tree and then
  852.    * calls graphTree
  853.    *
  854.    * @param text a <code>StringBuffer</code> value
  855.    */
  856.   protected void graph(StringBuffer text) {
  857.     assignIDs(-1);
  858.     graphTree(text);
  859.   }
  860.   /**
  861.    * Return a dotty style string describing the tree
  862.    *
  863.    * @param text a <code>StringBuffer</code> value
  864.    */
  865.   protected void graphTree(StringBuffer text) {
  866.     text.append("N" + m_id
  867. + (m_isLeaf 
  868.    ? " [label="LM " + m_leafModelNum
  869.    : " [label="" + m_instances.attribute(m_splitAtt).name())
  870. + (m_isLeaf
  871.  ? " (" + ((m_globalDeviation > 0.0) 
  872.   ?  m_numInstances + "/" 
  873.      + Utils.doubleToString((100.0 * 
  874.      m_rootMeanSquaredError /
  875.      m_globalAbsDeviation), 
  876.     1, 3) 
  877.      + "%)"
  878.    : m_numInstances + ")")
  879.     + "" shape=box style=filled "
  880.    : """)
  881. + (m_saveInstances
  882.    ? "data=n" + m_instances + "n,n"
  883.    : "")
  884. + "]n");
  885.     if (m_left != null) {
  886.       text.append("N" + m_id + "->" + "N" + m_left.m_id + " [label="<="
  887.   + Utils.doubleToString(m_splitValue, 1, 3)
  888.   + ""]n");
  889.       m_left.graphTree(text);
  890.     }
  891.      
  892.     if (m_right != null) {
  893.       text.append("N" + m_id + "->" + "N" + m_right.m_id + " [label=">"
  894.   + Utils.doubleToString(m_splitValue, 1, 3)
  895.   + ""]n");
  896.       m_right.graphTree(text);
  897.     }
  898.   }
  899.   /**
  900.    * Set whether to save instances for visualization purposes.
  901.    * Default is to save memory.
  902.    *
  903.    * @param save a <code>boolean</code> value
  904.    */
  905.   protected void setSaveInstances(boolean save) {
  906.     m_saveInstances = save;
  907.   }
  908. }