SymmetricalUncertAttributeEval.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 10k
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.  *    SymmetricalUncertAttributeEval.java
  18.  *    Copyright (C) 1999 Mark Hall
  19.  *
  20.  */
  21. package  weka.attributeSelection;
  22. import  java.io.*;
  23. import  java.util.*;
  24. import  weka.core.*;
  25. import  weka.filters.*;
  26. /** 
  27.  * Class for Evaluating attributes individually by measuring symmetrical 
  28.  * uncertainty with respect to the class.
  29.  *
  30.  * Valid options are:<p>
  31.  *
  32.  * -M <br>
  33.  * Treat missing values as a seperate value. <br>
  34.  *
  35.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  36.  * @version $Revision: 1.11 $
  37.  */
  38. public class SymmetricalUncertAttributeEval
  39.   extends AttributeEvaluator
  40.   implements OptionHandler
  41. {
  42.   /** The training instances */
  43.   private Instances m_trainInstances;
  44.   /** The class index */
  45.   private int m_classIndex;
  46.   /** The number of attributes */
  47.   private int m_numAttribs;
  48.   /** The number of instances */
  49.   private int m_numInstances;
  50.   /** The number of classes */
  51.   private int m_numClasses;
  52.   /** Treat missing values as a seperate value */
  53.   private boolean m_missing_merge;
  54.   /**
  55.    * Returns a string describing this attribute evaluator
  56.    * @return a description of the evaluator suitable for
  57.    * displaying in the explorer/experimenter gui
  58.    */
  59.   public String globalInfo() {
  60.     return "GainRatioAttributeEval :nnEvaluates the worth of an attribute "
  61.       +"by measuring the symmetrical uncertainty with respect to the class. "
  62.       +"nn SymmU(Class, Attribute) = 2 * (H(Class) - H(Class | Attribute)) "
  63.       +"/ H(Class) + H(Attribute).n";
  64.   }
  65.   
  66.   /**
  67.    * Constructor
  68.    */
  69.   public SymmetricalUncertAttributeEval () {
  70.     resetOptions();
  71.   }
  72.   /**
  73.    * Returns an enumeration describing the available options
  74.    * @return an enumeration of all the available options
  75.    **/
  76.   public Enumeration listOptions () {
  77.     Vector newVector = new Vector(1);
  78.     newVector.addElement(new Option("ttreat missing values as a seperate " 
  79.     + "value.", "M", 0, "-M"));
  80.     return  newVector.elements();
  81.   }
  82.   /**
  83.    * Parses a given list of options.
  84.    *
  85.    * -M <br>
  86.    * Treat missing values as a seperate value. <p>
  87.    *
  88.    * @param options the list of options as an array of strings
  89.    * @exception Exception if an option is not supported
  90.    **/
  91.   public void setOptions (String[] options)
  92.     throws Exception
  93.   {
  94.     resetOptions();
  95.     setMissingMerge(!(Utils.getFlag('M', options)));
  96.   }
  97.   /**
  98.    * Returns the tip text for this property
  99.    * @return tip text for this property suitable for
  100.    * displaying in the explorer/experimenter gui
  101.    */
  102.   public String missingMergeTipText() {
  103.     return "Distribute counts for missing values. Counts are distributed "
  104.       +"across other values in proportion to their frequency. Otherwise, "
  105.       +"missing is treated as a separate value.";
  106.   }
  107.   /**
  108.    * distribute the counts for missing values across observed values
  109.    *
  110.    * @param b true=distribute missing values.
  111.    */
  112.   public void setMissingMerge (boolean b) {
  113.     m_missing_merge = b;
  114.   }
  115.   /**
  116.    * get whether missing values are being distributed or not
  117.    *
  118.    * @return true if missing values are being distributed.
  119.    */
  120.   public boolean getMissingMerge () {
  121.     return  m_missing_merge;
  122.   }
  123.   /**
  124.    * Gets the current settings of WrapperSubsetEval.
  125.    * @return an array of strings suitable for passing to setOptions()
  126.    */
  127.   public String[] getOptions () {
  128.     String[] options = new String[1];
  129.     int current = 0;
  130.     if (!getMissingMerge()) {
  131.       options[current++] = "-M";
  132.     }
  133.     while (current < options.length) {
  134.       options[current++] = "";
  135.     }
  136.     return  options;
  137.   }
  138.   /**
  139.    * Initializes a symmetrical uncertainty attribute evaluator. 
  140.    * Discretizes all attributes that are numeric.
  141.    *
  142.    * @param data set of instances serving as training data 
  143.    * @exception Exception if the evaluator has not been 
  144.    * generated successfully
  145.    */
  146.   public void buildEvaluator (Instances data)
  147.     throws Exception
  148.   {
  149.     if (data.checkForStringAttributes()) {
  150.       throw  new Exception("Can't handle string attributes!");
  151.     }
  152.     m_trainInstances = data;
  153.     m_classIndex = m_trainInstances.classIndex();
  154.     m_numAttribs = m_trainInstances.numAttributes();
  155.     m_numInstances = m_trainInstances.numInstances();
  156.     if (m_trainInstances.attribute(m_classIndex).isNumeric()) {
  157.       throw  new Exception("Class must be nominal!");
  158.     }
  159.     DiscretizeFilter disTransform = new DiscretizeFilter();
  160.     disTransform.setUseBetterEncoding(true);
  161.     disTransform.setInputFormat(m_trainInstances);
  162.     m_trainInstances = Filter.useFilter(m_trainInstances, disTransform);
  163.     m_numClasses = m_trainInstances.attribute(m_classIndex).numValues();
  164.   }
  165.   /**
  166.    * set options to default values
  167.    */
  168.   protected void resetOptions () {
  169.     m_trainInstances = null;
  170.     m_missing_merge = true;
  171.   }
  172.   /**
  173.    * evaluates an individual attribute by measuring the symmetrical
  174.    * uncertainty between it and the class.
  175.    *
  176.    * @param attribute the index of the attribute to be evaluated
  177.    * @exception Exception if the attribute could not be evaluated
  178.    */
  179.   public double evaluateAttribute (int attribute)
  180.     throws Exception
  181.   {
  182.     int i, j, ii, jj;
  183.     int nnj, nni, ni, nj;
  184.     double sum = 0.0;
  185.     ni = m_trainInstances.attribute(attribute).numValues() + 1;
  186.     nj = m_numClasses + 1;
  187.     double[] sumi, sumj;
  188.     Instance inst;
  189.     double temp = 0.0;
  190.     sumi = new double[ni];
  191.     sumj = new double[nj];
  192.     double[][] counts = new double[ni][nj];
  193.     sumi = new double[ni];
  194.     sumj = new double[nj];
  195.     for (i = 0; i < ni; i++) {
  196.       sumi[i] = 0.0;
  197.       for (j = 0; j < nj; j++) {
  198. sumj[j] = 0.0;
  199. counts[i][j] = 0.0;
  200.       }
  201.     }
  202.     // Fill the contingency table
  203.     for (i = 0; i < m_numInstances; i++) {
  204.       inst = m_trainInstances.instance(i);
  205.       if (inst.isMissing(attribute)) {
  206. ii = ni - 1;
  207.       }
  208.       else {
  209. ii = (int)inst.value(attribute);
  210.       }
  211.       if (inst.isMissing(m_classIndex)) {
  212. jj = nj - 1;
  213.       }
  214.       else {
  215. jj = (int)inst.value(m_classIndex);
  216.       }
  217.       counts[ii][jj]++;
  218.     }
  219.     // get the row totals
  220.     for (i = 0; i < ni; i++) {
  221.       sumi[i] = 0.0;
  222.       for (j = 0; j < nj; j++) {
  223. sumi[i] += counts[i][j];
  224. sum += counts[i][j];
  225.       }
  226.     }
  227.     // get the column totals
  228.     for (j = 0; j < nj; j++) {
  229.       sumj[j] = 0.0;
  230.       for (i = 0; i < ni; i++) {
  231. sumj[j] += counts[i][j];
  232.       }
  233.     }
  234.     // distribute missing counts
  235.     if (m_missing_merge && 
  236. (sumi[ni-1] < m_numInstances) && 
  237. (sumj[nj-1] < m_numInstances)) {
  238.       double[] i_copy = new double[sumi.length];
  239.       double[] j_copy = new double[sumj.length];
  240.       double[][] counts_copy = new double[sumi.length][sumj.length];
  241.       for (i = 0; i < ni; i++) {
  242. System.arraycopy(counts[i], 0, counts_copy[i], 0, sumj.length);
  243.       }
  244.       System.arraycopy(sumi, 0, i_copy, 0, sumi.length);
  245.       System.arraycopy(sumj, 0, j_copy, 0, sumj.length);
  246.       double total_missing = (sumi[ni - 1] + sumj[nj - 1] 
  247.       - counts[ni - 1][nj - 1]);
  248.       // do the missing i's
  249.       if (sumi[ni - 1] > 0.0) {
  250. for (j = 0; j < nj - 1; j++) {
  251.   if (counts[ni - 1][j] > 0.0) {
  252.     for (i = 0; i < ni - 1; i++) {
  253.       temp = ((i_copy[i]/(sum - i_copy[ni - 1])) * 
  254.       counts[ni - 1][j]);
  255.       counts[i][j] += temp;
  256.       sumi[i] += temp;
  257.     }
  258.     counts[ni - 1][j] = 0.0;
  259.   }
  260. }
  261.       }
  262.       sumi[ni - 1] = 0.0;
  263.       // do the missing j's
  264.       if (sumj[nj - 1] > 0.0) {
  265. for (i = 0; i < ni - 1; i++) {
  266.   if (counts[i][nj - 1] > 0.0) {
  267.     for (j = 0; j < nj - 1; j++) {
  268.       temp = ((j_copy[j]/(sum - j_copy[nj - 1]))*counts[i][nj - 1]);
  269.       counts[i][j] += temp;
  270.       sumj[j] += temp;
  271.     }
  272.     counts[i][nj - 1] = 0.0;
  273.   }
  274. }
  275.       }
  276.       sumj[nj - 1] = 0.0;
  277.       // do the both missing
  278.       if (counts[ni - 1][nj - 1] > 0.0 && total_missing != sum) {
  279. for (i = 0; i < ni - 1; i++) {
  280.   for (j = 0; j < nj - 1; j++) {
  281.     temp = (counts_copy[i][j]/(sum - total_missing)) * 
  282.       counts_copy[ni - 1][nj - 1];
  283.     counts[i][j] += temp;
  284.     sumi[i] += temp;
  285.     sumj[j] += temp;
  286.   }
  287. }
  288. counts[ni - 1][nj - 1] = 0.0;
  289.       }
  290.     }
  291.     return  ContingencyTables.symmetricalUncertainty(counts);
  292.   }
  293.   /**
  294.    * Return a description of the evaluator
  295.    * @return description as a string
  296.    */
  297.   public String toString () {
  298.     StringBuffer text = new StringBuffer();
  299.     if (m_trainInstances == null) {
  300.       text.append("tSymmetrical Uncertainty evaluator has not been built");
  301.     }
  302.     else {
  303.       text.append("tSymmetrical Uncertainty Ranking Filter");
  304.       if (!m_missing_merge) {
  305. text.append("ntMissing values treated as seperate");
  306.       }
  307.     }
  308.     text.append("n");
  309.     return  text.toString();
  310.   }
  311.   // ============
  312.   // Test method.
  313.   // ============
  314.   /**
  315.    * Main method for testing this class.
  316.    *
  317.    * @param argv should contain the following arguments:
  318.    * -t training file
  319.    */
  320.   public static void main (String[] argv) {
  321.     try {
  322.       System.out.println(AttributeSelection.
  323.  SelectAttributes(new SymmetricalUncertAttributeEval()
  324.   , argv));
  325.     }
  326.     catch (Exception e) {
  327.       e.printStackTrace();
  328.       System.out.println(e.getMessage());
  329.     }
  330.   }
  331. }