CfsSubsetEval.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 25k
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.  *    CfsSubsetEval.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.classifiers.*;
  26. import  weka.filters.supervised.attribute.Discretize;
  27. import  weka.filters.Filter;
  28. /** 
  29.  * CFS attribute subset evaluator.
  30.  * For more information see: <p>
  31.  *
  32.  * Hall, M. A. (1998). Correlation-based Feature Subset Selection for Machine 
  33.  * Learning. Thesis submitted in partial fulfilment of the requirements of the
  34.  * degree of Doctor of Philosophy at the University of Waikato. <p>
  35.  *
  36.  * Valid options are:
  37.  *
  38.  * -M <br>
  39.  * Treat missing values as a seperate value. <p>
  40.  * 
  41.  * -L <br>
  42.  * Include locally predictive attributes. <p>
  43.  *
  44.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  45.  * @version $Revision: 1.17 $
  46.  */
  47. public class CfsSubsetEval
  48.   extends SubsetEvaluator
  49.   implements OptionHandler
  50. {
  51.   /** The training instances */
  52.   private Instances m_trainInstances;
  53.   /** Discretise attributes when class in nominal */
  54.   private Discretize m_disTransform;
  55.   /** The class index */
  56.   private int m_classIndex;
  57.   /** Is the class numeric */
  58.   private boolean m_isNumeric;
  59.   /** Number of attributes in the training data */
  60.   private int m_numAttribs;
  61.   /** Number of instances in the training data */
  62.   private int m_numInstances;
  63.   /** Treat missing values as seperate values */
  64.   private boolean m_missingSeperate;
  65.   /** Include locally predicitive attributes */
  66.   private boolean m_locallyPredictive;
  67.   /** Holds the matrix of attribute correlations */
  68.   private Matrix m_corr_matrix;
  69.   /** Standard deviations of attributes (when using pearsons correlation) */
  70.   private double[] m_std_devs;
  71.   /** Threshold for admitting locally predictive features */
  72.   private double m_c_Threshold;
  73.   /**
  74.    * Returns a string describing this attribute evaluator
  75.    * @return a description of the evaluator suitable for
  76.    * displaying in the explorer/experimenter gui
  77.    */
  78.   public String globalInfo() {
  79.     return "CfsSubsetEval :nnEvaluates the worth of a subset of attributes "
  80.       +"by considering the individual predictive ability of each feature "
  81.       +"along with the degree of redundancy between them.nn"
  82.       +"Subsets of features that are highly correlated with the class "
  83.       +"while having low intercorrelation are preferred.n";
  84.   }
  85.   /**
  86.    * Constructor
  87.    */
  88.   public CfsSubsetEval () {
  89.     resetOptions();
  90.   }
  91.   /**
  92.    * Returns an enumeration describing the available options.
  93.    * @return an enumeration of all the available options.
  94.    *
  95.    **/
  96.   public Enumeration listOptions () {
  97.     Vector newVector = new Vector(3);
  98.     newVector.addElement(new Option("tTreat missing values as a seperate" 
  99.     + "ntvalue.", "M", 0, "-M"));
  100.     newVector.addElement(new Option("tInclude locally predictive attributes" 
  101.     + ".", "L", 0, "-L"));
  102.     return  newVector.elements();
  103.   }
  104.   /**
  105.    * Parses and sets a given list of options. <p>
  106.    *
  107.    * Valid options are:
  108.    *
  109.    * -M <br>
  110.    * Treat missing values as a seperate value. <p>
  111.    * 
  112.    * -L <br>
  113.    * Include locally predictive attributes. <p>
  114.    *
  115.    * @param options the list of options as an array of strings
  116.    * @exception Exception if an option is not supported
  117.    *
  118.    **/
  119.   public void setOptions (String[] options)
  120.     throws Exception
  121.   {
  122.     String optionString;
  123.     resetOptions();
  124.     setMissingSeperate(Utils.getFlag('M', options));
  125.     setLocallyPredictive(Utils.getFlag('L', options));
  126.   }
  127.   /**
  128.    * Returns the tip text for this property
  129.    * @return tip text for this property suitable for
  130.    * displaying in the explorer/experimenter gui
  131.    */
  132.   public String locallyPredictiveTipText() {
  133.     return "Identify locally predictive attributes. Iteratively adds "
  134.       +"attributes with the highest correlation with the class as long "
  135.       +"as there is not already an attribute in the subset that has a "
  136.       +"higher correlation with the attribute in question";
  137.   }
  138.   /**
  139.    * Include locally predictive attributes
  140.    *
  141.    * @param b true or false
  142.    */
  143.   public void setLocallyPredictive (boolean b) {
  144.     m_locallyPredictive = b;
  145.   }
  146.   /**
  147.    * Return true if including locally predictive attributes
  148.    *
  149.    * @return true if locally predictive attributes are to be used
  150.    */
  151.   public boolean getLocallyPredictive () {
  152.     return  m_locallyPredictive;
  153.   }
  154.   /**
  155.    * Returns the tip text for this property
  156.    * @return tip text for this property suitable for
  157.    * displaying in the explorer/experimenter gui
  158.    */
  159.   public String missingSeperateTipText() {
  160.     return "Treat missing as a separate value. Otherwise, counts for missing "
  161.       +"values are distributed across other values in proportion to their "
  162.       +"frequency.";
  163.   }
  164.   /**
  165.    * Treat missing as a seperate value
  166.    *
  167.    * @param b true or false
  168.    */
  169.   public void setMissingSeperate (boolean b) {
  170.     m_missingSeperate = b;
  171.   }
  172.   /**
  173.    * Return true is missing is treated as a seperate value
  174.    *
  175.    * @return true if missing is to be treated as a seperate value
  176.    */
  177.   public boolean getMissingSeperate () {
  178.     return  m_missingSeperate;
  179.   }
  180.   /**
  181.    * Gets the current settings of CfsSubsetEval
  182.    *
  183.    * @return an array of strings suitable for passing to setOptions()
  184.    */
  185.   public String[] getOptions () {
  186.     String[] options = new String[2];
  187.     int current = 0;
  188.     if (getMissingSeperate()) {
  189.       options[current++] = "-M";
  190.     }
  191.     if (getLocallyPredictive()) {
  192.       options[current++] = "-L";
  193.     }
  194.     while (current < options.length) {
  195.       options[current++] = "";
  196.     }
  197.     return  options;
  198.   }
  199.   /**
  200.    * Generates a attribute evaluator. Has to initialize all fields of the 
  201.    * evaluator that are not being set via options.
  202.    *
  203.    * CFS also discretises attributes (if necessary) and initializes
  204.    * the correlation matrix.
  205.    *
  206.    * @param data set of instances serving as training data 
  207.    * @exception Exception if the evaluator has not been 
  208.    * generated successfully
  209.    */
  210.   public void buildEvaluator (Instances data)
  211.     throws Exception
  212.   {
  213.     if (data.checkForStringAttributes()) {
  214.       throw  new UnsupportedAttributeTypeException("Can't handle string attributes!");
  215.     }
  216.     m_trainInstances = data;
  217.     m_trainInstances.deleteWithMissingClass();
  218.     m_classIndex = m_trainInstances.classIndex();
  219.     m_numAttribs = m_trainInstances.numAttributes();
  220.     m_numInstances = m_trainInstances.numInstances();
  221.     m_isNumeric = m_trainInstances.attribute(m_classIndex).isNumeric();
  222.     if (!m_isNumeric) {
  223.       m_disTransform = new Discretize();
  224.       m_disTransform.setUseBetterEncoding(true);
  225.       m_disTransform.setInputFormat(m_trainInstances);
  226.       m_trainInstances = Filter.useFilter(m_trainInstances, m_disTransform);
  227.     }
  228.     m_std_devs = new double[m_numAttribs];
  229.     m_corr_matrix = new Matrix(m_numAttribs, m_numAttribs);
  230.     for (int i = 0; i < m_corr_matrix.numRows(); i++) {
  231.       m_corr_matrix.setElement(i, i, 1.0);
  232.       m_std_devs[i] = 1.0;
  233.     }
  234.     for (int i = 0; i < m_numAttribs; i++) {
  235.       for (int j = i + 1; j < m_numAttribs; j++) {
  236.         m_corr_matrix.setElement(i, j, -999);
  237.         m_corr_matrix.setElement(j, i, -999);
  238.       }
  239.     }
  240.   }
  241.   /**
  242.    * evaluates a subset of attributes
  243.    *
  244.    * @param subset a bitset representing the attribute subset to be 
  245.    * evaluated 
  246.    * @exception Exception if the subset could not be evaluated
  247.    */
  248.   public double evaluateSubset (BitSet subset)
  249.     throws Exception
  250.   {
  251.     double num = 0.0;
  252.     double denom = 0.0;
  253.     double corr;
  254.     // do numerator
  255.     for (int i = 0; i < m_numAttribs; i++) {
  256.       if (i != m_classIndex) {
  257.         if (subset.get(i)) {
  258.           if (m_corr_matrix.getElement(i, m_classIndex) == -999) {
  259.             corr = correlate(i, m_classIndex);
  260.             m_corr_matrix.setElement(i, m_classIndex, corr);
  261.             m_corr_matrix.setElement(m_classIndex, i, corr);
  262.             num += (m_std_devs[i] * corr);
  263.           }
  264.           else {num += (m_std_devs[i] * 
  265. m_corr_matrix.getElement(i, m_classIndex));
  266.   }
  267. }
  268.       }
  269.     }
  270.     // do denominator
  271.     for (int i = 0; i < m_numAttribs; i++) {
  272.       if (i != m_classIndex) {
  273. if (subset.get(i)) {
  274.   denom += (1.0 * m_std_devs[i] * m_std_devs[i]);
  275.   for (int j = i + 1; j < m_numAttribs; j++) {if (subset.get(j)) {
  276.     if (m_corr_matrix.getElement(i, j) == -999) {
  277.       corr = correlate(i, j);
  278.       m_corr_matrix.setElement(i, j, corr);
  279.       m_corr_matrix.setElement(j, i, corr);
  280.       denom += (2.0 * m_std_devs[i] * m_std_devs[j] * corr);
  281.     }
  282.     else {denom += (2.0 * m_std_devs[i] * m_std_devs[j] * 
  283.     m_corr_matrix.getElement(i, j));
  284.     }
  285.   }
  286.   }
  287. }
  288.       }
  289.     }
  290.     if (denom < 0.0) {
  291.       denom *= -1.0;
  292.     }
  293.     if (denom == 0.0) {
  294.       return  (0.0);
  295.     }
  296.     double merit = (num/Math.sqrt(denom));
  297.     if (merit < 0.0) {
  298.       merit *= -1.0;
  299.     }
  300.     return  merit;
  301.   }
  302.   private double correlate (int att1, int att2) {
  303.     if (!m_isNumeric) {
  304.       return  symmUncertCorr(att1, att2);
  305.     }
  306.     boolean att1_is_num = (m_trainInstances.attribute(att1).isNumeric());
  307.     boolean att2_is_num = (m_trainInstances.attribute(att2).isNumeric());
  308.     if (att1_is_num && att2_is_num) {
  309.       return  num_num(att1, att2);
  310.     }
  311.     else {if (att2_is_num) {
  312.       return  num_nom2(att1, att2);
  313.     }
  314.     else {if (att1_is_num) {
  315.       return  num_nom2(att2, att1);
  316.     }
  317.     }
  318.     }
  319.     return  nom_nom(att1, att2);
  320.   }
  321.   private double symmUncertCorr (int att1, int att2) {
  322.     int i, j, k, ii, jj;
  323.     int nnj, nni, ni, nj;
  324.     double sum = 0.0;
  325.     double sumi[], sumj[];
  326.     double counts[][];
  327.     Instance inst;
  328.     double corr_measure;
  329.     boolean flag = false;
  330.     double temp = 0.0;
  331.     if (att1 == m_classIndex || att2 == m_classIndex) {
  332.       flag = true;
  333.     }
  334.     ni = m_trainInstances.attribute(att1).numValues() + 1;
  335.     nj = m_trainInstances.attribute(att2).numValues() + 1;
  336.     counts = new double[ni][nj];
  337.     sumi = new double[ni];
  338.     sumj = new double[nj];
  339.     for (i = 0; i < ni; i++) {
  340.       sumi[i] = 0.0;
  341.       for (j = 0; j < nj; j++) {
  342. sumj[j] = 0.0;
  343. counts[i][j] = 0.0;
  344.       }
  345.     }
  346.     // Fill the contingency table
  347.     for (i = 0; i < m_numInstances; i++) {
  348.       inst = m_trainInstances.instance(i);
  349.       if (inst.isMissing(att1)) {
  350. ii = ni - 1;
  351.       }
  352.       else {
  353. ii = (int)inst.value(att1);
  354.       }
  355.       if (inst.isMissing(att2)) {
  356. jj = nj - 1;
  357.       }
  358.       else {
  359. jj = (int)inst.value(att2);
  360.       }
  361.       counts[ii][jj]++;
  362.     }
  363.     // get the row totals
  364.     for (i = 0; i < ni; i++) {
  365.       sumi[i] = 0.0;
  366.       for (j = 0; j < nj; j++) {
  367. sumi[i] += counts[i][j];
  368. sum += counts[i][j];
  369.       }
  370.     }
  371.     // get the column totals
  372.     for (j = 0; j < nj; j++) {
  373.       sumj[j] = 0.0;
  374.       for (i = 0; i < ni; i++) {
  375. sumj[j] += counts[i][j];
  376.       }
  377.     }
  378.     // distribute missing counts
  379.     if (!m_missingSeperate && 
  380. (sumi[ni-1] < m_numInstances) && 
  381. (sumj[nj-1] < m_numInstances)) {
  382.       double[] i_copy = new double[sumi.length];
  383.       double[] j_copy = new double[sumj.length];
  384.       double[][] counts_copy = new double[sumi.length][sumj.length];
  385.       for (i = 0; i < ni; i++) {
  386. System.arraycopy(counts[i], 0, counts_copy[i], 0, sumj.length);
  387.       }
  388.       System.arraycopy(sumi, 0, i_copy, 0, sumi.length);
  389.       System.arraycopy(sumj, 0, j_copy, 0, sumj.length);
  390.       double total_missing = 
  391. (sumi[ni - 1] + sumj[nj - 1] - counts[ni - 1][nj - 1]);
  392.       // do the missing i's
  393.       if (sumi[ni - 1] > 0.0) {
  394. for (j = 0; j < nj - 1; j++) {
  395.   if (counts[ni - 1][j] > 0.0) {
  396.     for (i = 0; i < ni - 1; i++) {
  397.       temp = ((i_copy[i]/(sum - i_copy[ni - 1]))*counts[ni - 1][j]);
  398.       counts[i][j] += temp;
  399.       sumi[i] += temp;
  400.     }
  401.     counts[ni - 1][j] = 0.0;
  402.   }
  403. }
  404.       }
  405.       sumi[ni - 1] = 0.0;
  406.       // do the missing j's
  407.       if (sumj[nj - 1] > 0.0) {
  408. for (i = 0; i < ni - 1; i++) {
  409.   if (counts[i][nj - 1] > 0.0) {
  410.     for (j = 0; j < nj - 1; j++) {
  411.       temp = ((j_copy[j]/(sum - j_copy[nj - 1]))*counts[i][nj - 1]);
  412.       counts[i][j] += temp;
  413.       sumj[j] += temp;
  414.     }
  415.     counts[i][nj - 1] = 0.0;
  416.   }
  417. }
  418.       }
  419.       sumj[nj - 1] = 0.0;
  420.       // do the both missing
  421.       if (counts[ni - 1][nj - 1] > 0.0 && total_missing != sum) {
  422. for (i = 0; i < ni - 1; i++) {
  423.   for (j = 0; j < nj - 1; j++) {
  424.     temp = (counts_copy[i][j]/(sum - total_missing)) * 
  425.       counts_copy[ni - 1][nj - 1];
  426.     
  427.     counts[i][j] += temp;
  428.     sumi[i] += temp;
  429.     sumj[j] += temp;
  430.   }
  431. }
  432. counts[ni - 1][nj - 1] = 0.0;
  433.       }
  434.     }
  435.     // corr_measure = Correlate.symm_uncert(counts,sumi,sumj,sum,ni,nj,flag);
  436.     corr_measure = ContingencyTables.symmetricalUncertainty(counts);
  437.     // corr_measure = ContingencyTables.gainRatio(counts);
  438.     if (Utils.eq(corr_measure, 0.0)) {
  439.       if (flag == true) {
  440. return  (0.0);
  441.       }
  442.       else {
  443. return  (1.0);
  444.       }
  445.     }
  446.     else {
  447.       return  (corr_measure);
  448.     }
  449.   }
  450.   private double num_num (int att1, int att2) {
  451.     int i;
  452.     Instance inst;
  453.     double r, diff1, diff2, num = 0.0, sx = 0.0, sy = 0.0;
  454.     double mx = m_trainInstances.meanOrMode(m_trainInstances.attribute(att1));
  455.     double my = m_trainInstances.meanOrMode(m_trainInstances.attribute(att2));
  456.     for (i = 0; i < m_numInstances; i++) {
  457.       inst = m_trainInstances.instance(i);
  458.       diff1 = (inst.isMissing(att1))? 0.0 : (inst.value(att1) - mx);
  459.       diff2 = (inst.isMissing(att2))? 0.0 : (inst.value(att2) - my);
  460.       num += (diff1*diff2);
  461.       sx += (diff1*diff1);
  462.       sy += (diff2*diff2);
  463.     }
  464.     if (sx != 0.0) {
  465.       if (m_std_devs[att1] == 1.0) {
  466. m_std_devs[att1] = Math.sqrt((sx/m_numInstances));
  467.       }
  468.     }
  469.     if (sy != 0.0) {
  470.       if (m_std_devs[att2] == 1.0) {
  471. m_std_devs[att2] = Math.sqrt((sy/m_numInstances));
  472.       }
  473.     }
  474.     if ((sx*sy) > 0.0) {
  475.       r = (num/(Math.sqrt(sx*sy)));
  476.       return  ((r < 0.0)? -r : r);
  477.     }
  478.     else {
  479.       if (att1 != m_classIndex && att2 != m_classIndex) {
  480. return  1.0;
  481.       }
  482.       else {
  483. return  0.0;
  484.       }
  485.     }
  486.   }
  487.   private double num_nom2 (int att1, int att2) {
  488.     int i, ii, k;
  489.     double temp;
  490.     Instance inst;
  491.     int mx = (int)m_trainInstances.
  492.       meanOrMode(m_trainInstances.attribute(att1));
  493.     double my = m_trainInstances.
  494.       meanOrMode(m_trainInstances.attribute(att2));
  495.     double stdv_num = 0.0;
  496.     double diff1, diff2;
  497.     double r = 0.0, rr, max_corr = 0.0;
  498.     int nx = (!m_missingSeperate) 
  499.       ? m_trainInstances.attribute(att1).numValues() 
  500.       : m_trainInstances.attribute(att1).numValues() + 1;
  501.     double[] prior_nom = new double[nx];
  502.     double[] stdvs_nom = new double[nx];
  503.     double[] covs = new double[nx];
  504.     for (i = 0; i < nx; i++) {
  505.       stdvs_nom[i] = covs[i] = prior_nom[i] = 0.0;
  506.     }
  507.     // calculate frequencies (and means) of the values of the nominal 
  508.     // attribute
  509.     for (i = 0; i < m_numInstances; i++) {
  510.       inst = m_trainInstances.instance(i);
  511.       if (inst.isMissing(att1)) {
  512. if (!m_missingSeperate) {
  513.   ii = mx;
  514. }
  515. else {
  516.   ii = nx - 1;
  517. }
  518.       }
  519.       else {
  520. ii = (int)inst.value(att1);
  521.       }
  522.       // increment freq for nominal
  523.       prior_nom[ii]++;
  524.     }
  525.     for (k = 0; k < m_numInstances; k++) {
  526.       inst = m_trainInstances.instance(k);
  527.       // std dev of numeric attribute
  528.       diff2 = (inst.isMissing(att2))? 0.0 : (inst.value(att2) - my);
  529.       stdv_num += (diff2*diff2);
  530.       // 
  531.       for (i = 0; i < nx; i++) {
  532. if (inst.isMissing(att1)) {
  533.   if (!m_missingSeperate) {
  534.     temp = (i == mx)? 1.0 : 0.0;
  535.   }
  536.   else {
  537.     temp = (i == (nx - 1))? 1.0 : 0.0;
  538.   }
  539. }
  540. else {
  541.   temp = (i == inst.value(att1))? 1.0 : 0.0;
  542. }
  543. diff1 = (temp - (prior_nom[i]/m_numInstances));
  544. stdvs_nom[i] += (diff1*diff1);
  545. covs[i] += (diff1*diff2);
  546.       }
  547.     }
  548.     // calculate weighted correlation
  549.     for (i = 0, temp = 0.0; i < nx; i++) {
  550.       // calculate the weighted variance of the nominal
  551.       temp += ((prior_nom[i]/m_numInstances)*(stdvs_nom[i]/m_numInstances));
  552.       if ((stdvs_nom[i]*stdv_num) > 0.0) {
  553. //System.out.println("Stdv :"+stdvs_nom[i]);
  554. rr = (covs[i]/(Math.sqrt(stdvs_nom[i]*stdv_num)));
  555. if (rr < 0.0) {
  556.   rr = -rr;
  557. }
  558. r += ((prior_nom[i]/m_numInstances)*rr);
  559.       }
  560.       /* if there is zero variance for the numeric att at a specific 
  561.  level of the catergorical att then if neither is the class then 
  562.  make this correlation at this level maximally bad i.e. 1.0. 
  563.  If either is the class then maximally bad correlation is 0.0 */
  564.       else {if (att1 != m_classIndex && att2 != m_classIndex) {
  565. r += ((prior_nom[i]/m_numInstances)*1.0);
  566.       }
  567.       }
  568.     }
  569.     // set the standard deviations for these attributes if necessary
  570.     // if ((att1 != classIndex) && (att2 != classIndex)) // =============
  571.     if (temp != 0.0) {
  572.       if (m_std_devs[att1] == 1.0) {
  573. m_std_devs[att1] = Math.sqrt(temp);
  574.       }
  575.     }
  576.     if (stdv_num != 0.0) {
  577.       if (m_std_devs[att2] == 1.0) {
  578. m_std_devs[att2] = Math.sqrt((stdv_num/m_numInstances));
  579.       }
  580.     }
  581.     if (r == 0.0) {
  582.       if (att1 != m_classIndex && att2 != m_classIndex) {
  583. r = 1.0;
  584.       }
  585.     }
  586.     return  r;
  587.   }
  588.   private double nom_nom (int att1, int att2) {
  589.     int i, j, ii, jj, z;
  590.     double temp1, temp2;
  591.     Instance inst;
  592.     int mx = (int)m_trainInstances.
  593.       meanOrMode(m_trainInstances.attribute(att1));
  594.     int my = (int)m_trainInstances.
  595.       meanOrMode(m_trainInstances.attribute(att2));
  596.     double diff1, diff2;
  597.     double r = 0.0, rr, max_corr = 0.0;
  598.     int nx = (!m_missingSeperate) 
  599.       ? m_trainInstances.attribute(att1).numValues() 
  600.       : m_trainInstances.attribute(att1).numValues() + 1;
  601.     int ny = (!m_missingSeperate)
  602.       ? m_trainInstances.attribute(att2).numValues() 
  603.       : m_trainInstances.attribute(att2).numValues() + 1;
  604.     double[][] prior_nom = new double[nx][ny];
  605.     double[] sumx = new double[nx];
  606.     double[] sumy = new double[ny];
  607.     double[] stdvsx = new double[nx];
  608.     double[] stdvsy = new double[ny];
  609.     double[][] covs = new double[nx][ny];
  610.     for (i = 0; i < nx; i++) {
  611.       sumx[i] = stdvsx[i] = 0.0;
  612.     }
  613.     for (j = 0; j < ny; j++) {
  614.       sumy[j] = stdvsy[j] = 0.0;
  615.     }
  616.     for (i = 0; i < nx; i++) {
  617.       for (j = 0; j < ny; j++) {
  618. covs[i][j] = prior_nom[i][j] = 0.0;
  619.       }
  620.     }
  621.     // calculate frequencies (and means) of the values of the nominal 
  622.     // attribute
  623.     for (i = 0; i < m_numInstances; i++) {
  624.       inst = m_trainInstances.instance(i);
  625.       if (inst.isMissing(att1)) {
  626. if (!m_missingSeperate) {
  627.   ii = mx;
  628. }
  629. else {
  630.   ii = nx - 1;
  631. }
  632.       }
  633.       else {
  634. ii = (int)inst.value(att1);
  635.       }
  636.       if (inst.isMissing(att2)) {
  637. if (!m_missingSeperate) {
  638.   jj = my;
  639. }
  640. else {
  641.   jj = ny - 1;
  642. }
  643.       }
  644.       else {
  645. jj = (int)inst.value(att2);
  646.       }
  647.       // increment freq for nominal
  648.       prior_nom[ii][jj]++;
  649.       sumx[ii]++;
  650.       sumy[jj]++;
  651.     }
  652.     for (z = 0; z < m_numInstances; z++) {
  653.       inst = m_trainInstances.instance(z);
  654.       for (j = 0; j < ny; j++) {
  655. if (inst.isMissing(att2)) {
  656.   if (!m_missingSeperate) {
  657.     temp2 = (j == my)? 1.0 : 0.0;
  658.   }
  659.   else {
  660.     temp2 = (j == (ny - 1))? 1.0 : 0.0;
  661.   }
  662. }
  663. else {
  664.   temp2 = (j == inst.value(att2))? 1.0 : 0.0;
  665. }
  666. diff2 = (temp2 - (sumy[j]/m_numInstances));
  667. stdvsy[j] += (diff2*diff2);
  668.       }
  669.       // 
  670.       for (i = 0; i < nx; i++) {
  671. if (inst.isMissing(att1)) {
  672.   if (!m_missingSeperate) {
  673.     temp1 = (i == mx)? 1.0 : 0.0;
  674.   }
  675.   else {
  676.     temp1 = (i == (nx - 1))? 1.0 : 0.0;
  677.   }
  678. }
  679. else {
  680.   temp1 = (i == inst.value(att1))? 1.0 : 0.0;
  681. }
  682. diff1 = (temp1 - (sumx[i]/m_numInstances));
  683. stdvsx[i] += (diff1*diff1);
  684. for (j = 0; j < ny; j++) {
  685.   if (inst.isMissing(att2)) {
  686.     if (!m_missingSeperate) {
  687.       temp2 = (j == my)? 1.0 : 0.0;
  688.     }
  689.     else {
  690.       temp2 = (j == (ny - 1))? 1.0 : 0.0;
  691.     }
  692.   }
  693.   else {
  694.     temp2 = (j == inst.value(att2))? 1.0 : 0.0;
  695.   }
  696.   diff2 = (temp2 - (sumy[j]/m_numInstances));
  697.   covs[i][j] += (diff1*diff2);
  698. }
  699.       }
  700.     }
  701.     // calculate weighted correlation
  702.     for (i = 0; i < nx; i++) {
  703.       for (j = 0; j < ny; j++) {
  704. if ((stdvsx[i]*stdvsy[j]) > 0.0) {
  705.   //System.out.println("Stdv :"+stdvs_nom[i]);
  706.   rr = (covs[i][j]/(Math.sqrt(stdvsx[i]*stdvsy[j])));
  707.   if (rr < 0.0) {
  708.     rr = -rr;
  709.   }
  710.   r += ((prior_nom[i][j]/m_numInstances)*rr);
  711. }
  712. // if there is zero variance for either of the categorical atts then if
  713. // neither is the class then make this
  714. // correlation at this level maximally bad i.e. 1.0. If either is 
  715. // the class then maximally bad correlation is 0.0
  716. else {if (att1 != m_classIndex && att2 != m_classIndex) {
  717.   r += ((prior_nom[i][j]/m_numInstances)*1.0);
  718. }
  719. }
  720.       }
  721.     }
  722.     // calculate weighted standard deviations for these attributes
  723.     // (if necessary)
  724.     for (i = 0, temp1 = 0.0; i < nx; i++) {
  725.       temp1 += ((sumx[i]/m_numInstances)*(stdvsx[i]/m_numInstances));
  726.     }
  727.     if (temp1 != 0.0) {
  728.       if (m_std_devs[att1] == 1.0) {
  729. m_std_devs[att1] = Math.sqrt(temp1);
  730.       }
  731.     }
  732.     for (j = 0, temp2 = 0.0; j < ny; j++) {
  733.       temp2 += ((sumy[j]/m_numInstances)*(stdvsy[j]/m_numInstances));
  734.     }
  735.     if (temp2 != 0.0) {
  736.       if (m_std_devs[att2] == 1.0) {
  737. m_std_devs[att2] = Math.sqrt(temp2);
  738.       }
  739.     }
  740.     if (r == 0.0) {
  741.       if (att1 != m_classIndex && att2 != m_classIndex) {
  742. r = 1.0;
  743.       }
  744.     }
  745.     return  r;
  746.   }
  747.   /**
  748.    * returns a string describing CFS
  749.    *
  750.    * @return the description as a string
  751.    */
  752.   public String toString () {
  753.     StringBuffer text = new StringBuffer();
  754.     if (m_trainInstances == null) {
  755.       text.append("CFS subset evaluator has not been built yetn");
  756.     }
  757.     else {
  758.       text.append("tCFS Subset Evaluatorn");
  759.       if (m_missingSeperate) {
  760. text.append("tTreating missing values as a seperate valuen");
  761.       }
  762.       if (m_locallyPredictive) {
  763. text.append("tIncluding locally predictive attributesn");
  764.       }
  765.     }
  766.     return  text.toString();
  767.   }
  768.   private void addLocallyPredictive (BitSet best_group) {
  769.     int i, j;
  770.     boolean done = false;
  771.     boolean ok = true;
  772.     double temp_best = -1.0;
  773.     double corr;
  774.     j = 0;
  775.     BitSet temp_group = (BitSet)best_group.clone();
  776.     while (!done) {
  777.       temp_best = -1.0;
  778.       // find best not already in group
  779.       for (i = 0; i < m_numAttribs; i++) {
  780. if ((!temp_group.get(i)) && (i != m_classIndex)) {
  781.   if (m_corr_matrix.getElement(i, m_classIndex) == -999) {
  782.     corr = correlate(i, m_classIndex);
  783.     m_corr_matrix.setElement(i, m_classIndex, corr);
  784.     m_corr_matrix.setElement(m_classIndex, i, corr);
  785.   }
  786.   if (m_corr_matrix.getElement(i, m_classIndex) > temp_best) {
  787.     temp_best = m_corr_matrix.getElement(i, m_classIndex);
  788.     j = i;
  789.   }
  790. }
  791.       }
  792.       if (temp_best == -1.0) {
  793. done = true;
  794.       }
  795.       else {
  796. ok = true;
  797. temp_group.set(j);
  798. // check the best against correlations with others already
  799. // in group 
  800. for (i = 0; i < m_numAttribs; i++) {
  801.   if (best_group.get(i)) {
  802.     if (m_corr_matrix.getElement(i, j) == -999) {
  803.       corr = correlate(i, j);
  804.       m_corr_matrix.setElement(i, j, corr);
  805.       m_corr_matrix.setElement(j, i, corr);
  806.     }
  807.     if (m_corr_matrix.getElement(i, j) > temp_best - m_c_Threshold) {
  808.       ok = false;
  809.       break;
  810.     }
  811.   }
  812. }
  813. // if ok then add to best_group
  814. if (ok) {
  815.   best_group.set(j);
  816. }
  817.       }
  818.     }
  819.   }
  820.   /**
  821.    * Calls locallyPredictive in order to include locally predictive
  822.    * attributes (if requested).
  823.    *
  824.    * @param attributeSet the set of attributes found by the search
  825.    * @return a possibly ranked list of postprocessed attributes
  826.    * @exception Exception if postprocessing fails for some reason
  827.    */
  828.   public int[] postProcess (int[] attributeSet)
  829.     throws Exception
  830.   {
  831.     int j = 0;
  832.     if (!m_locallyPredictive) {
  833.       //      m_trainInstances = new Instances(m_trainInstances,0);
  834.       return  attributeSet;
  835.     }
  836.     BitSet bestGroup = new BitSet(m_numAttribs);
  837.     for (int i = 0; i < attributeSet.length; i++) {
  838.       bestGroup.set(attributeSet[i]);
  839.     }
  840.     addLocallyPredictive(bestGroup);
  841.     // count how many are set
  842.     for (int i = 0; i < m_numAttribs; i++) {
  843.       if (bestGroup.get(i)) {
  844. j++;
  845.       }
  846.     }
  847.     int[] newSet = new int[j];
  848.     j = 0;
  849.     for (int i = 0; i < m_numAttribs; i++) {
  850.       if (bestGroup.get(i)) {
  851. newSet[j++] = i;
  852.       }
  853.     }
  854.     //    m_trainInstances = new Instances(m_trainInstances,0);
  855.     return  newSet;
  856.   }
  857.   protected void resetOptions () {
  858.     m_trainInstances = null;
  859.     m_missingSeperate = false;
  860.     m_locallyPredictive = false;
  861.     m_c_Threshold = 0.0;
  862.   }
  863.   /**
  864.    * Main method for testing this class.
  865.    *
  866.    * @param args the options
  867.    */
  868.   public static void main (String[] args) {
  869.     try {
  870.       System.out.println(AttributeSelection.
  871.  SelectAttributes(new CfsSubsetEval(), args));
  872.     }
  873.     catch (Exception e) {
  874.       e.printStackTrace();
  875.       System.out.println(e.getMessage());
  876.     }
  877.   }
  878. }