HyperPipes.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 8k
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.  *    HyperPipes.java
  18.  *    Copyright (C) 1999 Intelligenesis Corp.
  19.  *
  20.  */
  21. package weka.classifiers.misc;
  22. import weka.classifiers.Evaluation;
  23. import weka.classifiers.Classifier;
  24. import weka.classifiers.DistributionClassifier;
  25. import weka.core.Attribute;
  26. import weka.core.Instance;
  27. import weka.core.Instances;
  28. import weka.core.Utils;
  29. import weka.core.UnsupportedAttributeTypeException;
  30. import weka.core.UnsupportedClassTypeException;
  31. import java.io.*;
  32. /**
  33.  * Class implementing a HyperPipe classifier. For each category a
  34.  * HyperPipe is constructed that contains all points of that category 
  35.  * (essentially records the attribute bounds observed for each category).
  36.  * Test instances are classified according to the category that most 
  37.  * contains the instance). 
  38.  * Does not handle numeric class, or missing values in test cases. Extremely
  39.  * simple algorithm, but has the advantage of being extremely fast, and
  40.  * works quite well when you have smegloads of attributes.
  41.  *
  42.  * @author Lucio de Souza Coelho (lucio@intelligenesis.net)
  43.  * @author Len Trigg (len@intelligenesis.net)
  44.  * @version $Revision: 1.10 $
  45.  */ 
  46. public class HyperPipes extends DistributionClassifier {
  47.   /** The index of the class attribute */
  48.   protected int m_ClassIndex;
  49.   /** The structure of the training data */
  50.   protected Instances m_Instances;
  51.   /** Stores the HyperPipe for each class */
  52.   protected HyperPipe [] m_HyperPipes;
  53.   /**
  54.    * Represents an n-dimensional structure that bounds all instances 
  55.    * passed to it (generally all of a given class value).
  56.    */
  57.   class HyperPipe implements Serializable {
  58.     /** Contains the numeric bounds of all instances in the HyperPipe */
  59.     protected double [][] m_NumericBounds;
  60.     /** Contains the nominal bounds of all instances in the HyperPipe */
  61.     protected boolean [][] m_NominalBounds;
  62.     /**
  63.      * Creates the HyperPipe as the n-dimensional parallel-piped 
  64.      * with minimum volume containing all the points in
  65.      * pointSet.
  66.      *
  67.      * @param instances all instances belonging to the same class
  68.      * @exception Exception if missing values are found
  69.      */
  70.     public HyperPipe(Instances instances) throws Exception {
  71.       
  72.       m_NumericBounds = new double [instances.numAttributes()][];
  73.       m_NominalBounds = new boolean [instances.numAttributes()][];
  74.       for (int i = 0; i < instances.numAttributes(); i++) {
  75. switch (instances.attribute(i).type()) {
  76. case Attribute.NUMERIC:
  77.   m_NumericBounds[i] = new double [2];
  78.   m_NumericBounds[i][0] = Double.POSITIVE_INFINITY;
  79.   m_NumericBounds[i][1] = Double.NEGATIVE_INFINITY;
  80.   break;
  81. case Attribute.NOMINAL:
  82.   m_NominalBounds[i] = new boolean [instances.attribute(i).numValues()];
  83.   break;
  84. default:
  85.   throw new UnsupportedAttributeTypeException("Cannot process string attributes!");
  86. }
  87.       }
  88.       for (int i = 0; i < instances.numInstances(); i++) {
  89. addInstance(instances.instance(i));
  90.       }
  91.     }
  92.     /**
  93.      * Updates the bounds arrays with a single instance. Missing values
  94.      * are ignored (i.e. they don't change the bounds for that attribute)
  95.      *
  96.      * @param instance the instance
  97.      * @exception Exception if any missing values are encountered
  98.      */
  99.     public void addInstance(Instance instance) throws Exception {
  100.       for (int j = 0; j < instance.numAttributes(); j++) {
  101. if ((j != m_ClassIndex) && (!instance.isMissing(j))) {
  102.   double current = instance.value(j);
  103.   if (m_NumericBounds[j] != null) { // i.e. a numeric attribute
  104.     if (current < m_NumericBounds[j][0])
  105.       m_NumericBounds[j][0] = current;
  106.     if (current > m_NumericBounds[j][1])
  107.       m_NumericBounds[j][1] = current;
  108.   } else { // i.e. a nominal attribute
  109.     m_NominalBounds[j][(int) current] = true;
  110.   }
  111. }
  112.       }
  113.     }
  114.     /**
  115.      * Returns the fraction of the dimensions of a given instance with
  116.      * values lying within the corresponding bounds of the HyperPipe.
  117.      *
  118.      * @param instance the instance
  119.      * @exception Exception if any missing values are encountered
  120.      */
  121.     public double partialContains(Instance instance) throws Exception {
  122.       
  123.       int count = 0;
  124.       for (int i = 0; i < instance.numAttributes(); i++) {
  125. if (i == m_ClassIndex) {
  126.   continue;
  127. }
  128. if (instance.isMissing(i)) {
  129.   continue;
  130. }
  131. double current = instance.value(i);
  132. if (m_NumericBounds[i] != null) { // i.e. a numeric attribute
  133.   if ((current >= m_NumericBounds[i][0]) 
  134.       && (current <= m_NumericBounds[i][1])) {
  135.     count++;
  136.   }
  137. } else { // i.e. a nominal attribute
  138.   if (m_NominalBounds[i][(int) current]) {
  139.     count++;
  140.   }
  141. }
  142.       }
  143.       return ((double)count) / (instance.numAttributes() - 1);
  144.     }
  145.   }
  146.   /**
  147.    * Generates the classifier.
  148.    *
  149.    * @param instances set of instances serving as training data 
  150.    * @exception Exception if the classifier has not been generated successfully
  151.    */
  152.   public void buildClassifier(Instances instances) throws Exception{
  153.     
  154.     if (instances.classIndex() == -1) {
  155.       throw new Exception("No class attribute assigned");
  156.     }
  157.     if (!instances.classAttribute().isNominal()) {
  158.       throw new UnsupportedClassTypeException("HyperPipes: class attribute needs to be nominal!");
  159.     }
  160.     m_ClassIndex = instances.classIndex();
  161.     m_Instances = new Instances(instances, 0); // Copy the structure for ref
  162.     // Create the HyperPipe for each class
  163.     m_HyperPipes = new HyperPipe [instances.numClasses()];
  164.     for (int i = 0; i < m_HyperPipes.length; i++) {
  165.       m_HyperPipes[i] = new HyperPipe(new Instances(instances, 0));
  166.     }
  167.     // Add the instances
  168.     for (int i = 0; i < instances.numInstances(); i++) {
  169.       updateClassifier(instances.instance(i));
  170.     }
  171.   }
  172.   /**
  173.    * Updates the classifier.
  174.    *
  175.    * @param instance the instance to be put into the classifier
  176.    * @exception Exception if the instance could not be included successfully
  177.    */
  178.   public void updateClassifier(Instance instance) throws Exception {
  179.   
  180.     if (instance.classIsMissing()) {
  181.       return;
  182.     }
  183.     m_HyperPipes[(int) instance.classValue()].addInstance(instance);
  184.   }
  185.   /**
  186.    * Classifies the given test instance.
  187.    *
  188.    * @param instance the instance to be classified
  189.    * @return the predicted class for the instance 
  190.    * @exception Exception if the instance can't be classified
  191.    */
  192.   public double [] distributionForInstance(Instance instance) throws Exception {
  193.         
  194.     double [] dist = new double[m_HyperPipes.length];
  195.     for (int j = 0; j < m_HyperPipes.length; j++) {
  196.       dist[j] = m_HyperPipes[j].partialContains(instance);
  197.     }
  198.     Utils.normalize(dist);
  199.     return dist;
  200.   }
  201.   /**
  202.    * Returns a description of this classifier.
  203.    *
  204.    * @return a description of this classifier as a string.
  205.    */
  206.   public String toString() {
  207.     if (m_HyperPipes == null) {
  208.       return ("HyperPipes classifier");
  209.     }
  210.     StringBuffer text = new StringBuffer("HyperPipes classifiern");
  211.     /* Perhaps print out the bounds for each HyperPipe.
  212.     for (int i = 0; i < m_HyperPipes.length; i++) {
  213.       text.append("HyperPipe for class: " 
  214.   + m_Instances.attribute(m_ClassIndex).value(i) + "n");
  215.       text.append(m_HyperPipes[i] + "nn");
  216.     }
  217.     */
  218.     return text.toString();
  219.   }
  220.   /**
  221.    * Main method for testing this class.
  222.    *
  223.    * @param argv should contain command line arguments for evaluation
  224.    * (see Evaluation).
  225.    */
  226.   public static void main(String [] argv) {
  227.     try {
  228.       System.out.println(Evaluation.evaluateModel(new HyperPipes(), argv));
  229.     } catch (Exception e) {
  230.       System.err.println(e.getMessage());
  231.     }
  232.   }
  233. }