ChiSquaredAttributeEval.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 12k
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.  *    ChiSquaredAttributeEval.java
  18.  *    Copyright (C) 1999 Eibe Frank
  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.unsupervised.attribute.NumericToBinary;
  27. import  weka.filters.Filter;
  28. /** 
  29.  * Class for Evaluating attributes individually by measuring the
  30.  * chi-squared statistic with respect to the class. 
  31.  *
  32.  * Valid options are:<p>
  33.  *
  34.  * -M <br>
  35.  * Treat missing values as a seperate value. <br>
  36.  *
  37.  * -B <br>
  38.  * Just binarize numeric attributes instead of properly discretizing them. <br>
  39.  *
  40.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  41.  * @version $Revision: 1.8 $ 
  42.  */
  43. public class ChiSquaredAttributeEval
  44.   extends AttributeEvaluator
  45.   implements OptionHandler {
  46.   /** Treat missing values as a seperate value */
  47.   private boolean m_missing_merge;
  48.   /** Just binarize numeric attributes */
  49.   private boolean m_Binarize;
  50.   /** The chi-squared value for each attribute */
  51.   private double[] m_ChiSquareds;
  52.   /**
  53.    * Returns a string describing this attribute evaluator
  54.    * @return a description of the evaluator suitable for
  55.    * displaying in the explorer/experimenter gui
  56.    */
  57.   public String globalInfo() {
  58.     return "ChiSquaredAttributeEval :nnEvaluates the worth of an attribute "
  59.       +"by computing the value of the chi-squared statistic with respect to the class.n";
  60.   }
  61.   /**
  62.    * Constructor
  63.    */
  64.   public ChiSquaredAttributeEval () {
  65.     resetOptions();
  66.   }
  67.   /**
  68.    * Returns an enumeration describing the available options
  69.    * @return an enumeration of all the available options
  70.    **/
  71.   public Enumeration listOptions () {
  72.     Vector newVector = new Vector(2);
  73.     newVector.addElement(new Option("ttreat missing values as a seperate " 
  74.     + "value.", "M", 0, "-M"));
  75.     newVector.addElement(new Option("tjust binarize numeric attributes insteadn " 
  76.     +"tof properly discretizing them.", "B", 0, 
  77.     "-B"));
  78.     return  newVector.elements();
  79.   }
  80.   /**
  81.    * Parses a given list of options. <p>
  82.    *
  83.    * Valid options are:<p>
  84.    *
  85.    * -M <br>
  86.    * Treat missing values as a seperate value. <br>
  87.    *
  88.    * -B <br>
  89.    * Just binarize numeric attributes instead of properly discretizing them. <br>
  90.    *
  91.    * @param options the list of options as an array of strings
  92.    * @exception Exception if an option is not supported
  93.    *
  94.    **/
  95.   public void setOptions (String[] options)
  96.     throws Exception {
  97.     resetOptions();
  98.     setMissingMerge(!(Utils.getFlag('M', options)));
  99.     setBinarizeNumericAttributes(Utils.getFlag('B', options));
  100.   }
  101.   /**
  102.    * Gets the current settings of WrapperSubsetEval.
  103.    *
  104.    * @return an array of strings suitable for passing to setOptions()
  105.    */
  106.   public String[] getOptions () {
  107.     String[] options = new String[2];
  108.     int current = 0;
  109.     if (!getMissingMerge()) {
  110.       options[current++] = "-M";
  111.     }
  112.     if (getBinarizeNumericAttributes()) {
  113.       options[current++] = "-B";
  114.     }
  115.     while (current < options.length) {
  116.       options[current++] = "";
  117.     }
  118.     return  options;
  119.   }
  120.   /**
  121.    * Returns the tip text for this property
  122.    * @return tip text for this property suitable for
  123.    * displaying in the explorer/experimenter gui
  124.    */
  125.   public String binarizeNumericAttributesTipText() {
  126.     return "Just binarize numeric attributes instead of properly discretizing them.";
  127.   }
  128.   /**
  129.    * Binarize numeric attributes.
  130.    *
  131.    * @param b true=binarize numeric attributes
  132.    */
  133.   public void setBinarizeNumericAttributes (boolean b) {
  134.     m_Binarize = b;
  135.   }
  136.   /**
  137.    * get whether numeric attributes are just being binarized.
  138.    *
  139.    * @return true if missing values are being distributed.
  140.    */
  141.   public boolean getBinarizeNumericAttributes () {
  142.     return  m_Binarize;
  143.   }
  144.   /**
  145.    * Returns the tip text for this property
  146.    * @return tip text for this property suitable for
  147.    * displaying in the explorer/experimenter gui
  148.    */
  149.   public String missingMergeTipText() {
  150.     return "Distribute counts for missing values. Counts are distributed "
  151.       +"across other values in proportion to their frequency. Otherwise, "
  152.       +"missing is treated as a separate value.";
  153.   }
  154.   /**
  155.    * distribute the counts for missing values across observed values
  156.    *
  157.    * @param b true=distribute missing values.
  158.    */
  159.   public void setMissingMerge (boolean b) {
  160.     m_missing_merge = b;
  161.   }
  162.   /**
  163.    * get whether missing values are being distributed or not
  164.    *
  165.    * @return true if missing values are being distributed.
  166.    */
  167.   public boolean getMissingMerge () {
  168.     return  m_missing_merge;
  169.   }
  170.   /**
  171.    * Initializes a chi-squared attribute evaluator.
  172.    * Discretizes all attributes that are numeric.
  173.    *
  174.    * @param data set of instances serving as training data 
  175.    * @exception Exception if the evaluator has not been 
  176.    * generated successfully
  177.    */
  178.   public void buildEvaluator (Instances data)
  179.     throws Exception {
  180.     
  181.     if (data.checkForStringAttributes()) {
  182.       throw  new UnsupportedAttributeTypeException("Can't handle string attributes!");
  183.     }
  184.     
  185.     int classIndex = data.classIndex();
  186.     if (data.attribute(classIndex).isNumeric()) {
  187.       throw  new Exception("Class must be nominal!");
  188.     }
  189.     int numInstances = data.numInstances();
  190.     
  191.     if (!m_Binarize) {
  192.       Discretize disTransform = new Discretize();
  193.       disTransform.setUseBetterEncoding(true);
  194.       disTransform.setInputFormat(data);
  195.       data = Filter.useFilter(data, disTransform);
  196.     } else {
  197.       NumericToBinary binTransform = new NumericToBinary();
  198.       binTransform.setInputFormat(data);
  199.       data = Filter.useFilter(data, binTransform);
  200.     }      
  201.     int numClasses = data.attribute(classIndex).numValues();
  202.     // Reserve space and initialize counters
  203.     double[][][] counts = new double[data.numAttributes()][][];
  204.     for (int k = 0; k < data.numAttributes(); k++) {
  205.       if (k != classIndex) {
  206. int numValues = data.attribute(k).numValues();
  207. counts[k] = new double[numValues + 1][numClasses + 1];
  208.       }
  209.     }
  210.     // Initialize counters
  211.     double[] temp = new double[numClasses + 1];
  212.     for (int k = 0; k < numInstances; k++) {
  213.       Instance inst = data.instance(k);
  214.       if (inst.classIsMissing()) {
  215. temp[numClasses] += inst.weight();
  216.       } else {
  217. temp[(int)inst.classValue()] += inst.weight();
  218.       }
  219.     }
  220.     for (int k = 0; k < counts.length; k++) {
  221.       if (k != classIndex) {
  222. for (int i = 0; i < temp.length; i++) {
  223.   counts[k][0][i] = temp[i];
  224. }
  225.       }
  226.     }
  227.     // Get counts
  228.     for (int k = 0; k < numInstances; k++) {
  229.       Instance inst = data.instance(k);
  230.       for (int i = 0; i < inst.numValues(); i++) {
  231. if (inst.index(i) != classIndex) {
  232.   if (inst.isMissingSparse(i) || inst.classIsMissing()) {
  233.     if (!inst.isMissingSparse(i)) {
  234.       counts[inst.index(i)][(int)inst.valueSparse(i)][numClasses] += 
  235. inst.weight();
  236.       counts[inst.index(i)][0][numClasses] -= inst.weight();
  237.     } else if (!inst.classIsMissing()) {
  238.       counts[inst.index(i)][data.attribute(inst.index(i)).numValues()]
  239. [(int)inst.classValue()] += inst.weight();
  240.       counts[inst.index(i)][0][(int)inst.classValue()] -= 
  241. inst.weight();
  242.     } else {
  243.       counts[inst.index(i)][data.attribute(inst.index(i)).numValues()]
  244. [numClasses] += inst.weight();
  245.       counts[inst.index(i)][0][numClasses] -= inst.weight();
  246.     }
  247.   } else {
  248.     counts[inst.index(i)][(int)inst.valueSparse(i)]
  249.       [(int)inst.classValue()] += inst.weight();
  250.     counts[inst.index(i)][0][(int)inst.classValue()] -= inst.weight();
  251.   }
  252. }
  253.       }
  254.     }
  255.     // distribute missing counts if required
  256.     if (m_missing_merge) {
  257.       
  258.       for (int k = 0; k < data.numAttributes(); k++) {
  259. if (k != classIndex) {
  260.   int numValues = data.attribute(k).numValues();
  261.   // Compute marginals
  262.   double[] rowSums = new double[numValues];
  263.   double[] columnSums = new double[numClasses];
  264.   double sum = 0;
  265.   for (int i = 0; i < numValues; i++) {
  266.     for (int j = 0; j < numClasses; j++) {
  267.       rowSums[i] += counts[k][i][j];
  268.       columnSums[j] += counts[k][i][j];
  269.     }
  270.     sum += rowSums[i];
  271.   }
  272.   if (Utils.gr(sum, 0)) {
  273.     double[][] additions = new double[numValues][numClasses];
  274.     // Compute what needs to be added to each row
  275.     for (int i = 0; i < numValues; i++) {
  276.       for (int j = 0; j  < numClasses; j++) {
  277. additions[i][j] = (rowSums[i] / sum) * counts[k][numValues][j];
  278.       }
  279.     }
  280.     
  281.     // Compute what needs to be added to each column
  282.     for (int i = 0; i < numClasses; i++) {
  283.       for (int j = 0; j  < numValues; j++) {
  284. additions[j][i] += (columnSums[i] / sum) * 
  285.   counts[k][j][numClasses];
  286.       }
  287.     }
  288.     
  289.     // Compute what needs to be added to each cell
  290.     for (int i = 0; i < numClasses; i++) {
  291.       for (int j = 0; j  < numValues; j++) {
  292. additions[j][i] += (counts[k][j][i] / sum) * 
  293.   counts[k][numValues][numClasses];
  294.       }
  295.     }
  296.     
  297.     // Make new contingency table
  298.     double[][] newTable = new double[numValues][numClasses];
  299.     for (int i = 0; i < numValues; i++) {
  300.       for (int j = 0; j < numClasses; j++) {
  301. newTable[i][j] = counts[k][i][j] + additions[i][j];
  302.       }
  303.     }
  304.     counts[k] = newTable;
  305.   }
  306. }
  307.       }
  308.     }
  309.     // Compute chi-squared values
  310.     m_ChiSquareds = new double[data.numAttributes()];
  311.     for (int i = 0; i < data.numAttributes(); i++) {
  312.       if (i != classIndex) {
  313. m_ChiSquareds[i] = ContingencyTables.
  314.   chiVal(ContingencyTables.reduceMatrix(counts[i]), false); 
  315.       }
  316.     }
  317.   }
  318.   /**
  319.    * Reset options to their default values
  320.    */
  321.   protected void resetOptions () {
  322.     m_ChiSquareds = null;
  323.     m_missing_merge = true;
  324.     m_Binarize = false;
  325.   }
  326.   /**
  327.    * evaluates an individual attribute by measuring its
  328.    * chi-squared value.
  329.    *
  330.    * @param attribute the index of the attribute to be evaluated
  331.    * @exception Exception if the attribute could not be evaluated
  332.    */
  333.   public double evaluateAttribute (int attribute)
  334.     throws Exception {
  335.     return m_ChiSquareds[attribute];
  336.   }
  337.   /**
  338.    * Describe the attribute evaluator
  339.    * @return a description of the attribute evaluator as a string
  340.    */
  341.   public String toString () {
  342.     StringBuffer text = new StringBuffer();
  343.     if (m_ChiSquareds == null) {
  344.       text.append("Chi-squared attribute evaluator has not been built");
  345.     }
  346.     else {
  347.       text.append("tChi-squared Ranking Filter");
  348.       if (!m_missing_merge) {
  349. text.append("ntMissing values treated as seperate");
  350.       }
  351.       if (m_Binarize) {
  352. text.append("ntNumeric attributes are just binarized");
  353.       }
  354.     }
  355.     
  356.     text.append("n");
  357.     return  text.toString();
  358.   }
  359.   
  360.   // ============
  361.   // Test method.
  362.   // ============
  363.   /**
  364.    * Main method for testing this class.
  365.    *
  366.    * @param argv the options
  367.    */
  368.   public static void main (String[] args) {
  369.     try {
  370.       System.out.println(AttributeSelection.
  371.  SelectAttributes(new ChiSquaredAttributeEval(), args));
  372.     }
  373.     catch (Exception e) {
  374.       e.printStackTrace();
  375.       System.out.println(e.getMessage());
  376.     }
  377.   }
  378. }