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