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

Windows Develop

Development Platform:

Java

  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.  *    Node.java
  18.  *    Copyright (C) 1999 Yong Wang
  19.  *
  20.  */
  21. package weka.classifiers.m5;
  22. import java.io.*;
  23. import java.util.*;
  24. import weka.core.*;
  25. /**
  26.  * Class for handing a node in the tree or the subtree under this node
  27.  * @author Yong Wang (yongwang@cs.waikato.ac.nz)
  28.  * @version $Revision: 1.5 $
  29.  */
  30. public final class Node implements Serializable {
  31.   boolean  type;           // = true, NODE;  = false, LEAF 
  32.   int      splitAttr;      // splitting attribute 
  33.   double   splitValue;     // the value of the splitting attribute at the 
  34.                            // splitting position
  35.   Function unsmoothed;     // unsmoothed function
  36.   Function smoothed;       // smoothed function
  37.   boolean  valueNode;      // =true, if use the constant term as the 
  38.                            // predicting function 
  39.   Node     upNode;         // pointer of the up node 
  40.   Node     leftNode;       // pointer of the left node
  41.   Node     rightNode;      // pointer of the right node
  42.   Errors   errors;         // evaluation errors of the model under this node
  43.   int      numParameters;  // number of parameters of the chosen model for this
  44.                            // node, either the subtree model or the linear model
  45.   SplitInfo sf;            // Spliting infomation 
  46.   int      lm;             // linear model number at the leaf; for NODE, lm = 0;
  47.   Instances instances;  // instances reaching this node
  48.   int model;               // model type: LINEAR REGRESSION, REGRESSION_TREE, 
  49.                            // MODEL_TYPE
  50.   double pruningFactor;    // to control the tree size
  51.   double deviation;        // deviation of the gobal class variable
  52.   final static int      LINEAR_REGRESSION=1;
  53.   final static int      REGRESSION_TREE=2;
  54.   final static int      MODEL_TREE=3;
  55.   final static double  SPLIT_NUM = 3.5;   // a node will not be further split 
  56.                                 // if it contains instances less than SPLIT_NUM 
  57.   /**
  58.    * Constructs a new node
  59.    * @param inst instances
  60.    * @param up the parent node
  61.    */
  62.   public Node(Instances inst, Node up){
  63.     int i;
  64.     type = true;
  65.     unsmoothed = new Function();
  66.     smoothed = new Function();
  67.     valueNode = true;
  68.     upNode = up;
  69.     leftNode = null;
  70.     rightNode = null;
  71.     errors = null; 
  72.     numParameters = 0;
  73.     instances = inst;
  74.     lm = 0;
  75.     if(up != null) {
  76.       model = up.model;
  77.       pruningFactor = up.pruningFactor;
  78.       deviation = up.deviation;
  79.     }
  80.   }
  81.   
  82.   /**
  83.    * Constructs the root of a tree 
  84.    * @param inst instances
  85.    * @param up the parent node
  86.    * @param options the options
  87.    */
  88.   public Node(Instances inst, Node up,Options options){
  89.     int i;
  90.     type = true;
  91.     unsmoothed = new Function();
  92.     smoothed = new Function();
  93.     valueNode = true;
  94.     upNode = up;
  95.     leftNode = null;
  96.     rightNode = null;
  97.     errors = null;
  98.     numParameters = 0;
  99.     instances = inst;
  100.     lm = 0;
  101.     
  102.     model = options.model;
  103.     pruningFactor = options.pruningFactor;
  104.     deviation = options.deviation;
  105.   }
  106.   
  107.   /** 
  108.    * Converts the information stored at this node to a string
  109.    * @return the converted string
  110.    * @exception Exception if something goes wrong
  111.    */
  112.   public final String  singleNodeToString() throws Exception {
  113.     StringBuffer text = new StringBuffer();
  114.         
  115.     text.append("Print single node (" + instances.numInstances() + "):n");
  116.     if(type==true)text.append("    Type:tttNODEn");
  117.     else text.append("    Type:tttLEAFn");
  118.     text.append("    Unsmoothed function:tt");
  119.     unsmoothed.toString(instances,0);
  120.     System.out.print("    Smoothed function:tt");
  121.     smoothed.toString(instances,0);
  122.     text.append("    Value node:ttt" + valueNode + "n");
  123.     if(errors!=null)text.append(errors.toString());
  124.     if(upNode != null) text.append("    upNode:tttyesn"); 
  125.     else text.append("    upNode:tttnulln"); 
  126.     if(leftNode != null) text.append("    leftNode:tttyesn"); 
  127.     else text.append("    leftNode:tttnulln"); 
  128.     if(rightNode != null) text.append("    rightNode:tttyesn"); 
  129.     else text.append("    rightNode:tttnulln"); 
  130.     text.append("    number of parameters:t" + numParameters +"n");
  131.     text.append("    LEAF number(lm):tt" + lm +"n");
  132.     text.append("    Number of instancestt" + instances.numInstances() +"n");
  133.     return text.toString();
  134.   }
  135.   /**
  136.    * Converts the tree under this node to a string
  137.    * @param treeLevel the depth of this node; 
  138.    *        the root of a tree should have treeLevel = 0
  139.    * @param deviation the global deviation of the class column, 
  140.    *        used for evaluating relative errors
  141.    * @return the converted string
  142.    */
  143.   public final String  treeToString(int treeLevel,double deviation) {
  144.     
  145.     int i;
  146.     StringBuffer text = new StringBuffer();
  147.     
  148.     if(type ==  true) {
  149.       text.append("n");
  150.       for(i=1;i<=treeLevel;i++)text.append("|   ");
  151.       if(instances.attribute(splitAttr).name().charAt(0) != '[') 
  152. text.append(instances.attribute(splitAttr).name() + " <= " + 
  153.     M5Utils.doubleToStringG(splitValue,1,3) + " : ");
  154.       else text.append(instances.attribute(splitAttr).name() + " false : ");
  155.       treeLevel++;
  156.       text.append(leftNode.treeToString(treeLevel,deviation));
  157.       treeLevel--;
  158.       for(i=1;i<=treeLevel;i++)text.append("|   ");
  159.       if(instances.attribute(splitAttr).name().charAt(0) != '[') 
  160. text.append(instances.attribute(splitAttr).name() + " >  " + 
  161.     M5Utils.doubleToStringG(splitValue,1,3) + " : ");
  162.       else text.append(instances.attribute(splitAttr).name() + " true : ");
  163.       treeLevel++;
  164.       text.append(rightNode.treeToString(treeLevel,deviation));
  165.       treeLevel--;
  166.     }
  167.     else{                             // LEAF
  168.       text.append("LM" + lm);
  169.       if(deviation > 0.0)
  170. text.append(" (" + instances.numInstances() + "/" + 
  171.     M5Utils.doubleToStringG((100. * errors.rootMeanSqrErr / 
  172.      deviation),1,3) + "%)n");
  173.       else text.append(" (" + instances.numInstances() + ")n");
  174.     }
  175.       
  176.     return text.toString();
  177.   }
  178.   /**
  179.    * Counts the number of linear models in the tree.
  180.    */
  181.   public final int numberOfLinearModels() {
  182.     if (type == false) {
  183.       return 1;
  184.     } else {
  185.       return leftNode.numberOfLinearModels() + 
  186. rightNode.numberOfLinearModels();
  187.     }
  188.   }
  189.   
  190.   /**
  191.    * Converts all the linear models at the leaves under the node to a string
  192.    * @param smooth either the smoothed models if true, otherwise 
  193.    *        the unsmoothed are converted
  194.    * @return the converted string
  195.    * @exception Exception if something goes wrong
  196.    */
  197.   public final String  formulaeToString(boolean smooth) throws Exception {
  198.     int startingPoint;
  199.     StringBuffer text = new StringBuffer();
  200.     if(type == true) {
  201.       text.append(leftNode.formulaeToString(smooth));
  202.       text.append(rightNode.formulaeToString(smooth));
  203.     }
  204.     else {
  205.       text.append("    LM" + lm + ":  ");
  206.       startingPoint = 6 + (int)(Math.log(lm +0.5)/Math.log(10)) + 1 + 3;
  207.       if(smooth == true) text.append(smoothed.toString(instances,startingPoint));
  208.       else text.append(unsmoothed.toString(instances,startingPoint));
  209.       text.append("n");
  210.     }
  211.     return text.toString();
  212.   }
  213.   
  214.   /**
  215.    * Sets the leaves' numbers
  216.    * @param leafCounter the number of leaves counted
  217.    * @return the number of the total leaves under the node
  218.    */
  219.   public final int  numLeaves(int leafCounter)
  220.   {
  221.     if(type == true) {        // NODE
  222.       lm = 0;
  223.       leafCounter = leftNode.numLeaves(leafCounter);
  224.       leafCounter = rightNode.numLeaves(leafCounter);
  225.     }
  226.     else{                             // LEAF
  227.       leafCounter++;
  228.       lm = leafCounter;
  229.     }
  230.     return leafCounter;
  231.   }
  232.   
  233.   /**
  234.    * Splits the node recursively, unless there are few instances or 
  235.    *     instances have similar values of the class attribute 
  236.    * @param inst instances
  237.    * @exception Exception if something goes wrong
  238.    */
  239.   public final void  split(Instances inst) throws Exception {
  240.     SplitInfo s,sMax;
  241.     int j, partition;
  242.     Instances leftInst,rightInst;
  243.     instances = inst;
  244.     if(instances.numInstances() < SPLIT_NUM  || 
  245.        M5Utils.stdDev(instances.classIndex(),instances) < deviation * 0.05) 
  246.       type = false;
  247.     else {  
  248.       sMax = new SplitInfo(0,instances.numInstances()-1,-1);
  249.       s = new SplitInfo(0,instances.numInstances()-1,-1);
  250.       for(j=0;j<instances.numAttributes();j++){
  251. if(j != instances.classIndex()){
  252.   instances.sort(instances.attribute(j));
  253.   s.attrSplit(j,instances);
  254.   if((Math.abs(s.maxImpurity - sMax.maxImpurity) > 1.e-6)  && 
  255.      (s.maxImpurity > sMax.maxImpurity + 1.e-6)) 
  256.     sMax = s.copy(); 
  257. }
  258.       }
  259.       
  260.       if(sMax.splitAttr < 0 || sMax.position < 1 || sMax.position > 
  261.  instances.numInstances()-1) type = false;
  262.       if(type == true){
  263. sf = sMax;
  264. splitAttr = sMax.splitAttr;               // split attribute
  265. splitValue = sMax.splitValue;             // split value
  266. unsmoothed = new Function(splitAttr);     // unsmoothed function
  267. leftInst = new Instances(instances, instances.numInstances());
  268. rightInst = new Instances(instances, instances.numInstances());
  269. for (int i = 0; i < instances.numInstances(); i++)
  270.   if (instances.instance(i).value(splitAttr) <= splitValue)
  271.     leftInst.add(instances.instance(i));
  272.   else
  273.     rightInst.add(instances.instance(i));
  274. leftInst.compactify();
  275. rightInst.compactify();
  276. leftNode = new Node(leftInst,this);
  277. leftNode.split(leftInst);                 // split left node
  278. rightNode = new Node(rightInst,this);
  279. rightNode.split(rightInst);                // split right node
  280. this.valueNode();                      // function of the constant value 
  281. if(model != REGRESSION_TREE){
  282.   unsmoothed = Function.combine(unsmoothed,leftNode.unsmoothed); 
  283.                  // passes up the attributes found under the left node
  284.   unsmoothed = Function.combine(unsmoothed,rightNode.unsmoothed); 
  285.                  // passes up the attributes found under the right node
  286. }
  287. else unsmoothed = new Function();
  288.       }
  289.     }
  290.     if(type==false){                              // a leaf node
  291.       this.leafNode();
  292.       errors = unsmoothed.errors(instances);
  293.     }
  294.   }
  295.   
  296.   /**
  297.    * Sets the node to a leaf
  298.    * @exception Exception if something goes wrong
  299.    */
  300.   public final void  leafNode() throws Exception {
  301.     type = false;
  302.     unsmoothed.terms[0] = 0;
  303.     this.valueNode();
  304.   }
  305.   
  306.   /**
  307.    * Takes a constant value as the function at the node
  308.    * @exception Exception if something goes wrong
  309.    */
  310.   public final void  valueNode() throws Exception {
  311.     int i;
  312.     valueNode = true;
  313.     unsmoothed.coeffs[0] = 0.0;
  314.     for(i=0;i<=instances.numInstances()-1;i++)
  315.       unsmoothed.coeffs[0] += instances.instance(i).classValue();
  316.     unsmoothed.coeffs[0] /= instances.numInstances();
  317.   }
  318.   /**
  319.    * Prunes the model tree
  320.    * @param modelType determines what kind a model is constructed, a model tree, 
  321.    *      a regression tree or a simple linear regression
  322.    * @param pruningFactor the pruning factor influences the size of the pruned tree
  323.    * @exception Exception if something goes wrong
  324.    */
  325.   public final void  prune() throws Exception {
  326.     
  327.     int list[];
  328.     double eps1,eps2,va;
  329.     Errors e1,e2;
  330.     Function function;
  331.     
  332.     if(type == false){                        // LEAF
  333.       errors = unsmoothed.errors(instances);
  334.       numParameters = 1;
  335.     }
  336.     else {                                    // NODE
  337.       if(model == LINEAR_REGRESSION){         // outputs linear regression model
  338. function = new Function(instances);
  339. if(function.terms[0] < Math.sqrt(instances.numInstances())*2.0 && 
  340.    function.terms[0] < 50)unsmoothed = function;
  341. this.regression(unsmoothed);
  342. valueNode = false;
  343. errors = unsmoothed.errors(instances);
  344. type = false;
  345.       }
  346.       else {                                  
  347. leftNode.prune();                     // prunes the left node
  348. rightNode.prune();                    // pruned the right node
  349. if(model != REGRESSION_TREE){         // model tree
  350.   unsmoothed = Function.combine(unsmoothed,leftNode.unsmoothed);
  351.   unsmoothed = Function.combine(unsmoothed,rightNode.unsmoothed);
  352. }
  353. else unsmoothed = new Function();     // regression tree
  354.         numParameters = leftNode.numParameters + rightNode.numParameters + 1;
  355.       
  356. this.function();                      // computes linear model at node
  357. e1 = unsmoothed.errors(instances);    // errors of the linear model
  358. eps1 = e1.rootMeanSqrErr * this.factor(instances.numInstances(),
  359.    unsmoothed.terms[0]+1,pruningFactor);
  360. e2 = this.errors(instances,false);    // errors of the subtree
  361. eps2 = e2.rootMeanSqrErr * this.factor(instances.numInstances(),
  362.    numParameters,pruningFactor);
  363. errors = e2;
  364. if(eps1 <= eps2 || eps1 < deviation * 0.00001) { // chooses linear model
  365.   type = false;
  366.   numParameters = unsmoothed.terms[0]+1;
  367.   errors = e1;
  368. }
  369.       } 
  370.     }
  371.   }
  372.   /**
  373.    * Computes the coefficients of a linear model using the instances at this 
  374.    *      node
  375.    * @param function the linear model containing the index of the attributes; 
  376.    *      coefficients are to be computed
  377.    */
  378.   public final void  regression(Function function){
  379.     
  380.     int i,j,n,k;
  381.     Matrix x,y;
  382.     
  383.     n = instances.numInstances();
  384.     k = function.terms[0]+1;
  385.     x = new Matrix(n,k);
  386.     y = new Matrix(n,1);
  387.     for(i=0;i<=n-1;i++){
  388.       x.elements[i][0] = 1.0;
  389.       for(j=1;j<=k-1;j++)
  390. x.elements[i][j] = instances.instance(i).value(function.terms[j]);
  391.       y.elements[i][0] = instances.instance(i).value(instances.classIndex());
  392.     }
  393.     function.coeffs = x.regression(y,n,k); 
  394.   }
  395.   
  396.   /**
  397.    * Finds the appropriate order of the unsmoothed linear model at this node
  398.    * @exception Exception if something goes wrong
  399.    */
  400.   public final void  function() throws Exception {
  401.     int n,jmin,flag=0;
  402.     double err1,err2,sdy;
  403.     Errors e1,e2;
  404.     Function f1 = unsmoothed;
  405.     Function f2;
  406.     if(f1.terms[0]!=0){
  407.       sdy = M5Utils.stdDev(instances.classIndex(),instances);
  408.       this.regression(f1);
  409.       valueNode = false;
  410.       if(model != LINEAR_REGRESSION){
  411. e1 = f1.errors(instances);
  412. err1 = e1.rootMeanSqrErr * this.factor(instances.numInstances(),
  413.    f1.terms[0]+1,pruningFactor);
  414. flag = 0;
  415. while(flag==0){
  416.   jmin = f1.insignificant(sdy,instances);
  417.   if(jmin==-1)flag = 1;
  418.   else {
  419.     f2 = f1.remove(jmin);
  420.     this.regression(f2);
  421.     e2 = f2.errors(instances);
  422.     err2 = e2.rootMeanSqrErr * this.factor(instances.numInstances(),
  423.        f2.terms[0]+1,pruningFactor);
  424.     if(err2 > err1 && err2 > deviation * 0.00001) flag = 1;
  425.     else {        // compare estimated error with and without attr jmin
  426.       f1 = f2;
  427.       err1 = err2;
  428.       if(f1.terms[0]==0) flag = 1;
  429.     }
  430.   }
  431. }
  432.       }
  433.       unsmoothed = f1;
  434.     }
  435.     if(unsmoothed.terms[0] == 0){      // constant function without attributes
  436.       this.valueNode();
  437.     } 
  438.   }
  439.   
  440.   /**
  441.    * Calculates a multiplication factor used at this node
  442.    * @param n the number of instances
  443.    * @param v the number of the coefficients
  444.    * @pruningFactor the pruning factor
  445.    * @return multiplication factor
  446.    */
  447.   public final double  factor(int n,int v,double pruningFactor) {
  448.     double factor=0.0;
  449.     
  450.     if(n <= v)return 10.0;  /* Caution */
  451.     factor = (double)(n+pruningFactor*v)/(double)(n-v);
  452.     
  453.     return factor;
  454.   }
  455.   /**
  456.    * Smoothens all unsmoothed formulae at the tree leaves under this node.
  457.    */
  458.   public final void  smoothen()
  459.   {
  460.     if (type == false) {
  461.       smoothed = unsmoothed.copy(); 
  462.       if(upNode != null)
  463. this.smoothenFormula(this);
  464.     }
  465.     else {
  466.       leftNode.smoothen();
  467.       rightNode.smoothen();
  468.     }
  469.   }
  470.   
  471.   /**
  472.    * Recursively smoothens the unsmoothed linear model at this node with the 
  473.    *     unsmoothed linear models at the nodes above this
  474.    * @param current the unsmoothed linear model at the up node of the 
  475.    *     'current' will be used for smoothening
  476.    */
  477.   public final void  smoothenFormula(Node current) {
  478.     int i=smoothed.terms[0],j=current.upNode.unsmoothed.terms[0],k,l,
  479.       smoothingConstant=15;
  480.     Function function;
  481.     
  482.     function = Function.combine(smoothed,current.upNode.unsmoothed);
  483.     
  484.     function.coeffs[0] = 
  485.       M5Utils.smoothenValue(smoothed.coeffs[0],
  486.     current.upNode.unsmoothed.coeffs[0],
  487.     current.instances.numInstances(),
  488.     smoothingConstant);
  489.     for(k=function.terms[0];k>=1;k--){
  490.       if(i>=1 && j>=1){
  491. if(function.terms[k]==smoothed.terms[i] && function.terms[k]==
  492.    current.upNode.unsmoothed.terms[j]){
  493.   function.coeffs[k] = 
  494.     M5Utils.smoothenValue(smoothed.coeffs[i],
  495.   current.upNode.unsmoothed.coeffs[j],
  496.   current.instances.numInstances(),
  497.   smoothingConstant);
  498.   i--;j--;
  499. }
  500. else if(function.terms[k]==smoothed.terms[i] && 
  501. function.terms[k]!=current.upNode.unsmoothed.terms[j]){
  502.   function.coeffs[k] = 
  503.     M5Utils.smoothenValue(smoothed.coeffs[i],
  504.   0.0,
  505.   current.instances.numInstances(),
  506.   smoothingConstant);
  507.   i--;
  508. }
  509. else if(function.terms[k]!=smoothed.terms[i] && 
  510. function.terms[k]==current.upNode.unsmoothed.terms[j]){
  511.   function.coeffs[k] = 
  512.     M5Utils.smoothenValue(0.0,
  513.   current.upNode.unsmoothed.coeffs[j],
  514.   current.instances.numInstances(),
  515.   smoothingConstant);
  516.   j--;
  517. }
  518. else M5Utils.errorMsg("wrong terms value in smoothing_formula().");
  519.       }
  520.       else if(i<1&&j<1)break;
  521.       else if(j>=1){
  522. for(l=k;l>=1;l--) 
  523.   function.coeffs[l] = 
  524.     M5Utils.smoothenValue(0.0,
  525.   current.upNode.unsmoothed.coeffs[j--],
  526.   current.instances.numInstances(),
  527.   smoothingConstant);
  528. break;
  529.       }
  530.       else {
  531. for(l=k;l>=1;l--) 
  532.   function.coeffs[l] = 
  533.     M5Utils.smoothenValue(smoothed.coeffs[i--],
  534.   0.0,
  535.   current.instances.numInstances(),
  536.   smoothingConstant);
  537. break;
  538.       }
  539.     }
  540.     smoothed = function;
  541.     if(current.upNode.upNode != null) this.smoothenFormula(current.upNode);
  542.   }
  543.   
  544.   /**
  545.    * Converts the predictions by the tree under this node to a string
  546.    * @param insta instances
  547.    * @param smooth =ture using the smoothed models; otherwise, the unsmoothed
  548.    * @return the converted string
  549.    * @exception Exception if something goes wrong
  550.    */
  551.   public final String  predictionsToString(Instances inst,int lmNo,
  552.    boolean smooth) throws Exception {
  553.     int i,lmNum;
  554.     double value;
  555.     StringBuffer text = new StringBuffer();
  556.     
  557.     text.append("    Predicting test instances (" + 
  558. inst.attribute(inst.classIndex()).name() + ", column " 
  559. + (inst.classIndex()+1) +")nn");
  560.     for(i=0;i<=inst.numInstances()-1;i++){
  561.       lmNum = this.leafNum(inst.instance(i));
  562.       if(lmNo==0 || lmNo==lmNum){
  563. text.append("      Predicting " + i + " (LM" + lmNum + "):  ");
  564. text.append(inst.instance(i).toString() + "n");
  565. value = this.predict(inst.instance(i),smooth);
  566. if(inst.instance(i).classIsMissing() == false)
  567.   text.append("      Actual value: " + 
  568.       M5Utils.doubleToStringG(inst.instance(i).classValue(),9,4) + 
  569.       "    Prediction: " + M5Utils.doubleToStringG(value,9,4) + 
  570.       "    Abs. error: " + M5Utils.doubleToStringG(Math.abs(inst.instance(i).classValue()-value),9,4) + 
  571.       "nn");
  572. else text.append("      Actual value:   missing    Prediction: " + 
  573.  M5Utils.doubleToStringG(value,9,4) + 
  574.  "    Abs. Error: undefinednn");
  575.       }
  576.     }
  577.     return text.toString();
  578.   }  
  579.  
  580.   /**
  581.    * Detects which leaf a instance falls into
  582.    * @param i instance i
  583.    * @param inst instances
  584.    * @return the leaf no.
  585.    */
  586.   public final int  leafNum(Instance instance) {
  587.     
  588.     int lmNum=0;
  589.     
  590.     if(type == false){
  591.       lmNum = lm;
  592.     }
  593.     else {
  594.       if (instance.value(splitAttr) <= splitValue)
  595. lmNum = leftNode.leafNum(instance);
  596.       else lmNum = rightNode.leafNum(instance);
  597.     }
  598.     
  599.     return lmNum;
  600.   }
  601.   /**
  602.    * Predicts the class value of an instance by the tree 
  603.    * @param i instance i
  604.    * @smooth =true, uses the smoothed model; otherwise uses the unsmoothed 
  605.    * @inst instances
  606.    * @return the predicted value
  607.    */
  608.   public final double  predict(Instance instance,boolean smooth) {
  609.     double y=0.0;
  610.     
  611.     if(type == false) {                 // LEAF
  612.       if(smooth==true){
  613. y = smoothed.predict(instance);
  614.       }
  615.       else {
  616. if(valueNode == true) y = unsmoothed.coeffs[0];
  617. else y = unsmoothed.predict(instance);
  618.       }
  619.     }
  620.     else {                             // NODE
  621.       if(instance.value(splitAttr) <= splitValue)
  622. y = leftNode.predict(instance,smooth);
  623.       else y = rightNode.predict(instance,smooth);
  624.     }
  625.     return y;
  626.   }
  627.   /**
  628.    * Evaluates a tree
  629.    * @param inst instances
  630.    * @smooth =true, evaluates the smoothed models; 
  631.    *         =false, evaluats the unsmoothed models
  632.    * @return the evaluation results
  633.    * @exception Exception if something goes wrong
  634.    */
  635.   public final Errors  errors(Instances inst,boolean smooth) throws Exception 
  636.   {
  637.     int i;
  638.     double tmp;
  639.     Errors e = new Errors(0,inst.numInstances()-1);
  640.     
  641.     for(i=0;i<=inst.numInstances()-1;i++){
  642.       tmp = this.predict(inst.instance(i),smooth) - inst.instance(i).classValue();
  643.       e.sumErr += tmp;
  644.       e.sumAbsErr += Math.abs(tmp);
  645.       e.sumSqrErr += tmp * tmp;
  646.     }
  647.     
  648.     e.meanAbsErr = e.sumAbsErr / e.numInstances;
  649.     e.meanSqrErr = (e.sumSqrErr - e.sumErr * e.sumErr/e.numInstances)
  650.       / e.numInstances;
  651.     e.meanSqrErr = Math.abs(e.meanSqrErr);
  652.     e.rootMeanSqrErr = Math.sqrt(e.meanSqrErr);
  653.     
  654.     return e;
  655.   }
  656.   /**
  657.    * Computes performance measures of a tree
  658.    * @param inst instances
  659.    * @param smooth =true uses the smoothed models; 
  660.    *        otherwise uses the unsmoothed models
  661.    * @return the performance measures
  662.    * @exception Exception if something goes wrong
  663.    */
  664.   public final Measures  measures(Instances inst,boolean smooth) throws Exception {
  665.     int i,numInstances,count;
  666.     double sd,y1[],y2[];
  667.     Measures measures = new Measures();
  668.     
  669.     errors = this.errors(inst,smooth);
  670.     numInstances = errors.numInstances -  errors.missingInstances;
  671.     y1 = new double[numInstances];
  672.     y2 = new double[numInstances];
  673.     count=0;
  674.     for(i=0;i<=inst.numInstances()-1;i++){
  675.       y1[count] = this.predict(inst.instance(i),smooth);
  676.       y2[count] = inst.instance(i).classValue();
  677.       count++;
  678.     }
  679.     measures.correlation = M5Utils.correlation(y1,y2,numInstances);
  680.     sd = M5Utils.stdDev(inst.classIndex(),inst);
  681.     if(sd > 0.0){
  682.       measures.meanAbsErr = errors.meanAbsErr;
  683.       measures.meanSqrErr = errors.meanSqrErr;
  684.       measures.type=0;
  685.     } 
  686.     else {
  687.       if(numInstances >= 1){
  688. measures.type=1;
  689. measures.meanAbsErr = errors.meanAbsErr;
  690. measures.meanSqrErr = errors.meanSqrErr;
  691.       }
  692.       else {
  693. measures.type=2;
  694. measures.meanAbsErr = 0.0;
  695. measures.meanSqrErr = 0.0;
  696.       }
  697.     }
  698.     
  699.     return measures;
  700.   }
  701.   /**
  702.    * Computes performance measures for both unsmoothed and smoothed models
  703.    * @param inst instances
  704.    * @exception Exception if something goes wrong
  705.    */
  706.   public final Measures[]  validation(Instances inst) throws Exception {
  707.     Measures measures[] = new Measures[2];
  708.     // without smoothing 
  709.     measures[0] = this.measures(inst,false);
  710.     
  711.     // with smoothing 
  712.     if(model == MODEL_TREE){
  713.       measures[1] = this.measures(inst,true);
  714.     }
  715.     else measures[1] = new Measures();
  716.     
  717.     return measures;
  718.   }
  719.   
  720.   /**
  721.    * Makes a copy of the tree under this node
  722.    * @param up the parant node of the new node
  723.    * @return a copy of the tree under this node
  724.    * @exception Exception if something goes wrong
  725.    */
  726.   public final Node  copy(Node up) throws Exception {
  727.     Node node = new Node(instances,upNode);
  728.     node.type = type;
  729.     node.splitAttr = splitAttr;
  730.     node.splitValue = splitValue;
  731.     node.unsmoothed = unsmoothed.copy();
  732.     node.smoothed = smoothed.copy();
  733.     node.valueNode = valueNode;
  734.     node.upNode = up;
  735.     if(errors == null) node.errors = null;
  736.     else node.errors = errors.copy();
  737.     node.numParameters = node.numParameters;
  738.     if(sf == null) node.sf = null;
  739.     else node.sf = sf.copy();
  740.     node.instances = new Instances(instances,0,
  741.     instances.numInstances());
  742.     node.lm = lm;
  743.     node.model = model;
  744.     node.pruningFactor = pruningFactor;
  745.     node.deviation = deviation;
  746.     if(leftNode != null) node.leftNode = leftNode.copy(node); 
  747.     else node.leftNode = null;
  748.     if(rightNode != null) node.rightNode = rightNode.copy(node); 
  749.     else node.rightNode = null;
  750.     return node;
  751.   }
  752.   /**
  753.    * Converts the performance measures into a string
  754.    * @param measures[] contains both the unsmoothed and smoothed measures
  755.    * @param inst the instances
  756.    * @param lmNo also converts the predictions by all linear models if lmNo=0, 
  757.    *        or one linear model spedified by lmNo.
  758.    * @param verbosity the verbosity level
  759.    * @param str the type of evaluation, one of 
  760.    *        "t" for training, "T" for testing, 
  761.    *        "f" for fold training, "F" for fold testing, 
  762.    *        "x" for cross-validation
  763.    * @return the converted string
  764.    * @exception Exception if something goes wrong
  765.    */
  766.   public final String  measuresToString(Measures measures[],Instances 
  767. inst,int lmNo,int verbosity,String str) throws Exception {
  768.     StringBuffer text = new StringBuffer();
  769.     double absDev,sd;
  770.     absDev = M5Utils.absDev(inst.classIndex(),inst);
  771.     sd = M5Utils.stdDev(inst.classIndex(),inst);
  772.     
  773.     text.append("  Without smoothing:nn");
  774.     if((verbosity>=2 || lmNo !=0) && 
  775.        (str.equals("T") == true || str.equals("F") == true)) 
  776.       text.append(predictionsToString(inst,lmNo,false));
  777.     text.append(measures[0].toString(absDev,sd,str,"u") + "nn");
  778.     text.append("  With smoothing:nn");
  779.     if((verbosity>=2 || lmNo !=0) && 
  780.        (str.equals("T") == true || str.equals("F") == true)) 
  781.       text.append(this.predictionsToString(inst,lmNo,true));
  782.     text.append(measures[1].toString(absDev,sd,str,"s") + "nn");
  783.     
  784.     return text.toString();
  785.   }  
  786. }