KStarNominalAttribute.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 18k
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.  *    KStarNominalAttribute.java
  18.  *    Copyright (c) 1995-97 by Len Trigg (trigg@cs.waikato.ac.nz).
  19.  *    Java port to Weka by Abdelaziz Mahoui (am14@cs.waikato.ac.nz).
  20.  *
  21.  */
  22. package weka.classifiers.kstar;
  23. import java.io.*;
  24. import java.util.*;
  25. import weka.core.*;
  26. import weka.classifiers.*;
  27. /**
  28.  * A custom class which provides the environment for computing the
  29.  * transformation probability of a specified test instance nominal
  30.  * attribute to a specified train instance nominal attribute.
  31.  *
  32.  * @author Len Trigg (len@intelligenesis.net)
  33.  * @author Abdelaziz Mahoui (am14@cs.waikato.ac.nz)
  34.  * @version $Revision 1.0 $
  35.  */
  36. public class KStarNominalAttribute implements KStarConstants {
  37.   
  38.   /** The training instances used for classification. */
  39.   protected Instances m_TrainSet;
  40.   /** The test instance */
  41.   protected Instance m_Test;
  42.   /** The train instance */
  43.   protected Instance m_Train;
  44.   /** The index of the nominal attribute in the test and train instances */
  45.   protected int m_AttrIndex;
  46.   /** The stop parameter */
  47.   protected double m_Stop = 1.0;
  48.   /** Probability of test attribute transforming into train attribute 
  49.       with missing value */
  50.   protected double m_MissingProb = 1.0;
  51.   /** Average probability of test attribute transforming into train 
  52.       attribute */
  53.   protected double m_AverageProb = 1.0;
  54.   /** Smallest probability of test attribute transforming into 
  55.       train attribute */
  56.   protected double m_SmallestProb = 1.0;
  57.   /** Number of trai instances with no missing attribute values */
  58.   protected int m_TotalCount;
  59.   /** Distribution of the attribute value in the train dataset */
  60.   protected int [] m_Distribution;
  61.   /** Set of colomns: each colomn representing a randomised version 
  62.       of the train dataset class colomn */
  63.   protected int [][] m_RandClassCols;
  64.   /** A cache for storing attribute values and their corresponding 
  65.       stop parameters */
  66.   protected KStarCache m_Cache;
  67.   // KStar Global settings
  68.   /** The number of instances in the dataset */
  69.   protected int m_NumInstances;
  70.   /** The number of class values */
  71.   protected int m_NumClasses;
  72.   /** The number of attributes */
  73.   protected int m_NumAttributes;
  74.   /** The class attribute type */
  75.   protected int m_ClassType;
  76.   /** missing value treatment */
  77.   protected int m_MissingMode = M_AVERAGE;
  78.   /** B_SPHERE = use specified blend, B_ENTROPY = entropic blend setting */
  79.   protected int m_BlendMethod = B_SPHERE ;
  80.   /** default sphere of influence blend setting */
  81.   protected int m_BlendFactor = 20;
  82.   
  83.   /**
  84.    * Constructor
  85.    */
  86.   public KStarNominalAttribute(Instance test, Instance train, int attrIndex,
  87.        Instances trainSet, int [][] randClassCol, 
  88.        KStarCache cache)
  89.   {
  90.     m_Test = test;
  91.     m_Train = train;
  92.     m_AttrIndex = attrIndex;
  93.     m_TrainSet = trainSet;
  94.     m_RandClassCols = randClassCol;
  95.     m_Cache = cache;
  96.     init();
  97.   }
  98.   /**
  99.    * Initializes the m_Attributes of the class.
  100.    */
  101.   private void init() {
  102.     try {
  103.       m_NumInstances  = m_TrainSet.numInstances();
  104.       m_NumClasses    = m_TrainSet.numClasses();
  105.       m_NumAttributes = m_TrainSet.numAttributes();
  106.       m_ClassType     = m_TrainSet.classAttribute().type();
  107.     } catch(Exception e) {
  108.       e.printStackTrace();
  109.     }
  110.   }
  111.   /**
  112.    * Calculates the probability of the indexed nominal attribute of the test
  113.    * instance transforming into the indexed nominal attribute of the training 
  114.    * instance.
  115.    *
  116.    * @return the value of the transformation probability.
  117.    */
  118.   public double transProb() {
  119.     String debug = "(KStarNominalAttribute.transProb) ";
  120.     double transProb = 0.0;
  121.     // check if the attribute value has been encountred before
  122.     // in which case it should be in the nominal cache
  123.     if (m_Cache.containsKey(m_Test.value(m_AttrIndex))) {
  124.       KStarCache.TableEntry te = 
  125. m_Cache.getCacheValues(m_Test.value(m_AttrIndex));
  126.       m_Stop = te.value;
  127.       m_MissingProb = te.pmiss;
  128.     }
  129.     else {
  130.       generateAttrDistribution();
  131.       // we have to compute the parameters
  132.       if (m_BlendMethod == B_ENTROPY) {
  133. m_Stop = stopProbUsingEntropy();
  134.       }
  135.       else { // default is B_SPHERE
  136. m_Stop = stopProbUsingBlend();
  137.       }
  138.       // store the values in cache
  139.       m_Cache.store( m_Test.value(m_AttrIndex), m_Stop, m_MissingProb );
  140.     }
  141.     // we've got our m_Stop, then what?
  142.     if (m_Train.isMissing(m_AttrIndex)) {
  143.       transProb = m_MissingProb;
  144.     }
  145.     else {
  146.       try {
  147. transProb = (1.0 - m_Stop) / m_Test.attribute(m_AttrIndex).numValues();
  148. if ( (int)m_Test.value(m_AttrIndex) == 
  149.      (int)m_Train.value(m_AttrIndex) )
  150.   {
  151.     transProb += m_Stop;
  152.   }
  153.       } catch (Exception e) {
  154. e.printStackTrace();
  155.       }
  156.     }
  157.     return transProb;
  158.   }
  159.   
  160.   /**
  161.    * Calculates the "stop parameter" for this attribute using
  162.    * the entropy method: the value is computed using a root finder
  163.    * algorithm. The method takes advantage of the calculation to
  164.    * compute the smallest and average transformation probabilities
  165.    * once the stop factor is obtained. It also sets the transformation
  166.    * probability to an attribute with a missing value.
  167.    *
  168.    * @return the value of the stop parameter.
  169.    *
  170.    */
  171.   private double stopProbUsingEntropy() {
  172.     String debug = "(KStarNominalAttribute.stopProbUsingEntropy)";
  173.     if ( m_ClassType != Attribute.NOMINAL ) {
  174.       System.err.println("Error: "+debug+" attribute class must be nominal!");
  175.       System.exit(1);
  176.     }
  177.     int itcount = 0;
  178.     double stopProb;
  179.     double lower, upper, pstop;
  180.     double bestminprob = 0.0, bestpsum = 0.0;
  181.     double bestdiff = 0.0, bestpstop = 0.0;
  182.     double currentdiff, lastdiff, stepsize, delta;
  183.     
  184.     KStarWrapper botvals = new KStarWrapper();
  185.     KStarWrapper upvals = new KStarWrapper();
  186.     KStarWrapper vals = new KStarWrapper();
  187.     // Initial values for root finder
  188.     lower = 0.0 + ROOT_FINDER_ACCURACY/2.0;
  189.     upper = 1.0 - ROOT_FINDER_ACCURACY/2.0;
  190.     
  191.     // Find (approx) entropy ranges
  192.     calculateEntropy(upper, upvals);
  193.     calculateEntropy(lower, botvals);
  194.     
  195.     if (upvals.avgProb == 0) {
  196.       // When there are no training instances with the test value:
  197.       // doesn't matter what exact value we use for pstop, just acts as
  198.       // a constant scale factor in this case.
  199.       calculateEntropy(lower, vals);
  200.     }
  201.     else
  202.       {
  203. // Optimise the scale factor
  204. if ( (upvals.randEntropy - upvals.actEntropy < 
  205.       botvals.randEntropy - botvals.actEntropy) &&
  206.      (botvals.randEntropy - botvals.actEntropy > FLOOR) )
  207.   {
  208.     bestpstop = pstop = lower;
  209.     stepsize = INITIAL_STEP;
  210.     bestminprob = botvals.minProb;
  211.     bestpsum = botvals.avgProb;
  212.   }
  213. else {
  214.   bestpstop = pstop = upper;
  215.   stepsize = -INITIAL_STEP;
  216.   bestminprob = upvals.minProb;
  217.   bestpsum = upvals.avgProb;
  218. }
  219. bestdiff = currentdiff = FLOOR;
  220. itcount = 0;
  221. /* Enter the root finder */
  222. while (true)
  223.   {
  224.     itcount++;
  225.     lastdiff = currentdiff;
  226.     pstop += stepsize;
  227.     if (pstop <= lower) {
  228.       pstop = lower;
  229.       currentdiff = 0.0;
  230.       delta = -1.0;
  231.     }
  232.     else if (pstop >= upper) {
  233.       pstop = upper;
  234.       currentdiff = 0.0;
  235.       delta = -1.0;
  236.     }
  237.     else {
  238.       calculateEntropy(pstop, vals);
  239.       currentdiff = vals.randEntropy - vals.actEntropy;
  240.       if (currentdiff < FLOOR) {
  241. currentdiff = FLOOR;
  242. if ((Math.abs(stepsize) < INITIAL_STEP) && 
  243.     (bestdiff == FLOOR)) {
  244.   bestpstop = lower;
  245.   bestminprob = botvals.minProb;
  246.   bestpsum = botvals.avgProb;
  247.   break;
  248. }
  249.       }
  250.       delta = currentdiff - lastdiff;
  251.     }
  252.     if (currentdiff > bestdiff) {
  253.       bestdiff = currentdiff;
  254.       bestpstop = pstop;
  255.       bestminprob = vals.minProb;
  256.       bestpsum = vals.avgProb;
  257.     }
  258.     if (delta < 0) {
  259.       if (Math.abs(stepsize) < ROOT_FINDER_ACCURACY) {
  260. break;
  261.       }
  262.       else {
  263. stepsize /= -2.0;
  264.       }
  265.     }
  266.     if (itcount > ROOT_FINDER_MAX_ITER) {
  267.       break;
  268.     }
  269.   }
  270.       }
  271.     
  272.     m_SmallestProb = bestminprob;
  273.     m_AverageProb = bestpsum;
  274.     // Set the probability of transforming to a missing value
  275.     switch ( m_MissingMode )
  276.       {
  277.       case M_DELETE:
  278. m_MissingProb = 0.0;
  279. break;
  280.       case M_NORMAL:
  281. m_MissingProb = 1.0;
  282. break;
  283.       case M_MAXDIFF:
  284. m_MissingProb = m_SmallestProb;
  285. break;
  286.       case M_AVERAGE:
  287. m_MissingProb = m_AverageProb;
  288. break;
  289.       }
  290.     if ( Math.abs(bestpsum - (double)m_TotalCount) < EPSILON) { 
  291.       // No difference in the values
  292.       stopProb = 1.0;
  293.     }
  294.     else {
  295.       stopProb = bestpstop;
  296.     }
  297.     return stopProb;
  298.   }
  299.   /**
  300.    * Calculates the entropy of the actual class prediction
  301.    * and the entropy for random class prediction. It also
  302.    * calculates the smallest and average transformation probabilities.
  303.    *
  304.    * @param stop the stop parameter
  305.    * @param params the object wrapper for the parameters:
  306.    * actual entropy, random entropy, average probability and smallest 
  307.    * probability.
  308.    * @return the values are returned in the object "params".
  309.    *
  310.    */
  311.   private void calculateEntropy( double stop, KStarWrapper params) {
  312.     String debug = "(KStarNominalAttribute.calculateEntropy)";
  313.     int i,j,k;
  314.     Instance train;
  315.     double actent = 0.0, randent=0.0;
  316.     double pstar, tprob, psum=0.0, minprob=1.0;
  317.     double actClassProb, randClassProb;
  318.     double [][] pseudoClassProb = new double[NUM_RAND_COLS+1][m_NumClasses];
  319.     // init ...
  320.     for(j = 0; j <= NUM_RAND_COLS; j++) {
  321.       for(i = 0; i < m_NumClasses; i++) {
  322. pseudoClassProb[j][i] = 0.0;
  323.       }
  324.     }
  325.     for (i=0; i < m_NumInstances; i++) {
  326.       train = m_TrainSet.instance(i);
  327.       if (!train.isMissing(m_AttrIndex)) {
  328. pstar = PStar(m_Test, train, m_AttrIndex, stop);
  329. tprob = pstar / m_TotalCount;
  330. if (pstar < minprob) {
  331.   minprob = pstar;
  332. }
  333. psum += tprob;
  334. // filter instances with same class value
  335. for (k=0 ; k <= NUM_RAND_COLS ; k++) {
  336.   // instance i is assigned a random class value in colomn k;
  337.   // colomn k = NUM_RAND_COLS contains the original mapping: 
  338.   // instance -> class vlaue
  339.   pseudoClassProb[k][ m_RandClassCols[k][i] ] += tprob;
  340. }
  341.       }
  342.     }
  343.     // compute the actual entropy using the class probs
  344.     // with the original class value mapping (colomn NUM_RAND_COLS)
  345.     for (j=m_NumClasses-1; j>=0; j--) {
  346.       actClassProb = pseudoClassProb[NUM_RAND_COLS][j] / psum;
  347.       if (actClassProb > 0) {
  348.      actent -= actClassProb * Math.log(actClassProb) / LOG2;
  349.       }
  350.     }
  351.     // compute a random entropy using the pseudo class probs
  352.     // excluding the colomn NUM_RAND_COLS
  353.     for (k=0; k < NUM_RAND_COLS;k++) {
  354.       for (i = m_NumClasses-1; i >= 0; i--) {
  355.    randClassProb = pseudoClassProb[k][i] / psum;
  356.    if (randClassProb > 0) {
  357.      randent -= randClassProb * Math.log(randClassProb) / LOG2;
  358. }
  359.       }
  360.     }
  361.     randent /= NUM_RAND_COLS;
  362.     // return the results ... Yuk !!!
  363.     params.actEntropy = actent;
  364.     params.randEntropy = randent;
  365.     params.avgProb = psum;
  366.     params.minProb = minprob;
  367.   }
  368.   
  369.   /**
  370.    * Calculates the "stop parameter" for this attribute using
  371.    * the blend method: the value is computed using a root finder
  372.    * algorithm. The method takes advantage of this calculation to
  373.    * compute the smallest and average transformation probabilities
  374.    * once the stop factor is obtained. It also sets the transformation
  375.    * probability to an attribute with a missing value.
  376.    *
  377.    * @return the value of the stop parameter.
  378.    *
  379.    */
  380.   private double stopProbUsingBlend() {
  381.     String debug = "(KStarNominalAttribute.stopProbUsingBlend) ";
  382.     int itcount = 0;
  383.     double stopProb, aimfor;
  384.     double lower, upper, tstop;
  385.     KStarWrapper botvals = new KStarWrapper();
  386.     KStarWrapper upvals = new KStarWrapper();
  387.     KStarWrapper vals = new KStarWrapper();
  388.     int testvalue = (int)m_Test.value(m_AttrIndex);
  389.     aimfor = (m_TotalCount - m_Distribution[testvalue]) * 
  390.       (double)m_BlendFactor / 100.0 + m_Distribution[testvalue];
  391.     // Initial values for root finder
  392.     tstop = 1.0 - (double)m_BlendFactor / 100.0;
  393.     lower = 0.0 + ROOT_FINDER_ACCURACY/2.0;
  394.     upper = 1.0 - ROOT_FINDER_ACCURACY/2.0;
  395.     // Find out function border values
  396.     calculateSphereSize(testvalue, lower, botvals);
  397.     botvals.sphere -= aimfor;
  398.     calculateSphereSize(testvalue, upper, upvals);
  399.     upvals.sphere -= aimfor;
  400.     
  401.     if (upvals.avgProb == 0) {
  402.       // When there are no training instances with the test value:
  403.       // doesn't matter what exact value we use for tstop, just acts as
  404.       // a constant scale factor in this case.
  405.       calculateSphereSize(testvalue, tstop, vals);
  406.     }
  407.     else if (upvals.sphere > 0) {
  408.       // Can't include aimfor instances, going for min possible
  409.       tstop = upper;
  410.       vals.avgProb = upvals.avgProb;
  411.     }
  412.     else {
  413.       // Enter the root finder
  414.       for (;;) {
  415. itcount++;
  416. calculateSphereSize(testvalue, tstop, vals);
  417. vals.sphere -= aimfor;
  418. if ( Math.abs(vals.sphere) <= ROOT_FINDER_ACCURACY ||
  419.      itcount >= ROOT_FINDER_MAX_ITER )
  420.   {
  421.     break;
  422.   }
  423. if (vals.sphere > 0.0) {
  424.   lower = tstop;
  425.   tstop = (upper + lower) / 2.0;
  426. }
  427. else {
  428.   upper = tstop;
  429.   tstop = (upper + lower) / 2.0;
  430. }
  431.       }
  432.     }
  433.     m_SmallestProb = vals.minProb;
  434.     m_AverageProb = vals.avgProb;
  435.     // Set the probability of transforming to a missing value
  436.     switch ( m_MissingMode )
  437.       {
  438.       case M_DELETE:
  439. m_MissingProb = 0.0;
  440. break;
  441.       case M_NORMAL:
  442. m_MissingProb = 1.0;
  443. break;
  444.       case M_MAXDIFF:
  445. m_MissingProb = m_SmallestProb;
  446. break;
  447.       case M_AVERAGE:
  448. m_MissingProb = m_AverageProb;
  449. break;
  450.       }
  451.     
  452.     if ( Math.abs(vals.avgProb - m_TotalCount) < EPSILON) { 
  453.       // No difference in the values
  454.       stopProb = 1.0;
  455.     }
  456.     else {
  457.       stopProb = tstop;
  458.     }
  459.     return stopProb;
  460.   }
  461.   
  462.   /**
  463.    * Calculates the size of the "sphere of influence" defined as:
  464.    * sphere = sum(P^2)/sum(P)^2
  465.    * P(i|j) = (1-tstop)*P(i) + ((i==j)?tstop:0).
  466.    * This method takes advantage of the calculation to compute the values of
  467.    * the "smallest" and "average" transformation probabilities when using
  468.    * the specified stop parameter.
  469.    *
  470.    * @param testValue the value of the test instance
  471.    * @param stop the stop parameter
  472.    * @param params a wrapper of the parameters to be computed:
  473.    * "sphere" the sphere size
  474.    * "avgprob" the average transformation probability
  475.    * "minProb" the smallest transformation probability
  476.    * @return the values are returned in "params" object.
  477.    *
  478.    */
  479.   private void calculateSphereSize(int testvalue, double stop, 
  480.    KStarWrapper params) {
  481.     String debug = "(KStarNominalAttribute.calculateSphereSize) ";
  482.     int i, thiscount;
  483.     double tprob, tval = 0.0, t1 = 0.0;
  484.     double sphere, minprob = 1.0, transprob = 0.0;
  485.     for(i = 0; i < m_Distribution.length; i++) {
  486.       thiscount = m_Distribution[i];
  487.       if ( thiscount != 0 ) {
  488. if ( testvalue == i ) {
  489.   tprob = (stop + (1 - stop) / m_Distribution.length) / m_TotalCount;
  490.   tval += tprob * thiscount;
  491.   t1 += tprob * tprob * thiscount;
  492. }
  493. else {
  494.   tprob = ((1 - stop) / m_Distribution.length) / m_TotalCount;
  495.   tval += tprob * thiscount;
  496.   t1 += tprob * tprob * thiscount;
  497. }
  498. if ( minprob > tprob * m_TotalCount ) {
  499.   minprob = tprob * m_TotalCount;
  500. }
  501.       }
  502.     }
  503.     transprob = tval;
  504.     sphere = (t1 == 0) ? 0 : ((tval * tval) / t1);
  505.     // return values ... Yck!!!
  506.     params.sphere = sphere;
  507.     params.avgProb = transprob;
  508.     params.minProb = minprob;
  509.   }
  510.   
  511.   /**
  512.    * Calculates the nominal probability function defined as:
  513.    * P(i|j) = (1-stop) * P(i) + ((i==j) ? stop : 0)
  514.    * In this case, it calculates the transformation probability of the
  515.    * indexed test attribute to the indexed train attribute.
  516.    *
  517.    * @param test the test instance
  518.    * @param train the train instance
  519.    * @param col the attribute index
  520.    * @return the value of the tranformation probability.
  521.    *
  522.    */
  523.   private double PStar(Instance test, Instance train, int col, double stop) {
  524.     String debug = "(KStarNominalAttribute.PStar) ";
  525.     double pstar;
  526.     int numvalues = 0;
  527.     try {
  528.       numvalues = test.attribute(col).numValues();
  529.     } catch (Exception ex) {
  530.       ex.printStackTrace();
  531.     }
  532.     if ( (int)test.value(col) == (int)train.value(col) ) {
  533.       pstar = stop + (1 - stop) / numvalues;
  534.     }
  535.     else {
  536.       pstar = (1 - stop) / numvalues;
  537.     }
  538.     return pstar;
  539.   }
  540.   
  541.   /**
  542.    * Calculates the distribution, in the dataset, of the indexed nominal
  543.    * attribute values. It also counts the actual number of training instances
  544.    * that contributed (those with non-missing values) to calculate the 
  545.    * distribution.
  546.    */
  547.   private void generateAttrDistribution() {
  548.     String debug = "(KStarNominalAttribute.generateAttrDistribution)";
  549.     m_Distribution = new int[ m_TrainSet.attribute(m_AttrIndex).numValues() ];
  550.     int i;
  551.     Instance train;
  552.     for (i=0; i < m_NumInstances; i++) {
  553.       train = m_TrainSet.instance(i);
  554.       if ( !train.isMissing(m_AttrIndex) ) {
  555. m_TotalCount++;
  556. m_Distribution[(int)train.value(m_AttrIndex)]++;
  557.       }
  558.     }
  559.   }
  560.   /**
  561.    * Sets the options.
  562.    *
  563.    */
  564.   public void setOptions(int missingmode, int blendmethod, int blendfactor) {
  565.     m_MissingMode = missingmode;
  566.     m_BlendMethod = blendmethod;
  567.     m_BlendFactor = blendfactor;
  568.   }
  569. } // class