KStarNumericAttribute.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.  *    KStarNumericAttribute.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.lazy.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 numeric
  30.  * attribute to a specified train instance numeric 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 KStarNumericAttribute implements KStarConstants {
  37.   /** The training instances used for classification. */
  38.   protected Instances m_TrainSet;
  39.   /** The test instance */
  40.   protected Instance m_Test;
  41.   /** The train instance */
  42.   protected Instance m_Train;
  43.   /** The index of the attribute in the test and train instances */
  44.   protected int m_AttrIndex;
  45.   /** The scale parameter */
  46.   protected double m_Scale = 1.0;
  47.   /** Probability of test attribute transforming into train attribute 
  48.       with missing value */
  49.   protected double m_MissingProb = 1.0;
  50.   /** Average probability of test attribute transforming into train 
  51.       attribute */
  52.   protected double m_AverageProb = 1.0;
  53.   /** Smallest probability of test attribute transforming into train 
  54.       attribute */
  55.   protected double m_SmallestProb = 1.0;
  56.   /** The set of disctances from the test attribute to the set of train 
  57.       attributes */
  58.   protected double [] m_Distances;
  59.   /** Set of colomns: each colomn representing a randomised version of 
  60.       the train dataset class colomn */
  61.   protected int [][] m_RandClassCols;
  62.   /** The number of train instances with no missing attribute values */
  63.   protected int m_ActualCount = 0;
  64.   /** A cache for storing attribute values and their corresponding scale 
  65.       parameters */
  66.   protected KStarCache m_Cache;
  67.   /** The number of instances in the dataset */
  68.   protected int m_NumInstances;
  69.   /** The number of class values */
  70.   protected int m_NumClasses;
  71.   /** The number of attributes */
  72.   protected int m_NumAttributes;
  73.   /** The class attribute type */
  74.   protected int m_ClassType;
  75.   /** missing value treatment */
  76.   protected int m_MissingMode = M_AVERAGE;
  77.   /** 0 = use specified blend, 1 = entropic blend setting */
  78.   protected int m_BlendMethod = B_SPHERE ;
  79.   /** default sphere of influence blend setting */
  80.   protected int m_BlendFactor = 20;
  81.   
  82.   /**
  83.    * Constructor
  84.    */
  85.   public KStarNumericAttribute(Instance test, Instance train, int attrIndex,
  86.        Instances trainSet, 
  87.        int [][] randClassCols, 
  88.        KStarCache cache)
  89.   {
  90.     m_Test      = test;
  91.     m_Train     = train;
  92.     m_AttrIndex = attrIndex;
  93.     m_TrainSet  = trainSet;
  94.     m_RandClassCols = randClassCols;
  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.   /**
  113.    * Calculates the transformation probability of the attribute indexed
  114.    * "m_AttrIndex" in test instance "m_Test" to the same attribute in
  115.    * the train instance "m_Train".
  116.    *
  117.    * @return the probability value
  118.    */
  119.   public double transProb() {
  120.     String debug = "(KStarNumericAttribute.transProb) ";
  121.     double transProb, distance, scale;
  122.     // check if the attribute value has been encountred before
  123.     // in which case it should be in the numeric cache
  124.     if ( m_Cache.containsKey(m_Test.value(m_AttrIndex))) {
  125.       KStarCache.TableEntry te = 
  126. m_Cache.getCacheValues( m_Test.value(m_AttrIndex) );
  127.       m_Scale = te.value;
  128.       m_MissingProb = te.pmiss;
  129.     }
  130.     else {
  131.       if (m_BlendMethod == B_ENTROPY) {
  132. m_Scale = scaleFactorUsingEntropy();
  133.       }
  134.       else { // default is B_SPHERE
  135. m_Scale = scaleFactorUsingBlend();
  136.       }
  137.       m_Cache.store( m_Test.value(m_AttrIndex), m_Scale, m_MissingProb );
  138.     }
  139.     // now what???
  140.     if (m_Train.isMissing(m_AttrIndex)) {
  141.       transProb = m_MissingProb;
  142.     }
  143.     else {
  144.       distance = 
  145. Math.abs( m_Test.value(m_AttrIndex) - m_Train.value(m_AttrIndex) );
  146.       transProb = PStar( distance, m_Scale );
  147.     }
  148.     return transProb;
  149.   }
  150.   
  151.   /**
  152.    * Calculates the scale factor for the attribute indexed
  153.    * "m_AttrIndex" in test instance "m_Test" using a global
  154.    * blending factor (default value is 20%).
  155.    *
  156.    * @return the scale factor value
  157.    */
  158.   private double scaleFactorUsingBlend() {
  159.     String debug = "(KStarNumericAttribute.scaleFactorUsingBlend)";
  160.     int i, j, lowestcount = 0, count = 0;
  161.     double lowest = -1.0, nextlowest = -1.0;
  162.     double root, broot, up, bot;
  163.     double aimfor, min_val = 9e300, scale = 1.0;
  164.     double avgprob = 0.0, minprob = 0.0, min_pos = 0.0;
  165.     KStarWrapper botvals = new KStarWrapper();
  166.     KStarWrapper upvals = new KStarWrapper();
  167.     KStarWrapper vals = new KStarWrapper();
  168.     m_Distances = new double [m_NumInstances];
  169.     for (j=0; j<m_NumInstances; j++) {
  170.       if ( m_TrainSet.instance(j).isMissing(m_AttrIndex) ) {
  171. // mark the train instance with a missing value by setting 
  172. // the distance to -1.0
  173. m_Distances[j] = -1.0;
  174.       }
  175.       else {
  176. m_Distances[j] = Math.abs(m_TrainSet.instance(j).value(m_AttrIndex) - 
  177.   m_Test.value(m_AttrIndex));
  178. if ( (m_Distances[j]+1e-5) < nextlowest || nextlowest == -1.0 ) {
  179.   if ( (m_Distances[j]+1e-5) < lowest || lowest == -1.0 ) {
  180.     nextlowest = lowest;
  181.     lowest = m_Distances[j];
  182.     lowestcount = 1;
  183.   }
  184.   else if ( Math.abs(m_Distances[j]-lowest) < 1e-5 ) {
  185.     // record the number training instances (number n0) at
  186.     // the smallest distance from test instance
  187.     lowestcount++;
  188.   }
  189.   else {
  190.     nextlowest = m_Distances[j];
  191.   }
  192. }
  193. // records the actual number of instances with no missing value
  194. m_ActualCount++;
  195.       }
  196.     }
  197.     
  198.     if (nextlowest == -1 || lowest == -1) { // Data values are all the same
  199.       scale = 1.0;
  200.       m_SmallestProb = m_AverageProb = 1.0;
  201.       return scale;
  202.     }
  203.     else {
  204.       // starting point for root
  205.       root = 1.0 / (nextlowest - lowest);
  206.       i = 0;
  207.       // given the expression: n0 <= E(scale) <= N
  208.       // E(scale) =  (N - n0) * b + n0  with blending factor: 0 <= b <= 1
  209.       // aimfor = (N - n0) * b + n0
  210.       aimfor = (m_ActualCount - lowestcount) * 
  211. (double)m_BlendFactor / 100.0 + lowestcount;
  212.       if (m_BlendFactor == 0) {
  213. aimfor += 1.0;
  214.       }
  215.       // root is bracketed in interval [bot,up]
  216.       bot = 0.0 + ROOT_FINDER_ACCURACY / 2.0;
  217.       up = root * 16;     // This is bodgy
  218.       // E(bot)
  219.       calculateSphereSize(bot, botvals);
  220.       botvals.sphere -= aimfor;
  221.       // E(up)
  222.       calculateSphereSize(up, upvals);
  223.       upvals.sphere -= aimfor;
  224.       
  225.       if (botvals.sphere < 0) {    // Couldn't include that many 
  226.                            // instances - going for max possible
  227. min_pos = bot;
  228. avgprob = botvals.avgProb;
  229. minprob = botvals.minProb;
  230.       }
  231.       else if (upvals.sphere > 0) { // Couldn't include that few, 
  232.                             // going for min possible
  233. min_pos = up;
  234. avgprob = upvals.avgProb;
  235. minprob = upvals.minProb;
  236.       }
  237.       else {
  238. // Root finding Algorithm starts here !
  239. for (;;) {
  240.   calculateSphereSize(root, vals);
  241.   vals.sphere -= aimfor;
  242.   if ( Math.abs(vals.sphere) < min_val ) {
  243.     min_val = Math.abs(vals.sphere);
  244.     min_pos = root;
  245.     avgprob = vals.avgProb;
  246.     minprob = vals.minProb;
  247.   }
  248.   if ( Math.abs(vals.sphere) <= ROOT_FINDER_ACCURACY ) {
  249.     break;        // converged to a solution, done!
  250.   }
  251.   if (vals.sphere > 0.0) {
  252.     broot = (root + up) / 2.0;
  253.     bot = root;
  254.     root = broot;
  255.   }
  256.   else {
  257.     broot = (root + bot) / 2.0;
  258.     up = root;
  259.     root = broot;
  260.   }
  261.   i++;
  262.   if (i > ROOT_FINDER_MAX_ITER) {
  263.     //     System.err.println("Warning: "+debug+" 
  264.     // ROOT_FINDER_MAX_ITER exceeded");
  265.     root = min_pos;
  266.     break;
  267.   }
  268. }
  269.       }
  270.       m_SmallestProb = minprob;
  271.       m_AverageProb = avgprob;
  272.       // Set the probability of transforming to a missing value
  273.       switch ( m_MissingMode )
  274. {
  275. case M_DELETE:
  276.   m_MissingProb = 0.0;
  277.   break;
  278. case M_NORMAL:
  279.   m_MissingProb = 1.0;
  280.   break;
  281. case M_MAXDIFF:
  282.   m_MissingProb = m_SmallestProb;
  283.   break;
  284. case M_AVERAGE:
  285.   m_MissingProb = m_AverageProb;
  286.   break;
  287. }
  288.       // set the scale factor value
  289.       scale = min_pos;
  290.       return scale;
  291.     }
  292.   }
  293.   
  294.   /**
  295.    * Calculates the size of the "sphere of influence" defined as:
  296.    * sphere = sum(P)^2/sum(P^2) where
  297.    * P(i) = root*exp(-2*i*root).
  298.    * Since there are n different training instances we multiply P(i) by 1/n.
  299.    */
  300.   private void calculateSphereSize(double scale, KStarWrapper params) {
  301.     String debug = "(KStarNumericAttribute.calculateSphereSize)";
  302.     int i;
  303.     double sphereSize, minprob = 1.0;
  304.     double pstar;                // P*(b|a)
  305.     double pstarSum = 0.0;       // sum(P*)
  306.     double pstarSquareSum = 0.0; // sum(P*^2)
  307.     double inc;
  308.     for (i = 0; i < m_NumInstances; i++) {
  309.       if (m_Distances[i] < 0) {
  310. // instance with missing value
  311. continue;
  312.       }
  313.       else {
  314. pstar = PStar( m_Distances[i], scale );
  315. if (minprob > pstar) {
  316.   minprob = pstar;
  317. }
  318. inc = pstar / m_ActualCount;
  319. pstarSum += inc;
  320. pstarSquareSum += inc * inc;
  321.       }
  322.     }
  323.     sphereSize = (pstarSquareSum == 0 ? 0 
  324.   : pstarSum * pstarSum / pstarSquareSum);
  325.     // return the values
  326.     params.sphere = sphereSize;
  327.     params.avgProb = pstarSum;
  328.     params.minProb = minprob;
  329.   }
  330.   
  331.   /**
  332.    * Calculates the scale factor using entropy.
  333.    *
  334.    * @return the scale factor value
  335.    */
  336.   private double scaleFactorUsingEntropy() {
  337.     String debug = "(KStarNumericAttribute.scaleFactorUsingEntropy)";
  338.     if ( m_ClassType != Attribute.NOMINAL ) {
  339.       System.err.println("Error: "+debug+" attribute class must be nominal!");
  340.       System.exit(1);
  341.     }
  342.     int i,j, lowestcount = 0, count, itcount;
  343.     double lowest = -1.0, nextlowest = -1.0;
  344.     double root, up, bot, stepsize, delta;
  345.     double actentropy = 0.0, randentropy = 0.0, actscale, randscale;
  346.     double minrand = 0.0, minact = 0.0, maxrand = 0.0, maxact = 0.0;
  347.     double bestdiff, bestroot, currentdiff, lastdiff;
  348.     double bestpsum, bestminprob, scale = 1.0;
  349.     KStarWrapper botvals = new KStarWrapper();
  350.     KStarWrapper upvals = new KStarWrapper();
  351.     KStarWrapper vals = new KStarWrapper();
  352.     m_Distances = new double [m_NumInstances];
  353.     for (j=0; j<m_NumInstances; j++) {
  354.       if ( m_TrainSet.instance(j).isMissing(m_AttrIndex) ) {
  355. // mark the train instance with a missing value by setting 
  356. // the distance to -1.0
  357. m_Distances[j] = -1.0;
  358.       }
  359.       else {
  360. m_Distances[j] = Math.abs(m_TrainSet.instance(j).value(m_AttrIndex) - 
  361.   m_Test.value(m_AttrIndex));
  362. if ( (m_Distances[j]+1e-5) < nextlowest || nextlowest == -1.0 ) {
  363.   if ( (m_Distances[j]+1e-5) < lowest || lowest == -1.0 ) {
  364.     nextlowest = lowest;
  365.     lowest = m_Distances[j];
  366.     lowestcount = 1;
  367.   }
  368.   else if ( Math.abs(m_Distances[j]-lowest) < 1e-5 ) {
  369.     // record the number training instances (number n0) at
  370.     // the smallest distance from test instance
  371.     lowestcount++;
  372.   }
  373.   else {
  374.     nextlowest = m_Distances[j];
  375.   }
  376. }
  377. // records the actual number of instances with no missing value
  378. m_ActualCount++;
  379.       }
  380.     } // for
  381.     
  382.     if (nextlowest == -1 || lowest == -1) { // Data values are all the same
  383.       scale = 1.0;
  384.       m_SmallestProb = m_AverageProb = 1.0;
  385.       return scale;
  386.     }
  387.     else {
  388.       // starting point for root
  389.       root = 1.0 / (nextlowest - lowest);
  390.       // root is bracketed in interval [bot,up]
  391.       bot = 0.0 + ROOT_FINDER_ACCURACY / 2;  
  392.       up = root * 8; // This is bodgy
  393.       // Find (approx) entropy ranges
  394.       calculateEntropy(up, upvals);
  395.       calculateEntropy(bot, botvals);
  396.       actscale = botvals.actEntropy - upvals.actEntropy;
  397.       randscale = botvals.randEntropy - upvals.randEntropy;
  398.       // Optimise the scale factor
  399.       bestroot = root = bot;
  400.       bestdiff = currentdiff = FLOOR1;
  401.       bestpsum = botvals.avgProb;
  402.       bestminprob = botvals.minProb;
  403.       stepsize = (up - bot) / 20.0;
  404.       itcount = 0;
  405.       // Root finding algorithm starts here!
  406.       while (true)
  407. {
  408.   itcount++;
  409.   lastdiff = currentdiff;
  410.   root += Math.log(root + 1.0) * stepsize;
  411.   if (root <= bot) {
  412.     root = bot;
  413.     currentdiff = 0.0;
  414.     delta = -1.0;
  415.   }
  416.   else if (root >= up) {
  417.     root = up;
  418.     currentdiff = 0.0;
  419.     delta = -1.0;
  420.   }
  421.   else {
  422.     calculateEntropy(root, vals);
  423.     // Normalise entropies
  424.     vals.randEntropy = (vals.randEntropy - upvals.randEntropy) / 
  425.       randscale;
  426.     vals.actEntropy = (vals.actEntropy - upvals.actEntropy) / 
  427.       randscale;
  428.     currentdiff = vals.randEntropy - vals.actEntropy;
  429.     if (currentdiff < FLOOR1) {
  430.       currentdiff = FLOOR1;
  431.       if (stepsize < 0) { 
  432. // If we've hit the end and turned around we can't 
  433. // have found any peaks
  434. bestdiff = currentdiff;
  435. bestroot = bot;
  436. bestpsum = botvals.avgProb;
  437. bestminprob = botvals.minProb;
  438. break;
  439.       }
  440.     }
  441.     delta = currentdiff - lastdiff;
  442.   }
  443.   if (currentdiff > bestdiff) {
  444.     bestdiff = currentdiff;
  445.     bestroot = root;
  446.     bestminprob = vals.minProb;
  447.     bestpsum = vals.avgProb;
  448.   }
  449.   if (delta < 0) {
  450.     if (Math.abs(stepsize) < ROOT_FINDER_ACCURACY) {
  451.       break;
  452.     }
  453.     else {
  454.       stepsize /= -4.0;
  455.     }
  456.   }
  457.   if (itcount > ROOT_FINDER_MAX_ITER) {
  458.     //  System.err.println("Warning: "+debug+" ROOT_FINDER_MAX_ITER 
  459.     // exceeded");
  460.     break;
  461.   }
  462. } // while
  463.       m_SmallestProb = bestminprob;
  464.       m_AverageProb = bestpsum;
  465.       // Set the probability of transforming to a missing value
  466.       switch ( m_MissingMode )
  467. {
  468. case M_DELETE:
  469.   m_MissingProb = 0.0;
  470.   break;
  471. case M_NORMAL:
  472.   m_MissingProb = 1.0;
  473.   break;
  474. case M_MAXDIFF:
  475.   m_MissingProb = m_SmallestProb;
  476.   break;
  477. case M_AVERAGE:
  478.   m_MissingProb = m_AverageProb;
  479.   break;
  480. }
  481.       // set scale factor
  482.       scale = bestroot;
  483.     } // else
  484.     return scale;
  485.   }
  486.   /**
  487.    * Calculates several parameters aside from the entropy: for a specified
  488.    * scale factor, calculates the actual entropy, a random entropy using a
  489.    * randomized set of class value colomns, and records the average and
  490.    * smallest probabilities (for use in missing value case).
  491.    */
  492.   private void calculateEntropy(double scale, KStarWrapper params) {    
  493.     String debug = "(KStarNumericAttribute.calculateEntropy)";
  494.     int i,j,k;
  495.     double actent = 0.0, randent = 0.0;
  496.     double pstar, tprob, avgprob = 0.0, minprob = 1.0;
  497.     double actClassProb, randClassProb;
  498.     double [][] pseudoClassProbs = new double[NUM_RAND_COLS+1][m_NumClasses];
  499.     // init
  500.     for(j = 0; j <= NUM_RAND_COLS; j++) {
  501.       for(i = 0; i < m_NumClasses; i++) {
  502. pseudoClassProbs[j][i] = 0.0;
  503.       }
  504.     }
  505.     for (i=0; i < m_NumInstances; i++) {
  506.       if (m_Distances[i] < 0) {
  507. // train instance has mising value
  508. continue;
  509.       }
  510.       else {
  511. pstar = PStar(m_Distances[i], scale);
  512. tprob = pstar / m_ActualCount;
  513. avgprob += tprob;
  514. if (pstar < minprob) {
  515.   minprob = pstar;
  516. }
  517. // filter instances with same class value
  518. for (k=0; k <= NUM_RAND_COLS; k++) {
  519.   // instance i is assigned a random class value in colomn k;
  520.   // colomn k = NUM_RAND_COLS contains the original mapping: 
  521.   // instance -> class vlaue
  522.   pseudoClassProbs[k][ m_RandClassCols[k][i] ] += tprob;
  523. }
  524.       }
  525.     }
  526.     // compute the actual entropy using the class probabilities
  527.     // with the original class value mapping (colomn NUM_RAND_COLS)
  528.     for (j = m_NumClasses-1; j >= 0; j--) {
  529.       actClassProb = pseudoClassProbs[NUM_RAND_COLS][j] / avgprob;
  530.       if (actClassProb > 0) {
  531.      actent -= actClassProb * Math.log(actClassProb) / LOG2;
  532.       }
  533.     }
  534.     // compute a random entropy using the pseudo class probs
  535.     // excluding the colomn NUM_RAND_COLS
  536.     for (k=0; k < NUM_RAND_COLS; k++) {
  537.       for (i = m_NumClasses-1; i >= 0; i--) {
  538.    randClassProb = pseudoClassProbs[k][i] / avgprob;
  539.    if (randClassProb > 0) {
  540.      randent -= randClassProb * Math.log(randClassProb) / LOG2;
  541. }
  542.       }
  543.     }
  544.     randent /= NUM_RAND_COLS;
  545.     // return the values
  546.     params.actEntropy = actent;
  547.     params.randEntropy = randent;
  548.     params.avgProb = avgprob;
  549.     params.minProb = minprob;
  550.   }
  551.   /**
  552.    * Calculates the value of P for a given value x using the expression:
  553.    * P(x) = scale * exp( -2.0 * x * scale )
  554.    *
  555.    * @param x input value
  556.    * @param scale the scale factor
  557.    * @return output of the function P(x)
  558.    */          
  559.   private double PStar(double x, double scale) {
  560.     return scale * Math.exp( -2.0 * x * scale );
  561.   }
  562.   /**
  563.    * Set options.
  564.    * @param missingmode the missing value treatment to use
  565.    * @param blendmethod the blending method to use
  566.    * @param blendfactor the level of blending to use
  567.    */
  568.   public void setOptions(int missingmode, int blendmethod, int blendfactor) {
  569.     m_MissingMode = missingmode;
  570.     m_BlendMethod = blendmethod;
  571.     m_BlendFactor = blendfactor;
  572.   }
  573.   /**
  574.    * Set the missing value mode.
  575.    * @param mode the type of missing value treatment to use
  576.    */
  577.   public void setMissingMode(int mode) {
  578.     m_MissingMode = mode;
  579.   }
  580.   /**
  581.    * Set the blending method
  582.    * @param method the blending method to use
  583.    */
  584.   public void setBlendMethod(int method) {
  585.     m_BlendMethod = method;
  586.   }
  587.   /**
  588.    * Set the blending factor
  589.    * @param factor the level of blending to use
  590.    */
  591.   public void setBlendFactor(int factor) {
  592.     m_BlendFactor = factor;
  593.   }
  594. } // class