KernelDensity.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 6k
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.  *    KernelDensity.java
  18.  *    Copyright (C) 1999 Eibe Frank
  19.  *
  20.  */
  21. package weka.classifiers;
  22. import java.io.*;
  23. import java.util.*;
  24. import weka.core.*;
  25. /**
  26.  * Class for building and using a very simple kernel density classifier.
  27.  *
  28.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  29.  * @version $Revision: 1.6 $
  30.  */
  31. public class KernelDensity extends DistributionClassifier {
  32.   /** The number of instances in each class (null if class numeric). */
  33.   private double [] m_Counts;
  34.   
  35.   /** The instances used for "training". */
  36.   private Instances m_Instances;
  37.   /** The minimum values for numeric attributes. */
  38.   private double [] m_MinArray;
  39.   /** The maximum values for numeric attributes. */
  40.   private double [] m_MaxArray;
  41.  
  42.   /** Constant */
  43.   private static double CO = Math.sqrt(2 * Math.PI);
  44.   /**
  45.    * Returns value for normal kernel
  46.    *
  47.    * @param x the argument to the kernel function
  48.    * @return the value for a normal kernel
  49.    */
  50.   private double normalKernel(double x) {
  51.     return Math.exp(-(x * x) / 2) / CO;
  52.   }
  53.   /**
  54.    * Generates the classifier.
  55.    *
  56.    * @param instances set of instances serving as training data 
  57.    * @exception Exception if the classifier has not been generated successfully
  58.    */
  59.   public void buildClassifier(Instances instances) throws Exception {
  60.     if (!instances.classAttribute().isNominal()) {
  61.       throw new Exception("Class attribute has to be nominal!");
  62.     }
  63.     if (instances.checkForStringAttributes()) {
  64.       throw new Exception("Can't handle string attributes!");
  65.     }
  66.     m_Instances = instances;
  67.     m_MinArray = new double [m_Instances.numAttributes()];
  68.     m_MaxArray = new double [m_Instances.numAttributes()];
  69.     for (int i = 0; i < m_Instances.numAttributes(); i++) {
  70.       m_MinArray[i] = m_MaxArray[i] = Double.NaN;
  71.     }
  72.     m_Counts = new double[m_Instances.numClasses()];
  73.     for (int i = 0; i < m_Instances.numInstances(); i++) {
  74.       Instance inst = m_Instances.instance(i);
  75.       if (!inst.classIsMissing()) {
  76. m_Counts[(int) inst.classValue()] += inst.weight();
  77.       }
  78.       updateMinMax(inst);
  79.     }
  80.   }
  81.   /**
  82.    * Calculates the class membership probabilities for the given test instance.
  83.    *
  84.    * @param instance the instance to be classified
  85.    * @return predicted class probability distribution
  86.    * @exception Exception if the probabilities can't be computed
  87.    */
  88.   public double[] distributionForInstance(Instance instance) throws Exception {
  89.     
  90.     double[] probs = new double[m_Instances.numClasses()];
  91.     double prob, sum, temp;
  92.     double lowerBound = Math.pow(Double.MIN_VALUE, 1.0 / 
  93.  (instance.numAttributes() - 1.0)); 
  94.     sum = Math.sqrt(Utils.sum(m_Counts));
  95.     updateMinMax(instance);
  96.     for (int i = 0; i < m_Instances.numInstances(); i++) {
  97.       Instance inst = m_Instances.instance(i);
  98.       if (!inst.classIsMissing()) {
  99. prob = 1;
  100. for (int j = 0; j < m_Instances.numAttributes(); j++) {
  101.   if (j != m_Instances.classIndex()) {
  102.     temp = normalKernel(distance(instance, inst, j) * sum) * sum;
  103.     if (temp < lowerBound) {
  104.       prob *= lowerBound;
  105.     } else {
  106.       prob *= temp;
  107.     }
  108.   }
  109. }
  110. probs[(int) inst.classValue()] += prob;
  111.       }
  112.     }
  113.     Utils.normalize(probs);
  114.     return probs;
  115.   }
  116.   /**
  117.    * Returns a description of the classifier.
  118.    *
  119.    * @return a description of the classifier as a string.
  120.    */
  121.   public String toString() {
  122.     return "Kernel Density Estimator";
  123.   }
  124.   /**
  125.    * Calculates the distance between two instances according to one attribute
  126.    *
  127.    * @param test the first instance
  128.    * @param train the second instance
  129.    * @return the distance between the two given instances
  130.    */
  131.   private double distance(Instance first, Instance second, int i) {
  132.     
  133.     double diff, distance = 0;
  134.     if (m_Instances.attribute(i).isNominal()) {
  135.       
  136.       // If attribute is nominal
  137.       if (first.isMissing(i) || second.isMissing(i) ||
  138.   ((int)first.value(i) != (int)second.value(i))) {
  139. distance += 1;
  140.       }
  141.     } else {
  142.       
  143.       // If attribute is numeric
  144.       if (first.isMissing(i) || second.isMissing(i)) {
  145. if (first.isMissing(i) && second.isMissing(i)) {
  146.   diff = 1;
  147. } else {
  148.   if (second.isMissing(i)) {
  149.     diff = norm(first.value(i), i);
  150.   } else {
  151.     diff = norm(second.value(i), i);
  152.   }
  153.   if (diff < 0.5) {
  154.     diff = 1.0 - diff;
  155.   }
  156. }
  157.       } else {
  158. diff = norm(first.value(i), i) - norm(second.value(i), i);
  159.       }
  160.       distance += diff;
  161.     }
  162.       
  163.     return distance;
  164.   }
  165.     
  166.   /**
  167.    * Normalizes a given value of a numeric attribute.
  168.    *
  169.    * @param x the value to be normalized
  170.    * @param i the attribute's index
  171.    */
  172.   private double norm(double x, int i) {
  173.     if (Double.isNaN(m_MinArray[i]) || Utils.eq(m_MaxArray[i],m_MinArray[i])) {
  174.       return 0;
  175.     } else {
  176.       return (x - m_MinArray[i]) / (m_MaxArray[i] - m_MinArray[i]);
  177.     }
  178.   }
  179.   /**
  180.    * Updates the minimum and maximum values for all the attributes
  181.    * based on a new instance.
  182.    *
  183.    * @param instance the new instance
  184.    */
  185.   private void updateMinMax(Instance instance) {
  186.     
  187.     for (int j = 0; j < m_Instances.numAttributes(); j++) {
  188.       if ((m_Instances.attribute(j).isNumeric()) 
  189.   && (!instance.isMissing(j))) {
  190. if (Double.isNaN(m_MinArray[j])) {
  191.   m_MinArray[j] = instance.value(j);
  192.   m_MaxArray[j] = instance.value(j);
  193. } else {
  194.   if (instance.value(j) < m_MinArray[j]) {
  195.     m_MinArray[j] = instance.value(j);
  196.   } else {
  197.     if (instance.value(j) > m_MaxArray[j]) {
  198.       m_MaxArray[j] = instance.value(j);
  199.     }
  200.   }
  201. }
  202.       }
  203.     }
  204.   }
  205.  
  206.   /**
  207.    * Main method for testing this class.
  208.    *
  209.    * @param argv the options
  210.    */
  211.   public static void main(String [] argv) {
  212.     try {
  213.       System.out.println(Evaluation.evaluateModel(new KernelDensity(), argv));
  214.     } catch (Exception e) {
  215.       System.err.println(e.getMessage());
  216.     }
  217.   }
  218. }