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