ConjunctiveRule.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 43k
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.  *    ConjunctiveRule.java
  18.  *    Copyright (C) 2001 Xin Xu
  19.  *
  20.  */
  21. package weka.classifiers.rules;
  22. import java.io.*;
  23. import java.util.*;
  24. import weka.core.*;
  25. import weka.classifiers.*;
  26. /**
  27.  * This class implements a single conjunctive rule learner that can predict
  28.  * for numeric and nominal class labels.<p>  
  29.  *
  30.  * A rule consists of antecedents "AND"ed together and the consequent (class value) 
  31.  * for the classification/regression.  In this case, the consequent is the 
  32.  * distribution of the available classes (or numeric value) in the dataset.  
  33.  * If the test instance is not covered by this rule, then it's predicted
  34.  * using the default class distributions/value of the data not covered by the
  35.  * rule in the training data. <br>
  36.  * This learner selects an antecedent by computing the Information Gain of each 
  37.  * antecendent and prunes the generated rule using Reduced Error Prunning (REP). <p>
  38.  *
  39.  * For classification, the Information of one antecedent is the weighted average of
  40.  * the entropies of both the data covered and not covered by the rule. <br>
  41.  *
  42.  * For regression, the Information is the weighted average of the mean-squared errors 
  43.  * of both the data covered and not covered by the rule. <p>
  44.  *
  45.  * In pruning, weighted average of accuracy rate of the pruning data is used 
  46.  * for classification while the weighted average of the mean-squared errors
  47.  * of the pruning data is used for regression. <p>
  48.  *
  49.  * @author: Xin XU (xx5@cs.waikato.ac.nz)
  50.  * @version $Revision: 1.5 $ 
  51.  */
  52. public class ConjunctiveRule extends DistributionClassifier implements OptionHandler, WeightedInstancesHandler{
  53.     
  54.   /** The number of folds to split data into Grow and Prune for REP*/
  55.   private int m_Folds = 3;
  56.     
  57.   /** The class attribute of the data*/
  58.   private Attribute m_ClassAttribute;
  59.     
  60.   /** The vector of antecedents of this rule*/
  61.   protected FastVector m_Antds = null;
  62.     
  63.   /** The default rule distribution of the data not covered*/
  64.   protected double[] m_DefDstr = null;
  65.     
  66.   /** The consequent of this rule */
  67.   protected double[] m_Cnsqt = null;
  68.         
  69.   /** Number of classes in the training data */
  70.   private int m_NumClasses = 0;
  71.     
  72.   /** The seed to perform randomization */
  73.   private long m_Seed = 1;
  74.     
  75.   /** The Random object used for randomization */
  76.   private Random m_Random = null;
  77.   /** Whether randomize the data */
  78.   private boolean m_IsRandomized = true;
  79.     
  80.   /** The predicted classes recorded for each antecedent in the growing data */
  81.   private FastVector m_Targets;
  82.   /** Whether to use exlusive expressions for nominal attributes */
  83.   private boolean m_IsExclude = false;
  84.   /** The minimal number of instance weights within a split*/
  85.   private double m_MinNo = 2.0;
  86.     
  87.   /** The number of antecedents in pre-pruning */
  88.   private int m_NumAntds = -1;
  89.   /** 
  90.    * The single antecedent in the rule, which is composed of an attribute and 
  91.    * the corresponding value.  There are two inherited classes, namely NumericAntd
  92.    * and NominalAntd in which the attributes are numeric and nominal respectively.
  93.    */
  94.     
  95.   private abstract class Antd{
  96.     /** The attribute of the antecedent */
  97.     protected Attribute att;
  98.     /** The attribute value of the antecedent.  
  99. For numeric attribute, value is either 0(1st bag) or 1(2nd bag) */
  100.     protected double value; 
  101.     /** The maximum infoGain achieved by this antecedent test */
  102.     protected double maxInfoGain;
  103.     /** The information of this antecedent test on the growing data */
  104.     protected double inform;
  105.     /** The parameter related to the meanSquaredError of the data not covered 
  106. by the previous antecedents when the class is numeric */
  107.     protected double uncoverWtSq, uncoverWtVl, uncoverSum;
  108.     /** The parameters related to the data not covered by the previous
  109. antecedents when the class is nominal */
  110.     protected double[] uncover;
  111.     /** Constructor for nominal class */
  112.     public Antd(Attribute a, double[] unc){
  113.       att=a;
  114.       value=Double.NaN; 
  115.       maxInfoGain = 0;
  116.       inform = Double.NaN;
  117.       uncover = unc;
  118.     }
  119.     /* Constructor for numeric class */
  120.     public Antd(Attribute a, double uncoveredWtSq, 
  121. double uncoveredWtVl, double uncoveredWts){
  122.       att=a;
  123.       value=Double.NaN; 
  124.       maxInfoGain = 0;
  125.       inform = Double.NaN;
  126.       uncoverWtSq = uncoveredWtSq;
  127.       uncoverWtVl = uncoveredWtVl;
  128.       uncoverSum = uncoveredWts;
  129.     }
  130.     /* The abstract members for inheritance */
  131.     public abstract Instances[] splitData(Instances data, double defInfo);
  132.     public abstract boolean isCover(Instance inst);
  133.     public abstract String toString();
  134.     /* Get functions of this antecedent */
  135.     public Attribute getAttr(){ return att; }
  136.     public double getAttrValue(){ return value; }
  137.     public double getMaxInfoGain(){ return maxInfoGain; }
  138.     public double getInfo(){ return inform;}
  139.     /** 
  140.      * Function used to calculate the weighted mean squared error,
  141.      * i.e., sum[x-avg(x)]^2 based on the given elements of the formula:
  142.      * meanSquaredError = sum(Wi*Xi^2) - (sum(WiXi))^2/sum(Wi)
  143.      * 
  144.      * @param weightedSq sum(Wi*Xi^2)
  145.      * @param weightedValue sum(WiXi)
  146.      * @param sum sum of weights
  147.      * @return the weighted mean-squared error
  148.      */
  149.     protected double wtMeanSqErr(double weightedSq, double weightedValue, double sum){
  150.       if(Utils.smOrEq(sum, 1.0E-6))
  151. return 0;     
  152.       return (weightedSq - (weightedValue * weightedValue) / sum);
  153.     }
  154.     /**
  155.      * Function used to calculate the entropy of given vector of values
  156.      * entropy = (1/sum)*{-sigma[i=1..P](Xi*log2(Xi)) + sum*log2(sum)}
  157.      * where P is the length of the vector
  158.      *
  159.      * @param value the given vector of values
  160.      * @param sum the sum of the given values.  It's provided just for efficiency.
  161.      * @return the entropy
  162.      */
  163.     protected double entropy(double[] value, double sum){    
  164.       if(Utils.smOrEq(sum, 1.0E-6))
  165. return 0;
  166.       double entropy = 0;     
  167.       for(int i=0; i < value.length; i++){
  168. if(!Utils.eq(value[i],0))
  169.   entropy -= value[i] * Utils.log2(value[i]);
  170.       }
  171.       entropy += sum * Utils.log2(sum);
  172.       entropy /= sum;
  173.       return entropy;
  174.     }
  175.   }
  176.     
  177.   /** 
  178.    * The antecedent with numeric attribute
  179.    */
  180.   private class NumericAntd extends Antd{
  181.     /* The split point for this numeric antecedent */
  182.     private double splitPoint;
  183.     /* Constructor for nominal class */
  184.     public NumericAntd(Attribute a, double[] unc){ 
  185.       super(a, unc);
  186.       splitPoint = Double.NaN;
  187.     }    
  188.     /* Constructor for numeric class */
  189.     public NumericAntd(Attribute a, double sq, double vl, double wts){ 
  190.       super(a, sq, vl, wts);
  191.       splitPoint = Double.NaN;
  192.     }
  193.     /* Get split point of this numeric antecedent */
  194.     public double getSplitPoint(){ return splitPoint; }
  195.     /**
  196.      * Implements the splitData function.  
  197.      * This procedure is to split the data into two bags according 
  198.      * to the information gain of the numeric attribute value
  199.      * the data with missing values are stored in the last split.
  200.      * The maximum infoGain is also calculated.  
  201.      * 
  202.      * @param insts the data to be split
  203.      * @param defInfo the default information for data
  204.      * @return the array of data after split
  205.      */
  206.     public Instances[] splitData(Instances insts, double defInfo){
  207.       Instances data = new Instances(insts);
  208.       data.sort(att);
  209.       int total=data.numInstances();// Total number of instances without 
  210.       // missing value for att
  211.       maxInfoGain = 0;
  212.       value = 0;
  213.     
  214.       // Compute minimum number of Instances required in each split
  215.       double minSplit;
  216.       if(m_ClassAttribute.isNominal()){
  217. minSplit =  0.1 * (data.sumOfWeights()) /
  218.   ((double)m_ClassAttribute.numValues());
  219. if (Utils.smOrEq(minSplit,m_MinNo)) 
  220.   minSplit = m_MinNo;
  221. else if (Utils.gr(minSplit,25)) 
  222.   minSplit = 25;
  223.       }
  224.       else
  225. minSplit = m_MinNo;
  226.  
  227.       double[] fst=null, snd=null, missing=null;
  228.       if(m_ClassAttribute.isNominal()){
  229. fst = new double[m_NumClasses];
  230. snd = new double[m_NumClasses];
  231. missing = new double[m_NumClasses];
  232. for(int v=0; v < m_NumClasses; v++)
  233.   fst[v]=snd[v]=missing[v]=0.0;
  234.       }
  235.       double fstCover=0, sndCover=0, fstWtSq=0, sndWtSq=0, fstWtVl=0, sndWtVl=0;
  236.     
  237.       int split=1;                  // Current split position
  238.       int prev=0;                   // Previous split position     
  239.       int finalSplit=split;         // Final split position
  240.     
  241.       for(int x=0; x<data.numInstances(); x++){
  242. Instance inst = data.instance(x);
  243. if(inst.isMissing(att)){
  244.   total = x;
  245.   break;
  246. }
  247. sndCover += inst.weight();
  248. if(m_ClassAttribute.isNominal()) // Nominal class
  249.   snd[(int)inst.classValue()] += inst.weight();
  250. else{                            // Numeric class
  251.   sndWtSq += inst.weight() * inst.classValue() * inst.classValue();
  252.   sndWtVl += inst.weight() * inst.classValue();
  253. }
  254.       }
  255.     
  256.     
  257.       // Enough Instances with known values?
  258.       if (Utils.sm(sndCover,(2*minSplit)))
  259. return null;
  260.     
  261.       double msingWtSq=0, msingWtVl=0;
  262.       Instances missingData = new Instances(data, 0);
  263.       for(int y=total; y < data.numInstances(); y++){     
  264. Instance inst = data.instance(y);
  265. missingData.add(inst);
  266. if(m_ClassAttribute.isNominal())
  267.   missing[(int)inst.classValue()] += inst.weight();
  268. else{ 
  269.   msingWtSq += inst.weight() * inst.classValue() * inst.classValue();
  270.   msingWtVl += inst.weight() * inst.classValue();
  271. }
  272.       }     
  273.     
  274.       if(total == 0) return null; // Data all missing for the attribute 
  275.     
  276.       splitPoint = data.instance(total-1).value(att);
  277.     
  278.       for(; split < total; split++){
  279. if(!Utils.eq(data.instance(split).value(att), // Can't split 
  280.      data.instance(prev).value(att))){// within same value   
  281.     
  282.   // Move the split point
  283.   for(int y=prev; y<split; y++){
  284.     Instance inst = data.instance(y);
  285.     fstCover += inst.weight(); sndCover -= inst.weight();
  286.     if(m_ClassAttribute.isNominal()){ // Nominal class
  287.       fst[(int)inst.classValue()] += inst.weight();
  288.       snd[(int)inst.classValue()] -= inst.weight();
  289.     }   
  290.     else{                             // Numeric class
  291.       fstWtSq += inst.weight() * inst.classValue() * inst.classValue();
  292.       fstWtVl += inst.weight() * inst.classValue();
  293.       sndWtSq -= inst.weight() * inst.classValue() * inst.classValue();
  294.       sndWtVl -= inst.weight() * inst.classValue();
  295.     }
  296.   }
  297.     
  298.   if(Utils.sm(fstCover, minSplit) || Utils.sm(sndCover, minSplit)){
  299.     prev=split;  // Cannot split because either
  300.     continue;    // split has not enough data
  301.   }
  302.     
  303.   double fstEntp = 0, sndEntp = 0;
  304.     
  305.   if(m_ClassAttribute.isNominal()){
  306.     fstEntp = entropy(fst, fstCover);
  307.     sndEntp = entropy(snd, sndCover);
  308.   }
  309.   else{
  310.     fstEntp = wtMeanSqErr(fstWtSq, fstWtVl, fstCover)/fstCover;
  311.     sndEntp = wtMeanSqErr(sndWtSq, sndWtVl, sndCover)/sndCover;
  312.   }
  313.     
  314.   /* Which bag has higher information gain? */
  315.   boolean isFirst; 
  316.   double fstInfoGain, sndInfoGain;
  317.   double info, infoGain, fstInfo, sndInfo;
  318.   if(m_ClassAttribute.isNominal()){
  319.     double sum = data.sumOfWeights();
  320.     double otherCover, whole = sum + Utils.sum(uncover), otherEntropy; 
  321.     double[] other = null;
  322.     // InfoGain of first bag
  323.     other = new double[m_NumClasses];
  324.     for(int z=0; z < m_NumClasses; z++)
  325.       other[z] = uncover[z] + snd[z] + missing[z];   
  326.     otherCover = whole - fstCover;
  327.     otherEntropy = entropy(other, otherCover);
  328.     // Weighted average
  329.     fstInfo = (fstEntp*fstCover + otherEntropy*otherCover)/whole;
  330.     fstInfoGain = defInfo - fstInfo;
  331.     // InfoGain of second bag 
  332.     other = new double[m_NumClasses];
  333.     for(int z=0; z < m_NumClasses; z++)
  334.       other[z] = uncover[z] + fst[z] + missing[z]; 
  335.     otherCover = whole - sndCover;
  336.     otherEntropy = entropy(other, otherCover);
  337.     // Weighted average
  338.     sndInfo = (sndEntp*sndCover + otherEntropy*otherCover)/whole;     
  339.     sndInfoGain = defInfo - sndInfo;
  340.   }
  341.   else{
  342.     double sum = data.sumOfWeights();
  343.     double otherWtSq = (sndWtSq + msingWtSq + uncoverWtSq), 
  344.       otherWtVl = (sndWtVl + msingWtVl + uncoverWtVl),
  345.       otherCover = (sum - fstCover + uncoverSum);
  346.     fstInfo = Utils.eq(fstCover, 0) ? 0 : (fstEntp * fstCover);
  347.     fstInfo += wtMeanSqErr(otherWtSq, otherWtVl, otherCover);
  348.     fstInfoGain = defInfo - fstInfo;
  349.     otherWtSq = (fstWtSq + msingWtSq + uncoverWtSq); 
  350.     otherWtVl = (fstWtVl + msingWtVl + uncoverWtVl);
  351.     otherCover = sum - sndCover + uncoverSum;
  352.     sndInfo = Utils.eq(sndCover, 0) ? 0 : (sndEntp * sndCover);
  353.     sndInfo += wtMeanSqErr(otherWtSq, otherWtVl, otherCover);
  354.     sndInfoGain = defInfo - sndInfo;
  355.   }
  356.     
  357.   if(Utils.gr(fstInfoGain,sndInfoGain) || 
  358.      (Utils.eq(fstInfoGain,sndInfoGain)&&(Utils.sm(fstEntp,sndEntp)))){ 
  359.     isFirst = true;
  360.     infoGain = fstInfoGain;
  361.     info = fstInfo;
  362.   }
  363.   else{
  364.     isFirst = false;
  365.     infoGain = sndInfoGain;
  366.     info = sndInfo;
  367.   }
  368.     
  369.   boolean isUpdate = Utils.gr(infoGain, maxInfoGain);
  370.     
  371.   /* Check whether so far the max infoGain */
  372.   if(isUpdate){
  373.     splitPoint = ((data.instance(split).value(att)) + (data.instance(prev).value(att)))/2.0;
  374.     value = ((isFirst) ? 0 : 1);
  375.     inform = info;
  376.     maxInfoGain = infoGain;
  377.     finalSplit = split;
  378.   }
  379.   prev=split;
  380. }
  381.       }
  382.     
  383.       /* Split the data */
  384.       Instances[] splitData = new Instances[3];
  385.       splitData[0] = new Instances(data, 0, finalSplit);
  386.       splitData[1] = new Instances(data, finalSplit, total-finalSplit);
  387.       splitData[2] = new Instances(missingData);
  388.     
  389.       return splitData;
  390.     }
  391.     /**
  392.      * Whether the instance is covered by this antecedent
  393.      * 
  394.      * @param inst the instance in question
  395.      * @return the boolean value indicating whether the instance is covered 
  396.      *         by this antecedent
  397.      */
  398.     public boolean isCover(Instance inst){
  399.       boolean isCover=false;
  400.       if(!inst.isMissing(att)){
  401. if(Utils.eq(value, 0)){
  402.   if(Utils.smOrEq(inst.value(att), splitPoint))
  403.     isCover=true;
  404. }
  405. else if(Utils.gr(inst.value(att), splitPoint))
  406.   isCover=true;
  407.       }
  408.       return isCover;
  409.     }
  410.     /**
  411.      * Prints this antecedent
  412.      *
  413.      * @return a textual description of this antecedent
  414.      */
  415.     public String toString() {
  416.       String symbol = Utils.eq(value, 0.0) ? " <= " : " > ";
  417.       return (att.name() + symbol + Utils.doubleToString(splitPoint, 6));
  418.     }   
  419.   }
  420.     
  421.     
  422.   /** 
  423.    * The antecedent with nominal attribute
  424.    */
  425.   class NominalAntd extends Antd{
  426.     /* The parameters of infoGain calculated for each attribute value */
  427.     private double[][] stats;
  428.     private double[] coverage;
  429.     private boolean isIn;
  430.     /* Constructor for nominal class */
  431.     public NominalAntd(Attribute a, double[] unc){ 
  432.       super(a, unc);
  433.       int bag = att.numValues();
  434.       stats = new double[bag][m_NumClasses];
  435.       coverage = new double[bag];
  436.       isIn = true;
  437.     }   
  438.     /* Constructor for numeric class */
  439.     public NominalAntd(Attribute a, double sq, double vl, double wts){ 
  440.       super(a, sq, vl, wts);
  441.       int bag = att.numValues();     
  442.       stats = null;
  443.       coverage = new double[bag];
  444.       isIn = true;
  445.     }
  446.     /**
  447.      * Implements the splitData function.  
  448.      * This procedure is to split the data into bags according 
  449.      * to the nominal attribute value
  450.      * the data with missing values are stored in the last bag.
  451.      * The infoGain for each bag is also calculated.  
  452.      * 
  453.      * @param data the data to be split
  454.      * @param defInfo the default information for data
  455.      * @return the array of data after split
  456.      */
  457.     public Instances[] splitData(Instances data, double defInfo){
  458.       int bag = att.numValues();
  459.       Instances[] splitData = new Instances[bag+1];
  460.       double[] wSq = new double[bag];
  461.       double[] wVl = new double[bag];
  462.       double totalWS=0, totalWV=0, msingWS=0, msingWV=0, sum=data.sumOfWeights();
  463.       double[] all = new double[m_NumClasses];
  464.       double[] missing = new double[m_NumClasses];    
  465.     
  466.       for(int w=0; w < m_NumClasses; w++)
  467. all[w] = missing[w] = 0;
  468.       for(int x=0; x<bag; x++){
  469. coverage[x] = wSq[x] = wVl[x] = 0;
  470. if(stats != null)
  471.   for(int y=0; y < m_NumClasses; y++)
  472.     stats[x][y] = 0;
  473. splitData[x] = new Instances(data, data.numInstances());
  474.       }
  475.       splitData[bag] = new Instances(data, data.numInstances());
  476.     
  477.       // Record the statistics of data
  478.       for(int x=0; x<data.numInstances(); x++){
  479. Instance inst=data.instance(x);
  480. if(!inst.isMissing(att)){
  481.   int v = (int)inst.value(att);
  482.   splitData[v].add(inst);
  483.   coverage[v] += inst.weight();
  484.   if(m_ClassAttribute.isNominal()){ // Nominal class
  485.     stats[v][(int)inst.classValue()] += inst.weight();
  486.     all[(int)inst.classValue()] += inst.weight();     
  487.   }
  488.   else{                             // Numeric class
  489.     wSq[v] += inst.weight() * inst.classValue() * inst.classValue();
  490.     wVl[v] += inst.weight() * inst.classValue();
  491.     totalWS += inst.weight() * inst.classValue() * inst.classValue();
  492.     totalWV += inst.weight() * inst.classValue();
  493.   }
  494. }
  495. else{
  496.   splitData[bag].add(inst);
  497.   if(m_ClassAttribute.isNominal()){ // Nominal class
  498.     all[(int)inst.classValue()] += inst.weight();
  499.     missing[(int)inst.classValue()] += inst.weight();
  500.   }
  501.   else{                            // Numeric class
  502.     totalWS += inst.weight() * inst.classValue() * inst.classValue();
  503.     totalWV += inst.weight() * inst.classValue();
  504.     msingWS += inst.weight() * inst.classValue() * inst.classValue();
  505.     msingWV += inst.weight() * inst.classValue();  
  506.   }
  507. }
  508.       }
  509.     
  510.       // The total weights of the whole grow data
  511.       double whole;
  512.       if(m_ClassAttribute.isNominal())
  513. whole = sum + Utils.sum(uncover);
  514.       else
  515. whole = sum + uncoverSum;
  516.   
  517.       // Find the split  
  518.       double minEntrp=Double.MAX_VALUE;
  519.       maxInfoGain = 0;
  520.     
  521.       // Check if >=2 splits have more than the minimal data
  522.       int count=0;
  523.       for(int x=0; x<bag; x++)
  524. if(Utils.grOrEq(coverage[x], m_MinNo))     
  525.   ++count;
  526.     
  527.       if(count < 2){ // Don't split
  528. maxInfoGain = 0;
  529. inform = defInfo;
  530. value = Double.NaN;
  531. return null;
  532.       }
  533.     
  534.       for(int x=0; x<bag; x++){
  535. double t = coverage[x], entrp, infoGain;
  536. if(Utils.sm(t, m_MinNo))
  537.   continue;
  538. if(m_ClassAttribute.isNominal()){ // Nominal class    
  539.   double[] other = new double[m_NumClasses];
  540.   for(int y=0; y < m_NumClasses; y++)
  541.     other[y] = all[y] - stats[x][y] + uncover[y]; 
  542.   double otherCover = whole - t;
  543.     
  544.   // Entropies of data covered and uncovered 
  545.   entrp = entropy(stats[x], t);
  546.   double uncEntp = entropy(other, otherCover);
  547.     
  548.   // Weighted average
  549.   infoGain = defInfo - (entrp*t + uncEntp*otherCover)/whole;    
  550. }
  551. else{                             // Numeric class
  552.   double weight = (whole - t);
  553.   entrp = wtMeanSqErr(wSq[x], wVl[x], t)/t;
  554.   infoGain = defInfo - (entrp * t) - 
  555.     wtMeanSqErr((totalWS-wSq[x]+uncoverWtSq),
  556. (totalWV-wVl[x]+uncoverWtVl), 
  557. weight);   
  558. }   
  559. // Test the exclusive expression
  560. boolean isWithin =true;
  561. if(m_IsExclude){
  562.   double infoGain2, entrp2;
  563.   if(m_ClassAttribute.isNominal()){ // Nominal class
  564.     double[] other2 = new double[m_NumClasses];
  565.     double[] notIn = new double[m_NumClasses];
  566.     for(int y=0; y < m_NumClasses; y++){
  567.       other2[y] = stats[x][y] + missing[y] + uncover[y];
  568.       notIn[y] = all[y] - stats[x][y] - missing[y];
  569.     } 
  570.     double msSum = Utils.sum(missing);
  571.     double otherCover2 = t + msSum + Utils.sum(uncover);
  572.     entrp2 = entropy(notIn, (sum-t-msSum));
  573.     double uncEntp2 = entropy(other2, otherCover2);
  574.     infoGain2 = defInfo - 
  575.       (entrp2*(sum-t-msSum) + uncEntp2*otherCover2)/whole;
  576.   }
  577.   else{                             // Numeric class
  578.     double msWts = splitData[bag].sumOfWeights();
  579.     double weight2 = t + uncoverSum + msWts;
  580.     entrp2 = wtMeanSqErr((totalWS-wSq[x]-msingWS),
  581.  (totalWV-wVl[x]-msingWV),(sum-t-msWts))
  582.       /(sum-t-msWts);
  583.     infoGain2 = defInfo - entrp2 * (sum-t-msWts) -
  584.       wtMeanSqErr((wSq[x]+uncoverWtSq+msingWS),
  585.   (wVl[x]+uncoverWtVl+msingWV), 
  586.   weight2);
  587.   }
  588.     
  589.   // Use the exclusive expression?
  590.   if (Utils.gr(infoGain2, infoGain) ||
  591.       (Utils.eq(infoGain2, infoGain) && Utils.sm(entrp2, entrp))){
  592.     infoGain = infoGain2;
  593.     entrp = entrp2;
  594.     isWithin =false;
  595.   }
  596. }
  597. // Test this split
  598. if (Utils.gr(infoGain, maxInfoGain) ||
  599.     (Utils.eq(infoGain, maxInfoGain) && Utils.sm(entrp, minEntrp))){
  600.   value = (double)x;
  601.   maxInfoGain = infoGain;
  602.   inform = maxInfoGain - defInfo;
  603.   minEntrp = entrp;
  604.   isIn = isWithin;
  605. }
  606.       }
  607.     
  608.       return splitData;
  609.     }
  610.     /**
  611.      * Whether the instance is covered by this antecedent
  612.      * 
  613.      * @param inst the instance in question
  614.      * @return the boolean value indicating whether the instance is covered 
  615.      *         by this antecedent
  616.      */
  617.     public boolean isCover(Instance inst){   
  618.       boolean isCover=false;
  619.       if(!inst.isMissing(att)){
  620. if(isIn){
  621.   if(Utils.eq(inst.value(att), value))
  622.     isCover=true;
  623. }
  624. else if(!Utils.eq(inst.value(att), value))
  625.   isCover=true;
  626.       }
  627.       return isCover;
  628.     }
  629.     /**
  630.      * Whether the expression is "att = value" or att != value"
  631.      * for this nominal attribute.  True if in the former expression, 
  632.      * otherwise the latter
  633.      * 
  634.      * @return the boolean value
  635.      */
  636.     public boolean isIn(){  
  637.       return isIn;
  638.     }
  639.     /**
  640.      * Prints this antecedent
  641.      *
  642.      * @return a textual description of this antecedent
  643.      */
  644.     public String toString() {
  645.       String symbol = isIn ? " = " : " != ";     
  646.       return (att.name() + symbol + att.value((int)value));
  647.     } 
  648.   }
  649.     
  650.   /**
  651.    * Returns an enumeration describing the available options
  652.    * Valid options are: <p>
  653.    *
  654.    * -N number <br>
  655.    * Set number of folds for REP. One fold is
  656.    * used as the pruning set. (Default: 3) <p>
  657.    *
  658.    * -R <br>
  659.    * Set if NOT randomize the data before split to growing and 
  660.    * pruning data. If NOT set, the seed of randomization is 
  661.    * specified by the -S option. (Default: randomize) <p>
  662.    * 
  663.    * -S <br>
  664.    * Seed of randomization. (Default: 1)<p>
  665.    *
  666.    * -E <br>
  667.    * Set whether consider the exclusive expressions for nominal
  668.    * attribute split. (Default: false) <p>
  669.    *
  670.    * -M number <br>
  671.    * Set the minimal weights of instances within a split.
  672.    * (Default: 2) <p>
  673.    *
  674.    * -P number <br>
  675.    * Set the number of antecedents allowed in the rule if pre-pruning
  676.    * is used.  If this value is other than -1, then pre-pruning will be
  677.    * used, otherwise the rule uses REP. (Default: -1) <p>
  678.    *
  679.    * @return an enumeration of all the available options
  680.    */
  681.   public Enumeration listOptions() {
  682.     Vector newVector = new Vector(6);
  683.     newVector.addElement(new Option("tSet number of folds for REPn" +
  684.     "tOne fold is used as pruning set.n" +
  685.     "t(default 3)","N", 1, "-N <number of folds>"));
  686.     newVector.addElement(new Option("tSet if NOT uses randomizationn" +
  687.     "t(default:use randomization)","R", 0, "-R"));
  688.     newVector.addElement(new Option("tSet whether consider the exclusiven" +
  689.     "texpressions for nominal attributesn"+
  690.     "t(default false)","E", 0, "-E"));
  691.     newVector.addElement(new Option("tSet the minimal weights of instancesn" +
  692.     "twithin a split.n" +
  693.     "t(default 2.0)","M", 1, "-M <min. weights>"));
  694.     
  695.     newVector.addElement(new Option("tSet number of antecedents for pre-pruningn" +
  696.     "tif -1, then REP is usedn" +
  697.     "t(default -1)","P", 1, "-P <number of antecedents>"));
  698.     
  699.     newVector.addElement(new Option("tSet the seed of randomizationn" +
  700.     "t(default 1)","S", 1, "-S <seed>"));
  701.     
  702.     return newVector.elements();
  703.   }
  704.     
  705.   /**
  706.    * Parses a given list of options.
  707.    *
  708.    * @param options the list of options as an array of strings
  709.    * @exception Exception if an option is not supported
  710.    */
  711.   public void setOptions(String[] options) throws Exception{
  712.     String numFoldsString = Utils.getOption('N', options);
  713.     if (numFoldsString.length() != 0) 
  714.       m_Folds = Integer.parseInt(numFoldsString);
  715.     else 
  716.       m_Folds = 3;
  717.     String minNoString = Utils.getOption('M', options);
  718.     if (minNoString.length() != 0) 
  719.       m_MinNo = Double.parseDouble(minNoString);
  720.     else 
  721.       m_MinNo = 2.0;
  722.     String seedString = Utils.getOption('S', options);
  723.     if (seedString.length() != 0) 
  724.       m_Seed = Integer.parseInt(seedString);
  725.     else 
  726.       m_Seed = 1;
  727.     String numAntdsString = Utils.getOption('P', options);
  728.     if (numAntdsString.length() != 0) 
  729.       m_NumAntds = Integer.parseInt(numAntdsString);
  730.     else 
  731.       m_NumAntds = -1;
  732.     m_IsRandomized = (!Utils.getFlag('R', options));
  733.     m_IsExclude = Utils.getFlag('E', options);
  734.   }
  735.     
  736.   /**
  737.    * Gets the current settings of the Classifier.
  738.    *
  739.    * @return an array of strings suitable for passing to setOptions
  740.    */
  741.   public String [] getOptions() {
  742.     String [] options = new String [10];
  743.     int current = 0;
  744.     options[current++] = "-N"; options[current++] = "" + m_Folds;
  745.     options[current++] = "-M"; options[current++] = "" + m_MinNo;
  746.     options[current++] = "-P"; options[current++] = "" + m_NumAntds;
  747.     options[current++] = "-S"; options[current++] = "" + m_Seed;
  748.     if(!m_IsRandomized)
  749.       options[current++] = "-R";
  750.     if(m_IsExclude)
  751.       options[current++] = "-E";
  752.     while (current < options.length) 
  753.       options[current++] = "";
  754.     return options;
  755.   }
  756.     
  757.   /** The access functions for parameters */
  758.   public void setFolds(int folds){  m_Folds = folds; }
  759.   public int getFolds(){ return m_Folds; }
  760.   public void setSeed(long s){ m_Seed = s; }
  761.   public long getSeed(){ return m_Seed; }
  762.   public boolean getRandomized(){ return m_IsRandomized;}
  763.   public void setRandomized(boolean r){ m_IsRandomized = r;}
  764.   public boolean getExclusive(){ return m_IsExclude;}
  765.   public void setExclusive(boolean e){ m_IsExclude = e;}
  766.   public void setMinNo(double m){  m_MinNo = m; }
  767.   public double getMinNo(){ return m_MinNo; }
  768.   public void setNumAntds(int n){  m_NumAntds = n; }
  769.   public int getNumAntds(){ return m_NumAntds; }
  770.     
  771.   /**
  772.    * Builds a single rule learner with REP dealing with nominal classes or
  773.    * numeric classes.
  774.    * For nominal classes, this rule learner predicts a distribution on
  775.    * the classes.
  776.    * For numeric classes, this learner predicts a single value.
  777.    *
  778.    * @param instances the training data
  779.    * @exception Exception if classifier can't be built successfully
  780.    */
  781.   public void buildClassifier(Instances instances) throws Exception{
  782.     if (instances.checkForStringAttributes())
  783.       throw new UnsupportedAttributeTypeException("Cannot handle string attributes!");
  784.  
  785.     Instances data = new Instances(instances);
  786.     if(data.numInstances() == 0)
  787. throw new Exception("No training data!");
  788.     data.deleteWithMissingClass();
  789.     
  790.     if(data.numInstances() == 0)
  791. throw new Exception("Not training data without missing class values.");
  792.     if(data.numInstances() < m_Folds)
  793.       throw new Exception("Not enough data for REP.");
  794.     m_ClassAttribute = data.classAttribute();
  795.     if(m_ClassAttribute.isNominal())
  796.       m_NumClasses = m_ClassAttribute.numValues();
  797.     else
  798.       m_NumClasses = 1;
  799.     m_Antds = new FastVector();
  800.     m_DefDstr = new double[m_NumClasses];
  801.     m_Cnsqt = new double[m_NumClasses];
  802.     m_Targets = new FastVector();     
  803.     m_Random = new Random(m_Seed);
  804.     
  805.     if(m_IsRandomized){  // Randomize the data
  806. data.randomize(m_Random);
  807.     }
  808.     
  809.     if(m_NumAntds != -1){
  810.       grow(data);
  811.     }
  812.     else{
  813.       // Split data into Grow and Prune    
  814.       data.stratify(m_Folds);
  815.       Instances growData=data.trainCV(m_Folds, m_Folds-1);
  816.       Instances pruneData=data.testCV(m_Folds, m_Folds-1);
  817.       grow(growData);      // Build this rule  
  818.       prune(pruneData);    // Prune this rule      
  819.     }
  820.     if(m_ClassAttribute.isNominal()){    
  821.       Utils.normalize(m_Cnsqt);
  822.       if(Utils.gr(Utils.sum(m_DefDstr), 0))
  823. Utils.normalize(m_DefDstr);
  824.     }
  825.   }
  826.     
  827.   /**
  828.    * Computes class distribution for the given instance.
  829.    *
  830.    * @param instance the instance for which distribution is to be computed
  831.    * @return the class distribution for the given instance
  832.    */
  833.   public double[] distributionForInstance(Instance instance) throws Exception{
  834.       if(instance == null)
  835.   throw new Exception("Testing instance is NULL!");
  836.     if (isCover(instance))
  837.       return m_Cnsqt;
  838.     else
  839.       return m_DefDstr;
  840.   }
  841.  
  842.   /**
  843.    * Whether the instance covered by this rule
  844.    * 
  845.    * @param inst the instance in question
  846.    * @return the boolean value indicating whether the instance is covered by this rule
  847.    */
  848.   public boolean isCover(Instance datum){
  849.     boolean isCover=true;
  850.     for(int i=0; i<m_Antds.size(); i++){
  851.       Antd antd = (Antd)m_Antds.elementAt(i);
  852.       if(!antd.isCover(datum)){
  853. isCover = false;
  854. break;
  855.       }
  856.     }
  857.     return isCover;
  858.   }        
  859.     
  860.   /**
  861.    * Whether this rule has antecedents, i.e. whether it is a default rule
  862.    * 
  863.    * @return the boolean value indicating whether the rule has antecedents
  864.    */
  865.   public boolean hasAntds(){
  866.     if (m_Antds == null)
  867.       return false;
  868.     else
  869.       return (m_Antds.size() > 0);
  870.   }      
  871.   /**
  872.    * Build one rule using the growing data
  873.    *
  874.    * @param data the growing data used to build the rule
  875.    */    
  876.   private void grow(Instances data){
  877.     Instances growData = new Instances(data);
  878.     double defInfo;
  879.     double whole = data.sumOfWeights();
  880.     if(m_NumAntds != 0){
  881.       /* Class distribution for data both covered and not covered by one antecedent */
  882.       double[][] classDstr = new double[2][m_NumClasses];
  883.     
  884.       /* Compute the default information of the growing data */
  885.       for(int j=0; j < m_NumClasses; j++){
  886. classDstr[0][j] = 0;
  887. classDstr[1][j] = 0;
  888.       }
  889.       if(m_ClassAttribute.isNominal()){     
  890. for(int i=0; i < growData.numInstances(); i++){
  891.   Instance datum = growData.instance(i);
  892.   classDstr[0][(int)datum.classValue()] += datum.weight();
  893. }
  894. defInfo = ContingencyTables.entropy(classDstr[0]);    
  895.       }
  896.       else{
  897. for(int i=0; i < growData.numInstances(); i++){
  898.   Instance datum = growData.instance(i);
  899.   classDstr[0][0] += datum.weight() * datum.classValue();
  900. }
  901. // No need to be divided by the denomitor because
  902. // it's always the same
  903. double defMean = (classDstr[0][0] / whole);
  904. defInfo = meanSquaredError(growData, defMean) * growData.sumOfWeights();    
  905.       }
  906.     
  907.       // Store the default class distribution
  908.       double[][] tmp = new double[2][m_NumClasses];
  909.       for(int y=0; y < m_NumClasses; y++){
  910. if(m_ClassAttribute.isNominal()){
  911.   tmp[0][y] = classDstr[0][y];
  912.   tmp[1][y] = classDstr[1][y];
  913. }
  914. else{
  915.   tmp[0][y] = classDstr[0][y]/whole;
  916.   tmp[1][y] = classDstr[1][y];
  917. }
  918.       }
  919.       m_Targets.addElement(tmp); 
  920.     
  921.       /* Keep the record of which attributes have already been used*/    
  922.       boolean[] used=new boolean[growData.numAttributes()];
  923.       for (int k=0; k<used.length; k++)
  924. used[k]=false;
  925.       int numUnused=used.length;
  926.       double maxInfoGain, uncoveredWtSq=0, uncoveredWtVl=0, uncoveredWts=0;
  927.       boolean isContinue = true; // The stopping criterion of this rule
  928.     
  929.       while (isContinue){   
  930. maxInfoGain = 0;       // We require that infoGain be positive
  931. /* Build a list of antecedents */
  932. Antd oneAntd=null;
  933. Instances coverData = null, uncoverData = null;
  934. Enumeration enumAttr=growData.enumerateAttributes();     
  935. int index=-1;  
  936. /* Build one condition based on all attributes not used yet*/
  937. while (enumAttr.hasMoreElements()){
  938.   Attribute att= (Attribute)(enumAttr.nextElement());
  939.   index++;
  940.     
  941.   Antd antd =null;
  942.   if(m_ClassAttribute.isNominal()){     
  943.     if(att.isNumeric())
  944.       antd = new NumericAntd(att, classDstr[1]);
  945.     else
  946.       antd = new NominalAntd(att, classDstr[1]);
  947.   }
  948.   else
  949.     if(att.isNumeric())
  950.       antd = new NumericAntd(att, uncoveredWtSq, uncoveredWtVl, uncoveredWts);
  951.     else
  952.       antd = new NominalAntd(att, uncoveredWtSq, uncoveredWtVl, uncoveredWts); 
  953.     
  954.   if(!used[index]){
  955.     /* Compute the best information gain for each attribute,
  956.        it's stored in the antecedent formed by this attribute.
  957.        This procedure returns the data covered by the antecedent*/
  958.     Instances[] coveredData = computeInfoGain(growData, defInfo, antd);  
  959.     if(coveredData != null){
  960.       double infoGain = antd.getMaxInfoGain();
  961.       boolean isUpdate = Utils.gr(infoGain, maxInfoGain);
  962.     
  963.       if(isUpdate){
  964. oneAntd=antd;
  965. coverData = coveredData[0]; 
  966. uncoverData = coveredData[1];  
  967. maxInfoGain = infoGain;     
  968.       }
  969.     }
  970.   }
  971. }
  972. if(oneAntd == null) 
  973.   break;     
  974. //Numeric attributes can be used more than once
  975. if(!oneAntd.getAttr().isNumeric()){ 
  976.   used[oneAntd.getAttr().index()]=true;
  977.   numUnused--;
  978. }
  979. m_Antds.addElement(oneAntd);
  980. growData = coverData;// Grow data size is shrinking      
  981. for(int x=0; x < uncoverData.numInstances(); x++){
  982.   Instance datum = uncoverData.instance(x);
  983.   if(m_ClassAttribute.isNumeric()){
  984.     uncoveredWtSq += datum.weight() * datum.classValue() * datum.classValue();
  985.     uncoveredWtVl += datum.weight() * datum.classValue();
  986.     uncoveredWts += datum.weight();
  987.     classDstr[0][0] -= datum.weight() * datum.classValue();
  988.     classDstr[1][0] += datum.weight() * datum.classValue();
  989.   }
  990.   else{
  991.     classDstr[0][(int)datum.classValue()] -= datum.weight();
  992.     classDstr[1][(int)datum.classValue()] += datum.weight();
  993.   }
  994. }        
  995. // Store class distribution of growing data
  996. tmp = new double[2][m_NumClasses];
  997. for(int y=0; y < m_NumClasses; y++){
  998.   if(m_ClassAttribute.isNominal()){
  999.     tmp[0][y] = classDstr[0][y];
  1000.     tmp[1][y] = classDstr[1][y];
  1001.   }
  1002.   else{
  1003.     tmp[0][y] = classDstr[0][y]/(whole-uncoveredWts);
  1004.     tmp[1][y] = classDstr[1][y]/uncoveredWts;
  1005.   }
  1006. }
  1007. m_Targets.addElement(tmp);  
  1008. defInfo = oneAntd.getInfo();
  1009. int numAntdsThreshold = (m_NumAntds == -1) ? Integer.MAX_VALUE : m_NumAntds;
  1010. if(Utils.eq(growData.sumOfWeights(), 0.0) || 
  1011.    (numUnused == 0) ||
  1012.    (m_Antds.size() >= numAntdsThreshold))
  1013.   isContinue = false;
  1014.       }
  1015.     }
  1016.     m_Cnsqt = ((double[][])(m_Targets.lastElement()))[0];
  1017.     m_DefDstr = ((double[][])(m_Targets.lastElement()))[1];
  1018.   }
  1019.     
  1020.   /** 
  1021.    * Compute the best information gain for the specified antecedent
  1022.    *  
  1023.    * @param data the data based on which the infoGain is computed
  1024.    * @param defInfo the default information of data
  1025.    * @param antd the specific antecedent
  1026.    * @return the data covered and not covered by the antecedent
  1027.    */
  1028.   private Instances[] computeInfoGain(Instances instances, double defInfo, Antd antd){
  1029.     Instances data = new Instances(instances);
  1030.     /* Split the data into bags.
  1031.        The information gain of each bag is also calculated in this procedure */
  1032.     Instances[] splitData = antd.splitData(data, defInfo); 
  1033.     Instances[] coveredData = new Instances[2];
  1034.     /* Get the bag of data to be used for next antecedents */
  1035.     Instances tmp1 = new Instances(data, 0);
  1036.     Instances tmp2 = new Instances(data, 0);
  1037.     if(splitData == null)     
  1038.       return null;
  1039.     for(int x=0; x < (splitData.length-1); x++){
  1040.       if(x == ((int)antd.getAttrValue()))
  1041. tmp1 = splitData[x];
  1042.       else{
  1043. for(int y=0; y < splitData[x].numInstances(); y++)
  1044.   tmp2.add(splitData[x].instance(y));     
  1045.       }
  1046.     }
  1047.     if(antd.getAttr().isNominal()){ // Nominal attributes
  1048.       if(((NominalAntd)antd).isIn()){ // Inclusive expression
  1049. coveredData[0] = new Instances(tmp1);
  1050. coveredData[1] = new Instances(tmp2);
  1051.       }
  1052.       else{                           // Exclusive expression
  1053. coveredData[0] = new Instances(tmp2);
  1054. coveredData[1] = new Instances(tmp1);
  1055.       }
  1056.     }
  1057.     else{                           // Numeric attributes
  1058.       coveredData[0] = new Instances(tmp1);
  1059.       coveredData[1] = new Instances(tmp2);
  1060.     }
  1061.     /* Add data with missing value */
  1062.     for(int z=0; z<splitData[splitData.length-1].numInstances(); z++)
  1063.       coveredData[1].add(splitData[splitData.length-1].instance(z));
  1064.     return coveredData;
  1065.   }
  1066.     
  1067.   /**
  1068.    * Prune the rule using the pruning data.
  1069.    * The weighted average of accuracy rate/mean-squared error is 
  1070.    * used to prune the rule.
  1071.    *
  1072.    * @param pruneData the pruning data used to prune the rule
  1073.    */    
  1074.   private void prune(Instances pruneData){
  1075.     Instances data=new Instances(pruneData);
  1076.     Instances otherData = new Instances(data, 0);
  1077.     double total = data.sumOfWeights();
  1078.     /* The default accurate# and the the accuracy rate on pruning data */
  1079.     double defAccu;
  1080.     if(m_ClassAttribute.isNumeric())
  1081.       defAccu = meanSquaredError(pruneData,
  1082.  ((double[][])m_Targets.firstElement())[0][0]);
  1083.     else{
  1084.       int predict = Utils.maxIndex(((double[][])m_Targets.firstElement())[0]);
  1085.       defAccu = computeAccu(pruneData, predict)/total;
  1086.     }
  1087.     int size=m_Antds.size();
  1088.     if(size == 0){
  1089.       m_Cnsqt = ((double[][])m_Targets.lastElement())[0];
  1090.       m_DefDstr = ((double[][])m_Targets.lastElement())[1];
  1091.       return; // Default rule before pruning
  1092.     }
  1093.     double[] worthValue = new double[size];
  1094.     /* Calculate accuracy parameters for all the antecedents in this rule */
  1095.     for(int x=0; x<size; x++){
  1096.       Antd antd=(Antd)m_Antds.elementAt(x);
  1097.       Attribute attr= antd.getAttr();
  1098.       Instances newData = new Instances(data);
  1099.       if(Utils.eq(newData.sumOfWeights(),0.0))
  1100. break;
  1101.     
  1102.       data = new Instances(newData, newData.numInstances()); // Make data empty
  1103.     
  1104.       for(int y=0; y<newData.numInstances(); y++){
  1105. Instance ins=newData.instance(y);
  1106. if(antd.isCover(ins))              // Covered by this antecedent
  1107.   data.add(ins);                 // Add to data for further 
  1108. else
  1109.   otherData.add(ins);            // Not covered by this antecedent
  1110.       }
  1111.     
  1112.       double covered, other;     
  1113.       double[][] classes = 
  1114. (double[][])m_Targets.elementAt(x+1); // m_Targets has one more element
  1115.       if(m_ClassAttribute.isNominal()){
  1116. int coverClass = Utils.maxIndex(classes[0]),
  1117.   otherClass = Utils.maxIndex(classes[1]);
  1118. covered = computeAccu(data, coverClass); 
  1119. other = computeAccu(otherData, otherClass);
  1120.       }
  1121.       else{
  1122. double coverClass = classes[0][0],
  1123.   otherClass = classes[1][0];
  1124. covered = (data.sumOfWeights())*meanSquaredError(data, coverClass); 
  1125. other = (otherData.sumOfWeights())*meanSquaredError(otherData, otherClass);
  1126.       }
  1127.     
  1128.       worthValue[x] = (covered + other)/total;
  1129.     }
  1130.     /* Prune the antecedents according to the accuracy parameters */
  1131.     for(int z=(size-1); z > 0; z--){
  1132.       // Treatment to avoid precision problems
  1133.       double valueDelta;
  1134.       if(m_ClassAttribute.isNominal()){
  1135. if(Utils.sm(worthValue[z], 1.0))
  1136.   valueDelta = (worthValue[z] - worthValue[z-1]) / worthValue[z];
  1137. else
  1138.   valueDelta = worthValue[z] - worthValue[z-1];
  1139.       }
  1140.       else{
  1141. if(Utils.sm(worthValue[z], 1.0))
  1142.   valueDelta = (worthValue[z-1] - worthValue[z]) / worthValue[z];
  1143. else
  1144.   valueDelta = (worthValue[z-1] - worthValue[z]);
  1145.       }
  1146.     
  1147.       if(Utils.smOrEq(valueDelta, 0.0)){
  1148. m_Antds.removeElementAt(z);
  1149. m_Targets.removeElementAt(z+1);
  1150.       }
  1151.       else  break;
  1152.     }
  1153.     // Check whether this rule is a default rule
  1154.     if(m_Antds.size() == 1){
  1155.       double valueDelta;
  1156.       if(m_ClassAttribute.isNominal()){
  1157. if(Utils.sm(worthValue[0], 1.0))
  1158.   valueDelta = (worthValue[0] - defAccu) / worthValue[0];
  1159. else
  1160.   valueDelta = (worthValue[0] - defAccu);
  1161.       }
  1162.       else{
  1163. if(Utils.sm(worthValue[0], 1.0))
  1164.   valueDelta = (defAccu - worthValue[0]) / worthValue[0];
  1165. else
  1166.   valueDelta = (defAccu - worthValue[0]);
  1167.       }
  1168.     
  1169.       if(Utils.smOrEq(valueDelta, 0.0)){
  1170. m_Antds.removeAllElements();
  1171. m_Targets.removeElementAt(1);
  1172.       }
  1173.     }
  1174.     m_Cnsqt = ((double[][])(m_Targets.lastElement()))[0];
  1175.     m_DefDstr = ((double[][])(m_Targets.lastElement()))[1];
  1176.   }
  1177.     
  1178.   /**
  1179.    * Private function to compute number of accurate instances
  1180.    * based on the specified predicted class
  1181.    * 
  1182.    * @param data the data in question
  1183.    * @param clas the predicted class
  1184.    * @return the default accuracy number
  1185.    */
  1186.   private double computeAccu(Instances data, int clas){ 
  1187.     double accu = 0;
  1188.     for(int i=0; i<data.numInstances(); i++){
  1189.       Instance inst = data.instance(i);
  1190.       if((int)inst.classValue() == clas)
  1191. accu += inst.weight();
  1192.     }
  1193.     return accu;
  1194.   }
  1195.     
  1196.   /**
  1197.    * Private function to compute the squared error of
  1198.    * the specified data and the specified mean
  1199.    * 
  1200.    * @param data the data in question
  1201.    * @param mean the specified mean
  1202.    * @return the default mean-squared error
  1203.    */
  1204.   private double meanSquaredError(Instances data, double mean){ 
  1205.     if(Utils.eq(data.sumOfWeights(),0.0))
  1206.       return 0;
  1207.     double mSqErr=0, sum = data.sumOfWeights();
  1208.     for(int i=0; i < data.numInstances(); i++){
  1209.       Instance datum = data.instance(i);
  1210.       mSqErr += datum.weight()*
  1211. (datum.classValue() - mean)*
  1212. (datum.classValue() - mean);
  1213.     }  
  1214.     return (mSqErr / sum);
  1215.   }
  1216.      
  1217.   /**
  1218.    * Prints this rule with the specified class label
  1219.    *
  1220.    * @param att the string standing for attribute in the consequent of this rule
  1221.    * @param cl the string standing for value in the consequent of this rule
  1222.    * @return a textual description of this rule with the specified class label
  1223.    */
  1224.   public String toString(String att, String cl) {
  1225.     StringBuffer text =  new StringBuffer();
  1226.     if(m_Antds.size() > 0){
  1227.       for(int j=0; j< (m_Antds.size()-1); j++)
  1228. text.append("(" + ((Antd)(m_Antds.elementAt(j))).toString()+ ") and ");
  1229.       text.append("("+((Antd)(m_Antds.lastElement())).toString() + ")");
  1230.     }
  1231.     text.append(" => " + att + " = " + cl);
  1232.     return text.toString();
  1233.   }
  1234.     
  1235.   /**
  1236.    * Prints this rule
  1237.    *
  1238.    * @return a textual description of this rule
  1239.    */
  1240.   public String toString() {
  1241.     String title = 
  1242.       "nnSingle conjunctive rule learner:n"+
  1243.       "--------------------------------n", body = null;
  1244.     StringBuffer text =  new StringBuffer();
  1245.     if(m_ClassAttribute != null){
  1246.       if(m_ClassAttribute.isNominal()){
  1247. body = toString(m_ClassAttribute.name(), m_ClassAttribute.value(Utils.maxIndex(m_Cnsqt)));
  1248. text.append("nnClass distributions:nCovered by the rule:n");
  1249. for(int k=0; k < m_Cnsqt.length; k++)
  1250.   text.append(m_ClassAttribute.value(k)+ "t");
  1251. text.append('n');
  1252. for(int l=0; l < m_Cnsqt.length; l++)
  1253.   text.append(Utils.doubleToString(m_Cnsqt[l], 6)+"t");
  1254. text.append("nnNot covered by the rule:n");
  1255. for(int k=0; k < m_DefDstr.length; k++)
  1256.   text.append(m_ClassAttribute.value(k)+ "t");
  1257. text.append('n');
  1258. for(int l=0; l < m_DefDstr.length; l++)
  1259.   text.append(Utils.doubleToString(m_DefDstr[l], 6)+"t");     
  1260.       }
  1261.       else
  1262. body = toString(m_ClassAttribute.name(), Utils.doubleToString(m_Cnsqt[0], 6));
  1263.     }
  1264.     return (title + body + text.toString());
  1265.   }
  1266.     
  1267.   /**
  1268.    * Main method.
  1269.    *
  1270.    * @param args the options for the classifier
  1271.    */
  1272.   public static void main(String[] args) {
  1273.     try {
  1274.       System.out.println(Evaluation.evaluateModel(new ConjunctiveRule(), args));
  1275.     } catch (Exception e) {
  1276.       e.printStackTrace();
  1277.       System.err.println(e.getMessage());
  1278.     }
  1279.   }
  1280. }