J48.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 16k
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.  *    J48.java
  18.  *    Copyright (C) 1999 Eibe Frank
  19.  *
  20.  */
  21. package weka.classifiers.trees.j48;
  22. import java.util.*;
  23. import weka.core.*;
  24. import weka.classifiers.*;
  25. /**
  26.  * Class for generating an unpruned or a pruned C4.5 decision tree.
  27.  * For more information, see<p>
  28.  *
  29.  * Ross Quinlan (1993). <i>C4.5: Programs for Machine Learning</i>, 
  30.  * Morgan Kaufmann Publishers, San Mateo, CA. </p>
  31.  *
  32.  * Valid options are: <p>
  33.  *
  34.  * -U <br>
  35.  * Use unpruned tree.<p>
  36.  *
  37.  * -C confidence <br>
  38.  * Set confidence threshold for pruning. (Default: 0.25) <p>
  39.  *
  40.  * -M number <br>
  41.  * Set minimum number of instances per leaf. (Default: 2) <p>
  42.  *
  43.  * -R <br>
  44.  * Use reduced error pruning. No subtree raising is performed. <p>
  45.  *
  46.  * -N number <br>
  47.  * Set number of folds for reduced error pruning. One fold is
  48.  * used as the pruning set. (Default: 3) <p>
  49.  *
  50.  * -B <br>
  51.  * Use binary splits for nominal attributes. <p>
  52.  *
  53.  * -S <br>
  54.  * Don't perform subtree raising. <p>
  55.  *
  56.  * -L <br>
  57.  * Do not clean up after the tree has been built. <p>
  58.  *
  59.  * -A <br>
  60.  * If set, Laplace smoothing is used for predicted probabilites. <p>
  61.  *
  62.  *
  63.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  64.  * @version $Revision: 1.24 $
  65.  */
  66. public class J48 extends DistributionClassifier implements OptionHandler, 
  67.   Drawable, Matchable, Sourcable, WeightedInstancesHandler, Summarizable,
  68.   AdditionalMeasureProducer {
  69.   // To maintain the same version number after adding m_ClassAttribute
  70.   static final long serialVersionUID = -217733168393644444L;
  71.   /** The decision tree */
  72.   private ClassifierTree m_root;
  73.   
  74.   /** Unpruned tree? */
  75.   private boolean m_unpruned = false;
  76.   /** Confidence level */
  77.   private float m_CF = 0.25f;
  78.   /** Minimum number of instances */
  79.   private int m_minNumObj = 2;
  80.   /** Determines whether probabilities are smoothed using
  81.       Laplace correction when predictions are generated */
  82.   private boolean m_useLaplace = false;
  83.   /** Use reduced error pruning? */
  84.   private boolean m_reducedErrorPruning = false;
  85.   /** Number of folds for reduced error pruning. */
  86.   private int m_numFolds = 3;
  87.   /** Binary splits on nominal attributes? */
  88.   private boolean m_binarySplits = false;
  89.   /** Subtree raising to be performed? */
  90.   private boolean m_subtreeRaising = true;
  91.   /** Cleanup after the tree has been built. */
  92.   boolean m_noCleanup = false;
  93.   
  94.   /**
  95.    * Generates the classifier.
  96.    *
  97.    * @exception Exception if classifier can't be built successfully
  98.    */
  99.   public void buildClassifier(Instances instances) 
  100.        throws Exception{
  101.     ModelSelection modSelection;  
  102.     if (m_binarySplits)
  103.       modSelection = new BinC45ModelSelection(m_minNumObj, instances);
  104.     else
  105.       modSelection = new C45ModelSelection(m_minNumObj, instances);
  106.     if (!m_reducedErrorPruning)
  107.       m_root = new C45PruneableClassifierTree(modSelection, !m_unpruned, m_CF,
  108.     m_subtreeRaising, !m_noCleanup);
  109.     else
  110.       m_root = new PruneableClassifierTree(modSelection, !m_unpruned, m_numFolds,
  111.    !m_noCleanup);
  112.     m_root.buildClassifier(instances);
  113.     if (m_binarySplits) {
  114.       ((BinC45ModelSelection)modSelection).cleanup();
  115.     } else {
  116.       ((C45ModelSelection)modSelection).cleanup();
  117.     }
  118.   }
  119.   /**
  120.    * Classifies an instance.
  121.    *
  122.    * @exception Exception if instance can't be classified successfully
  123.    */
  124.   public double classifyInstance(Instance instance) throws Exception {
  125.     return m_root.classifyInstance(instance);
  126.   }
  127.   /** 
  128.    * Returns class probabilities for an instance.
  129.    *
  130.    * @exception Exception if distribution can't be computed successfully
  131.    */
  132.   public final double [] distributionForInstance(Instance instance) 
  133.        throws Exception {
  134.     return m_root.distributionForInstance(instance, m_useLaplace);
  135.   }
  136.   /**
  137.    * Returns graph describing the tree.
  138.    *
  139.    * @exception Exception if graph can't be computed
  140.    */
  141.   public String graph() throws Exception {
  142.     return m_root.graph();
  143.   }
  144.   /**
  145.    * Returns tree in prefix order.
  146.    *
  147.    * @exception Exception if something goes wrong
  148.    */
  149.   public String prefix() throws Exception {
  150.     
  151.     return m_root.prefix();
  152.   }
  153.   /**
  154.    * Returns tree as an if-then statement.
  155.    *
  156.    * @return the tree as a Java if-then type statement
  157.    * @exception Exception if something goes wrong
  158.    */
  159.   public String toSource(String className) throws Exception {
  160.     StringBuffer [] source = m_root.toSource(className);
  161.     return 
  162.     "class " + className + " {nn"
  163.     +"  public static double classify(Object [] i)n"
  164.     +"    throws Exception {nn"
  165.     +"    double p = Double.NaN;n"
  166.     + source[0]  // Assignment code
  167.     +"    return p;n"
  168.     +"  }n"
  169.     + source[1]  // Support code
  170.     +"}n";
  171.   }
  172.   /**
  173.    * Returns an enumeration describing the available options.
  174.    *
  175.    * Valid options are: <p>
  176.    *
  177.    * -U <br>
  178.    * Use unpruned tree.<p>
  179.    *
  180.    * -C confidence <br>
  181.    * Set confidence threshold for pruning. (Default: 0.25) <p>
  182.    *
  183.    * -M number <br>
  184.    * Set minimum number of instances per leaf. (Default: 2) <p>
  185.    *
  186.    * -R <br>
  187.    * Use reduced error pruning. No subtree raising is performed. <p>
  188.    *
  189.    * -N number <br>
  190.    * Set number of folds for reduced error pruning. One fold is
  191.    * used as the pruning set. (Default: 3) <p>
  192.    *
  193.    * -B <br>
  194.    * Use binary splits for nominal attributes. <p>
  195.    *
  196.    * -S <br>
  197.    * Don't perform subtree raising. <p>
  198.    *
  199.    * -L <br>
  200.    * Do not clean up after the tree has been built.
  201.    *
  202.    * -A <br>
  203.    * If set, Laplace smoothing is used for predicted probabilites. <p>
  204.    *
  205.    * @return an enumeration of all the available options.
  206.    */
  207.   public Enumeration listOptions() {
  208.     Vector newVector = new Vector(9);
  209.     newVector.
  210. addElement(new Option("tUse unpruned tree.",
  211.       "U", 0, "-U"));
  212.     newVector.
  213. addElement(new Option("tSet confidence threshold for pruning.n" +
  214.       "t(default 0.25)",
  215.       "C", 1, "-C <pruning confidence>"));
  216.     newVector.
  217. addElement(new Option("tSet minimum number of instances per leaf.n" +
  218.       "t(default 2)",
  219.       "M", 1, "-M <minimum number of instances>"));
  220.     newVector.
  221. addElement(new Option("tUse reduced error pruning.",
  222.       "R", 0, "-R"));
  223.     newVector.
  224. addElement(new Option("tSet number of folds for reduced errorn" +
  225.       "tpruning. One fold is used as pruning set.n" +
  226.       "t(default 3)",
  227.       "N", 1, "-N <number of folds>"));
  228.     newVector.
  229. addElement(new Option("tUse binary splits only.",
  230.       "B", 0, "-B"));
  231.     newVector.
  232.         addElement(new Option("tDon't perform subtree raising.",
  233.       "S", 0, "-S"));
  234.     newVector.
  235.         addElement(new Option("tDo not clean up after the tree has been built.",
  236.       "L", 0, "-L"));
  237.    newVector.
  238.         addElement(new Option("tLaplace smoothing for predicted probabilities.",
  239.       "A", 0, "-A"));
  240.     return newVector.elements();
  241.   }
  242.   /**
  243.    * Parses a given list of options.
  244.    *
  245.    * @param options the list of options as an array of strings
  246.    * @exception Exception if an option is not supported
  247.    */
  248.   public void setOptions(String[] options) throws Exception{
  249.     
  250.     // Other options
  251.     String minNumString = Utils.getOption('M', options);
  252.     if (minNumString.length() != 0) {
  253.       m_minNumObj = Integer.parseInt(minNumString);
  254.     } else {
  255.       m_minNumObj = 2;
  256.     }
  257.     m_binarySplits = Utils.getFlag('B', options);
  258.     m_useLaplace = Utils.getFlag('A', options);
  259.     // Pruning options
  260.     m_unpruned = Utils.getFlag('U', options);
  261.     m_subtreeRaising = !Utils.getFlag('S', options);
  262.     m_noCleanup = Utils.getFlag('L', options);
  263.     if ((m_unpruned) && (!m_subtreeRaising)) {
  264.       throw new Exception("Subtree raising doesn't need to be unset for unpruned tree!");
  265.     }
  266.     m_reducedErrorPruning = Utils.getFlag('R', options);
  267.     if ((m_unpruned) && (m_reducedErrorPruning)) {
  268.       throw new Exception("Unpruned tree and reduced error pruning can't be selected " +
  269.   "simultaneously!");
  270.     }
  271.     String confidenceString = Utils.getOption('C', options);
  272.     if (confidenceString.length() != 0) {
  273.       if (m_reducedErrorPruning) {
  274. throw new Exception("Setting the confidence doesn't make sense " +
  275.     "for reduced error pruning.");
  276.       } else if (m_unpruned) {
  277. throw new Exception("Doesn't make sense to change confidence for unpruned "
  278.     +"tree!");
  279.       } else {
  280. m_CF = (new Float(confidenceString)).floatValue();
  281. if ((m_CF <= 0) || (m_CF >= 1)) {
  282.   throw new Exception("Confidence has to be greater than zero and smaller " +
  283.       "than one!");
  284. }
  285.       }
  286.     } else {
  287.       m_CF = 0.25f;
  288.     }
  289.     String numFoldsString = Utils.getOption('N', options);
  290.     if (numFoldsString.length() != 0) {
  291.       if (!m_reducedErrorPruning) {
  292. throw new Exception("Setting the number of folds" +
  293.     " doesn't make sense if" +
  294.     " reduced error pruning is not selected.");
  295.       } else {
  296. m_numFolds = Integer.parseInt(numFoldsString);
  297.       }
  298.     } else {
  299.       m_numFolds = 3;
  300.     }
  301.   }
  302.   /**
  303.    * Gets the current settings of the Classifier.
  304.    *
  305.    * @return an array of strings suitable for passing to setOptions
  306.    */
  307.   public String [] getOptions() {
  308.     String [] options = new String [10];
  309.     int current = 0;
  310.     if (m_noCleanup) {
  311.       options[current++] = "-L";
  312.     }
  313.     if (m_unpruned) {
  314.       options[current++] = "-U";
  315.     } else {
  316.       if (!m_subtreeRaising) {
  317. options[current++] = "-S";
  318.       }
  319.       if (m_reducedErrorPruning) {
  320. options[current++] = "-R";
  321. options[current++] = "-N"; options[current++] = "" + m_numFolds;
  322.       } else {
  323. options[current++] = "-C"; options[current++] = "" + m_CF;
  324.       }
  325.     }
  326.     if (m_binarySplits) {
  327.       options[current++] = "-B";
  328.     }
  329.     options[current++] = "-M"; options[current++] = "" + m_minNumObj;
  330.     if (m_useLaplace) {
  331.       options[current++] = "-A";
  332.     }
  333.     while (current < options.length) {
  334.       options[current++] = "";
  335.     }
  336.     return options;
  337.   }
  338.   
  339.   /**
  340.    * Get the value of useLaplace.
  341.    *
  342.    * @return Value of useLaplace.
  343.    */
  344.   public boolean getUseLaplace() {
  345.     
  346.     return m_useLaplace;
  347.   }
  348.   
  349.   /**
  350.    * Set the value of useLaplace.
  351.    *
  352.    * @param newuseLaplace Value to assign to useLaplace.
  353.    */
  354.   public void setUseLaplace(boolean newuseLaplace) {
  355.     
  356.     m_useLaplace = newuseLaplace;
  357.   }
  358.   
  359.   /**
  360.    * Returns a description of the classifier.
  361.    */
  362.   public String toString() {
  363.     if (m_root == null) {
  364.       return "No classifier built";
  365.     }
  366.     if (m_unpruned)
  367.       return "J48 unpruned treen------------------n" + m_root.toString();
  368.     else
  369.       return "J48 pruned treen------------------n" + m_root.toString();
  370.   }
  371.   /**
  372.    * Returns a superconcise version of the model
  373.    */
  374.   public String toSummaryString() {
  375.     return "Number of leaves: " + m_root.numLeaves() + "n"
  376.          + "Size of the tree: " + m_root.numNodes() + "n";
  377.   }
  378.   /**
  379.    * Returns the size of the tree
  380.    * @return the size of the tree
  381.    */
  382.   public double measureTreeSize() {
  383.     return m_root.numNodes();
  384.   }
  385.   /**
  386.    * Returns the number of leaves
  387.    * @return the number of leaves
  388.    */
  389.   public double measureNumLeaves() {
  390.     return m_root.numLeaves();
  391.   }
  392.   /**
  393.    * Returns the number of rules (same as number of leaves)
  394.    * @return the number of rules
  395.    */
  396.   public double measureNumRules() {
  397.     return m_root.numLeaves();
  398.   }
  399.   
  400.   /**
  401.    * Returns an enumeration of the additional measure names
  402.    * @return an enumeration of the measure names
  403.    */
  404.   public Enumeration enumerateMeasures() {
  405.     Vector newVector = new Vector(3);
  406.     newVector.addElement("measureTreeSize");
  407.     newVector.addElement("measureNumLeaves");
  408.     newVector.addElement("measureNumRules");
  409.     return newVector.elements();
  410.   }
  411.   /**
  412.    * Returns the value of the named measure
  413.    * @param measureName the name of the measure to query for its value
  414.    * @return the value of the named measure
  415.    * @exception IllegalArgumentException if the named measure is not supported
  416.    */
  417.   public double getMeasure(String additionalMeasureName) {
  418.     if (additionalMeasureName.compareTo("measureNumRules") == 0) {
  419.       return measureNumRules();
  420.     } else if (additionalMeasureName.compareTo("measureTreeSize") == 0) {
  421.       return measureTreeSize();
  422.     } else if (additionalMeasureName.compareTo("measureNumLeaves") == 0) {
  423.       return measureNumLeaves();
  424.     } else {
  425.       throw new IllegalArgumentException(additionalMeasureName 
  426.   + " not supported (j48)");
  427.     }
  428.   }
  429.   /**
  430.    * Get the value of unpruned.
  431.    *
  432.    * @return Value of unpruned.
  433.    */
  434.   public boolean getUnpruned() {
  435.     
  436.     return m_unpruned;
  437.   }
  438.   
  439.   /**
  440.    * Set the value of unpruned. Turns reduced-error pruning
  441.    * off if set.
  442.    * @param v  Value to assign to unpruned.
  443.    */
  444.   public void setUnpruned(boolean v) {
  445.     if (v) {
  446.       m_reducedErrorPruning = false;
  447.     }
  448.     m_unpruned = v;
  449.   }
  450.   
  451.   /**
  452.    * Get the value of CF.
  453.    *
  454.    * @return Value of CF.
  455.    */
  456.   public float getConfidenceFactor() {
  457.     
  458.     return m_CF;
  459.   }
  460.   
  461.   /**
  462.    * Set the value of CF.
  463.    *
  464.    * @param v  Value to assign to CF.
  465.    */
  466.   public void setConfidenceFactor(float v) {
  467.     
  468.     m_CF = v;
  469.   }
  470.   
  471.   /**
  472.    * Get the value of minNumObj.
  473.    *
  474.    * @return Value of minNumObj.
  475.    */
  476.   public int getMinNumObj() {
  477.     
  478.     return m_minNumObj;
  479.   }
  480.   
  481.   /**
  482.    * Set the value of minNumObj.
  483.    *
  484.    * @param v  Value to assign to minNumObj.
  485.    */
  486.   public void setMinNumObj(int v) {
  487.     
  488.     m_minNumObj = v;
  489.   }
  490.   
  491.   /**
  492.    * Get the value of reducedErrorPruning. 
  493.    *
  494.    * @return Value of reducedErrorPruning.
  495.    */
  496.   public boolean getReducedErrorPruning() {
  497.     
  498.     return m_reducedErrorPruning;
  499.   }
  500.   
  501.   /**
  502.    * Set the value of reducedErrorPruning. Turns
  503.    * unpruned trees off if set.
  504.    *
  505.    * @param v  Value to assign to reducedErrorPruning.
  506.    */
  507.   public void setReducedErrorPruning(boolean v) {
  508.     
  509.     if (v) {
  510.       m_unpruned = false;
  511.     }
  512.     m_reducedErrorPruning = v;
  513.   }
  514.   
  515.   /**
  516.    * Get the value of numFolds.
  517.    *
  518.    * @return Value of numFolds.
  519.    */
  520.   public int getNumFolds() {
  521.     
  522.     return m_numFolds;
  523.   }
  524.   
  525.   /**
  526.    * Set the value of numFolds.
  527.    *
  528.    * @param v  Value to assign to numFolds.
  529.    */
  530.   public void setNumFolds(int v) {
  531.     
  532.     m_numFolds = v;
  533.   }
  534.   
  535.   /**
  536.    * Get the value of binarySplits.
  537.    *
  538.    * @return Value of binarySplits.
  539.    */
  540.   public boolean getBinarySplits() {
  541.     
  542.     return m_binarySplits;
  543.   }
  544.   
  545.   /**
  546.    * Set the value of binarySplits.
  547.    *
  548.    * @param v  Value to assign to binarySplits.
  549.    */
  550.   public void setBinarySplits(boolean v) {
  551.     
  552.     m_binarySplits = v;
  553.   }
  554.   
  555.   /**
  556.    * Get the value of subtreeRaising.
  557.    *
  558.    * @return Value of subtreeRaising.
  559.    */
  560.   public boolean getSubtreeRaising() {
  561.     
  562.     return m_subtreeRaising;
  563.   }
  564.   
  565.   /**
  566.    * Set the value of subtreeRaising.
  567.    *
  568.    * @param v  Value to assign to subtreeRaising.
  569.    */
  570.   public void setSubtreeRaising(boolean v) {
  571.     
  572.     m_subtreeRaising = v;
  573.   }
  574.   
  575.   /**
  576.    * Check whether instance data is to be saved.
  577.    *
  578.    * @return true if instance data is saved
  579.    */
  580.   public boolean getSaveInstanceData() {
  581.     
  582.     return m_noCleanup;
  583.   }
  584.   
  585.   /**
  586.    * Set whether instance data is to be saved.
  587.    * @param v true if instance data is to be saved
  588.    */
  589.   public void setSaveInstanceData(boolean v) {
  590.     
  591.     m_noCleanup = v;
  592.   }
  593.  
  594.   /**
  595.    * Main method for testing this class
  596.    *
  597.    * @param String options 
  598.    */
  599.   public static void main(String [] argv){
  600.     try {
  601.       System.out.println(Evaluation.evaluateModel(new J48(), argv));
  602.     } catch (Exception e) {
  603.       System.err.println(e.getMessage());
  604.     }
  605.   }
  606. }
  607.