NaiveBayes.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 11k
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.  *    NaiveBayes.java
  18.  *    Copyright (C) 1999 Eibe Frank,Len Trigg
  19.  *
  20.  */
  21. package weka.classifiers.bayes;
  22. import weka.classifiers.Classifier;
  23. import weka.classifiers.DistributionClassifier;
  24. import weka.classifiers.Evaluation;
  25. import weka.classifiers.UpdateableClassifier;
  26. import java.io.*;
  27. import java.util.*;
  28. import weka.core.*;
  29. import weka.estimators.*;
  30. /**
  31.  * Class for a Naive Bayes classifier using estimator classes. Numeric 
  32.  * estimator precision values are chosen based on analysis of the 
  33.  * training data. For this reason, the classifier is not an 
  34.  * UpdateableClassifier (which in typical usage are initialized with zero 
  35.  * training instances) -- if you need the UpdateableClassifier functionality,
  36.  * use the NaiveBayesUpdateable classifier. The NaiveBayesUpdateable
  37.  * classifier will  use a default precision of 0.1 for numeric attributes
  38.  * when buildClassifier is called with zero training instances.
  39.  * <p>
  40.  * For more information on Naive Bayes classifiers, see<p>
  41.  *
  42.  * George H. John and Pat Langley (1995). <i>Estimating
  43.  * Continuous Distributions in Bayesian Classifiers</i>. Proceedings
  44.  * of the Eleventh Conference on Uncertainty in Artificial
  45.  * Intelligence. pp. 338-345. Morgan Kaufmann, San Mateo.<p>
  46.  *
  47.  * Valid options are:<p>
  48.  *
  49.  * -K <br>
  50.  * Use kernel estimation for modelling numeric attributes rather than
  51.  * a single normal distribution.<p>
  52.  *
  53.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  54.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  55.  * @version $Revision: 1.12 $
  56.  */
  57. public class NaiveBayes extends DistributionClassifier 
  58.   implements OptionHandler, WeightedInstancesHandler {
  59.   /** The attribute estimators. */
  60.   protected Estimator [][] m_Distributions;
  61.   
  62.   /** The class estimator. */
  63.   protected Estimator m_ClassDistribution;
  64.   /**
  65.    * Whether to use kernel density estimator rather than normal distribution
  66.    * for numeric attributes
  67.    */
  68.   protected boolean m_UseKernelEstimator;
  69.   /** The number of classes (or 1 for numeric class) */
  70.   protected int m_NumClasses;
  71.   /**
  72.    * The dataset header for the purposes of printing out a semi-intelligible 
  73.    * model 
  74.    */
  75.   protected Instances m_Instances;
  76.   /*** The precision parameter used for numeric attributes */
  77.   protected static final double DEFAULT_NUM_PRECISION = 0.01;
  78.   /**
  79.    * Generates the classifier.
  80.    *
  81.    * @param instances set of instances serving as training data 
  82.    * @exception Exception if the classifier has not been generated 
  83.    * successfully
  84.    */
  85.   public void buildClassifier(Instances instances) throws Exception {
  86.     if (instances.checkForStringAttributes()) {
  87.       throw new UnsupportedAttributeTypeException("Cannot handle string attributes!");
  88.     }
  89.     if (instances.classAttribute().isNumeric()) {
  90.       throw new UnsupportedClassTypeException("Naive Bayes: Class is numeric!");
  91.     }
  92.     m_NumClasses = instances.numClasses();
  93.     if (m_NumClasses < 0) {
  94.       throw new Exception ("Dataset has no class attribute");
  95.     }
  96.     // Copy the instances
  97.     m_Instances = new Instances(instances);
  98.     // Reserve space for the distributions
  99.     m_Distributions = new Estimator[m_Instances.numAttributes() - 1]
  100.     [m_Instances.numClasses()];
  101.     m_ClassDistribution = new DiscreteEstimator(m_Instances.numClasses(), 
  102. true);
  103.     int attIndex = 0;
  104.     Enumeration enum = m_Instances.enumerateAttributes();
  105.     while (enum.hasMoreElements()) {
  106.       Attribute attribute = (Attribute) enum.nextElement();
  107.       // If the attribute is numeric, determine the estimator 
  108.       // numeric precision from differences between adjacent values
  109.       double numPrecision = DEFAULT_NUM_PRECISION;
  110.       if (attribute.type() == Attribute.NUMERIC) {
  111. m_Instances.sort(attribute);
  112. if ((m_Instances.numInstances() > 0)
  113.     && !m_Instances.instance(0).isMissing(attribute)) {
  114.   double lastVal = m_Instances.instance(0).value(attribute);
  115.   double currentVal, deltaSum = 0;
  116.   int distinct = 0;
  117.   for (int i = 1; i < m_Instances.numInstances(); i++) {
  118.     Instance currentInst = m_Instances.instance(i);
  119.     if (currentInst.isMissing(attribute)) {
  120.       break;
  121.     }
  122.     currentVal = currentInst.value(attribute);
  123.     if (currentVal != lastVal) {
  124.       deltaSum += currentVal - lastVal;
  125.       lastVal = currentVal;
  126.       distinct++;
  127.     }
  128.   }
  129.   if (distinct > 0) {
  130.     numPrecision = deltaSum / distinct;
  131.   }
  132. }
  133.       }
  134.       for (int j = 0; j < m_Instances.numClasses(); j++) {
  135. switch (attribute.type()) {
  136. case Attribute.NUMERIC: 
  137.   if (m_UseKernelEstimator) {
  138.     m_Distributions[attIndex][j] = 
  139.     new KernelEstimator(numPrecision);
  140.   } else {
  141.     m_Distributions[attIndex][j] = 
  142.     new NormalEstimator(numPrecision);
  143.   }
  144.   break;
  145. case Attribute.NOMINAL:
  146.   m_Distributions[attIndex][j] = 
  147.   new DiscreteEstimator(attribute.numValues(), true);
  148.   break;
  149. default:
  150.   throw new Exception("Attribute type unknown to NaiveBayes");
  151. }
  152.       }
  153.       attIndex++;
  154.     }
  155.     // Compute counts
  156.     Enumeration enumInsts = m_Instances.enumerateInstances();
  157.     while (enumInsts.hasMoreElements()) {
  158.       Instance instance = 
  159. (Instance) enumInsts.nextElement();
  160.       updateClassifier(instance);
  161.     }
  162.     // Save space
  163.     m_Instances = new Instances(m_Instances, 0);
  164.   }
  165.   /**
  166.    * Updates the classifier with the given instance.
  167.    *
  168.    * @param instance the new training instance to include in the model 
  169.    * @exception Exception if the instance could not be incorporated in
  170.    * the model.
  171.    */
  172.   public void updateClassifier(Instance instance) throws Exception {
  173.     if (!instance.classIsMissing()) {
  174.       Enumeration enumAtts = m_Instances.enumerateAttributes();
  175.       int attIndex = 0;
  176.       while (enumAtts.hasMoreElements()) {
  177. Attribute attribute = (Attribute) enumAtts.nextElement();
  178. if (!instance.isMissing(attribute)) {
  179.   m_Distributions[attIndex][(int)instance.classValue()].
  180.     addValue(instance.value(attribute), instance.weight());
  181. }
  182. attIndex++;
  183.       }
  184.       m_ClassDistribution.addValue(instance.classValue(),
  185.    instance.weight());
  186.     }
  187.   }
  188.   /**
  189.    * Calculates the class membership probabilities for the given test 
  190.    * instance.
  191.    *
  192.    * @param instance the instance to be classified
  193.    * @return predicted class probability distribution
  194.    * @exception Exception if there is a problem generating the prediction
  195.    */
  196.   public double [] distributionForInstance(Instance instance) 
  197.   throws Exception { 
  198.     
  199.     double [] probs = new double[m_NumClasses];
  200.     for (int j = 0; j < m_NumClasses; j++) {
  201.       probs[j] = m_ClassDistribution.getProbability(j);
  202.     }
  203.     Enumeration enumAtts = instance.enumerateAttributes();
  204.     int attIndex = 0;
  205.     while (enumAtts.hasMoreElements()) {
  206.       Attribute attribute = (Attribute) enumAtts.nextElement();
  207.       if (!instance.isMissing(attribute)) {
  208. double temp, max = 0;
  209. for (int j = 0; j < m_NumClasses; j++) {
  210.   temp = Math.max(1e-75, m_Distributions[attIndex][j].
  211.   getProbability(instance.value(attribute)));
  212.   probs[j] *= temp;
  213.   if (probs[j] > max) {
  214.     max = probs[j];
  215.   }
  216.   if (Double.isNaN(probs[j])) {
  217.     throw new Exception("NaN returned from estimator for attribute "
  218. + attribute.name() + ":n"
  219. + m_Distributions[attIndex][j].toString());
  220.   }
  221. }
  222. if ((max > 0) && (max < 1e-75)) { // Danger of probability underflow
  223.   for (int j = 0; j < m_NumClasses; j++) {
  224.     probs[j] *= 1e75;
  225.   }
  226. }
  227.       }
  228.       attIndex++;
  229.     }
  230.     // Display probabilities
  231.     Utils.normalize(probs);
  232.     return probs;
  233.   }
  234.   /**
  235.    * Returns an enumeration describing the available options.
  236.    *
  237.    * @return an enumeration of all the available options.
  238.    */
  239.   public Enumeration listOptions() {
  240.     Vector newVector = new Vector(1);
  241.     newVector.addElement(
  242.     new Option("tUse kernel density estimator rather than normaln"
  243.        +"tdistribution for numeric attributes",
  244.        "K", 0,"-K"));
  245.     return newVector.elements();
  246.   }
  247.   /**
  248.    * Parses a given list of options. Valid options are:<p>
  249.    *
  250.    * -K <br>
  251.    * Use kernel estimation for modelling numeric attributes rather than
  252.    * a single normal distribution.<p>
  253.    *
  254.    * @param options the list of options as an array of strings
  255.    * @exception Exception if an option is not supported
  256.    */
  257.   public void setOptions(String[] options) throws Exception {
  258.     
  259.     m_UseKernelEstimator = Utils.getFlag('K', options);
  260.     Utils.checkForRemainingOptions(options);
  261.   }
  262.   /**
  263.    * Gets the current settings of the classifier.
  264.    *
  265.    * @return an array of strings suitable for passing to setOptions
  266.    */
  267.   public String [] getOptions() {
  268.     String [] options = new String [1];
  269.     int current = 0;
  270.     if (m_UseKernelEstimator) {
  271.       options[current++] = "-K";
  272.     }
  273.     while (current < options.length) {
  274.       options[current++] = "";
  275.     }
  276.     return options;
  277.   }
  278.   /**
  279.    * Returns a description of the classifier.
  280.    *
  281.    * @return a description of the classifier as a string.
  282.    */
  283.   public String toString() {
  284.     
  285.     StringBuffer text = new StringBuffer();
  286.     text.append("Naive Bayes Classifier");
  287.     if (m_Instances == null) {
  288.       text.append(": No model built yet.");
  289.     } else {
  290.       try {
  291. for (int i = 0; i < m_Distributions[0].length; i++) {
  292.   text.append("nnClass " + m_Instances.classAttribute().value(i) +
  293.       ": Prior probability = " + Utils.
  294.       doubleToString(m_ClassDistribution.getProbability(i),
  295.      4, 2) + "nn");
  296.   Enumeration enumAtts = m_Instances.enumerateAttributes();
  297.   int attIndex = 0;
  298.   while (enumAtts.hasMoreElements()) {
  299.     Attribute attribute = (Attribute) enumAtts.nextElement();
  300.     text.append(attribute.name() + ":  " 
  301. + m_Distributions[attIndex][i]);
  302.     attIndex++;
  303.   }
  304. }
  305.       } catch (Exception ex) {
  306. text.append(ex.getMessage());
  307.       }
  308.     }
  309.     return text.toString();
  310.   }
  311.   
  312.   /**
  313.    * Gets if kernel estimator is being used.
  314.    *
  315.    * @return Value of m_UseKernelEstimatory.
  316.    */
  317.   public boolean getUseKernelEstimator() {
  318.     
  319.     return m_UseKernelEstimator;
  320.   }
  321.   
  322.   /**
  323.    * Sets if kernel estimator is to be used.
  324.    *
  325.    * @param v  Value to assign to m_UseKernelEstimatory.
  326.    */
  327.   public void setUseKernelEstimator(boolean v) {
  328.     
  329.     m_UseKernelEstimator = v;
  330.   }
  331.   /**
  332.    * Main method for testing this class.
  333.    *
  334.    * @param argv the options
  335.    */
  336.   public static void main(String [] argv) {
  337.     try {
  338.       System.out.println(Evaluation.evaluateModel(new NaiveBayes(), argv));
  339.     } catch (Exception e) {
  340.       e.printStackTrace();
  341.       System.err.println(e.getMessage());
  342.     }
  343.   }
  344. }