ClassOrder.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 13k
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.  *    ClassOrder.java
  18.  *    Copyright (C) 2002 Xin Xu
  19.  *
  20.  */
  21. package weka.filters.supervised.attribute;
  22. import weka.filters.*;
  23. import java.util.Enumeration;
  24. import java.util.Vector;
  25. import java.util.Random;
  26. import weka.core.Attribute;
  27. import weka.core.Instance;
  28. import weka.core.Instances;
  29. import weka.core.Option;
  30. import weka.core.OptionHandler;
  31. import weka.core.Utils;
  32. /** 
  33.  * A filter that sorts the order of classes so that the class values are 
  34.  * no longer of in the order of that in the header file after filtered.
  35.  * The values of the class will be in the order specified by the user
  36.  * -- it could be either in ascending/descending order by the class
  37.  * frequency or in random order.<p>
  38.  *
  39.  * The format of the header is thus not changed in this filter 
  40.  * (although it still uses <code>setInputFormat()</code>), but
  41.  * the class value of each instance is converted to sorted 
  42.  * values within the same range.  The value can also be converted back
  43.  * using <code>originalValue(double value)</code> procedure.<p>  
  44.  *
  45.  * @author Xin Xu (xx5@cs.waikato.ac.nz)
  46.  * @version $Revision: 1.1 $
  47.  */
  48. public class ClassOrder extends Filter implements SupervisedFilter,
  49.   OptionHandler {
  50.     
  51.   /** The seed of randomization */
  52.   private long m_Seed = 1;
  53.     
  54.   /** The random object */
  55.   private Random m_Random = null;
  56.     
  57.   /** 
  58.    * The 1-1 converting table from the original class values
  59.    * to the new values
  60.    */
  61.   private double[] m_Converter = null;
  62.     
  63.   /** Class attribute of the data */
  64.   private Attribute m_ClassAttribute = null;
  65.   /** The class order to be sorted */
  66.   private int m_ClassOrder = 0;
  67.     
  68.   /** The class values are sorted in ascending order based on their frequencies */
  69.   public static final int FREQ_ASCEND = 0;
  70.   /** The class values are sorted in descending order based on their frequencies */
  71.   public static final int FREQ_DESCEND = 1;
  72.    
  73.   /** The class values are sorted in random order*/
  74.   public static final int RANDOM =2;
  75.   /** This class can provide the class distribution in the sorted order 
  76.    *  as side effect */
  77.   private double[] m_ClassCounts = null;
  78.     
  79.   /**
  80.    * Returns an enumeration describing the available options.
  81.    *
  82.    * @return an enumeration of all the available options.
  83.    */
  84.   public Enumeration listOptions() {
  85.     Vector newVector = new Vector(1);
  86.     newVector.addElement(new Option("tSpecify the seed of randomizationn"
  87.     + "tused to randomize the classn"
  88.     + "torder (default: 1)",
  89.     "R", 1, "-R <seed>"));
  90.     newVector.addElement(new Option("tSpecify the class order to ben"
  91.     + "tsorted, could be 0: ascendingn"
  92.     + "t1: descending and 2: random.(default: 0)",
  93.     "C", 1, "-C <order>"));
  94.     return newVector.elements();
  95.   }
  96.     
  97.     
  98.   /**
  99.    * Parses a given list of options controlling the behaviour of this object.
  100.    * Valid options are:<p>
  101.    *
  102.    * -R <seed> <br>
  103.    * Specify the seed of randomization used to randomize the class order
  104.    * (default: 1)<p>
  105.    *
  106.    * -C <order><br>
  107.    * Specify the class order to be sorted, could be 0: ascending, 1: descending 
  108.    * and 2: random(default: 0)<p>
  109.    *
  110.    * @param options the list of options as an array of strings
  111.    * @exception Exception if an option is not supported
  112.    */
  113.   public void setOptions(String[] options) throws Exception {
  114.     String seedString = Utils.getOption('R', options);
  115.     if (seedString.length() != 0)
  116.       m_Seed = Long.parseLong(seedString);
  117.     else 
  118.       m_Seed = 1;  
  119.     String orderString = Utils.getOption('C', options);
  120.     if (orderString.length() != 0)
  121.       m_ClassOrder = Integer.parseInt(orderString);
  122.     else 
  123.       m_ClassOrder = FREQ_ASCEND;   
  124.     if (getInputFormat() != null)
  125.       setInputFormat(getInputFormat()); 
  126.     m_Random = null;
  127.   }
  128.     
  129.     
  130.   /**
  131.    * Gets the current settings of the filter.
  132.    *
  133.    * @return an array of strings suitable for passing to setOptions
  134.    */
  135.   public String [] getOptions() {
  136.     String [] options = new String [4];
  137.     int current = 0;
  138.     options[current++] = "-R"; 
  139.     options[current++] = "" + m_Seed;
  140.     options[current++] = "-C"; 
  141.     options[current++] = "" + m_ClassOrder;
  142.     while (current < options.length) {
  143.       options[current++] = "";
  144.     }
  145.     return options;
  146.   }
  147.     
  148.   /**
  149.    * Returns the tip text for this property
  150.    *
  151.    * @return tip text for this property suitable for
  152.    * displaying in the explorer/experimenter gui
  153.    */
  154.   public String seedTipText() {
  155.     return "Specify the seed of randomization of the class order";
  156.   }
  157.     
  158.   /**
  159.    * Get the current randomization seed
  160.    *
  161.    * @return a seed
  162.    */
  163.   public long getSeed() {
  164.     return m_Seed;
  165.   }
  166.   /**
  167.    * Set randomization seed
  168.    *
  169.    * @param seed the set seed
  170.    */
  171.   public void setSeed(long seed){
  172.     m_Seed = seed;
  173.     m_Random = null;
  174.   }
  175.     
  176.   /**
  177.    * Returns the tip text for this property
  178.    *
  179.    * @return tip text for this property suitable for
  180.    * displaying in the explorer/experimenter gui
  181.    */
  182.   public String classOrderTipText() {
  183.     return "Specify the class order after the filtering";
  184.   }
  185.     
  186.   /**
  187.    * Get the wanted class order
  188.    *
  189.    * @return class order
  190.    */
  191.   public int getClassOrder() {
  192.     return m_ClassOrder;
  193.   }
  194.     
  195.   /**
  196.    * Set the wanted class order
  197.    *
  198.    * @param order the class order
  199.    */
  200.   public void setClassOrder(int order){
  201.     m_ClassOrder = order;
  202.   }
  203.     
  204.   /**
  205.    * Sets the format of the input instances.
  206.    *
  207.    * @param instanceInfo an Instances object containing the input instance
  208.    * structure (any instances contained in the object are ignored - only the
  209.    * structure is required).
  210.    * @return true if the outputFormat may be collected immediately
  211.    */
  212.     public boolean setInputFormat(Instances instanceInfo) throws Exception {     
  213. super.setInputFormat(new Instances(instanceInfo, 0));
  214. m_ClassAttribute = instanceInfo.classAttribute();
  215. m_Random = new Random(m_Seed);
  216. int numClasses = instanceInfo.numClasses();
  217. m_Converter = new double[numClasses];
  218. m_ClassCounts = new double[numClasses];
  219. return false;
  220.     }    
  221.     
  222.   /**
  223.    * Input an instance for filtering. Ordinarily the instance is processed
  224.    * and made available for output immediately. Some filters require all
  225.    * instances be read before producing output.
  226.    *
  227.    * @param instance the input instance
  228.    * @return true if the filtered instance may now be
  229.    * collected with output().
  230.    * @exception IllegalStateException if no input format has been defined.
  231.    */
  232.   public boolean input(Instance instance) {
  233.     if (getInputFormat() == null) {
  234.       throw new IllegalStateException("No input instance format defined");
  235.     }
  236.     if (m_NewBatch) {
  237.       resetQueue();
  238.       m_NewBatch = false;
  239.     }
  240.     if((!instance.isMissing(m_ClassAttribute)) 
  241.        && m_ClassAttribute.isNominal())
  242.       m_ClassCounts[(int)instance.classValue()] += instance.weight();
  243.     bufferInput(instance);
  244.     return false;
  245.   }
  246.   /**
  247.    * Signify that this batch of input to the filter is finished. If
  248.    * the filter requires all instances prior to filtering, output()
  249.    * may now be called to retrieve the filtered instances. Any
  250.    * subsequent instances filtered should be filtered based on setting
  251.    * obtained from the first batch (unless the inputFormat has been
  252.    * re-assigned or new options have been set). This implementation 
  253.    * sorts the class values and provide class counts in the output format
  254.    *
  255.    * @return true if there are instances pending output
  256.    * @exception NullPointerException if no input structure has been defined,
  257.    * @exception Exception if there was a problem finishing the batch.
  258.    */
  259.   public boolean batchFinished() throws Exception {
  260.       Instances data = getInputFormat();
  261.       if ( data == null) 
  262.   throw new NullPointerException("No input instance format defined");
  263.     
  264.       data.deleteWithMissingClass();
  265.       if(data.numInstances() == 0)
  266.   throw new Exception(" No instances with a class value!");      
  267.       
  268.       Instances newInsts = new Instances(data, 0);
  269.       if(m_ClassAttribute.isNominal()){
  270.   switch(m_ClassOrder){
  271.   case FREQ_ASCEND:
  272.   case FREQ_DESCEND:
  273.       // Sort it and put into a double array
  274.       int[] tmp = Utils.sort(m_ClassCounts);
  275.       double[] classOrder = new double[tmp.length];
  276.       for(int t=0; t < tmp.length; t++)
  277.   classOrder[t] = (double)tmp[t];
  278.       
  279.       int lo=-1, hi=-1, max=m_Converter.length-1;
  280.       for(int y=0; y<=max; y++){ // y is the new class label
  281.   double next = (y==max) ? -1.0 : m_ClassCounts[(int)classOrder[y+1]];
  282.   if(Utils.eq(m_ClassCounts[(int)classOrder[y]], next)){
  283.       if(lo == -1)
  284.   lo = y;
  285.   }
  286.   else if(lo != -1){ // Randomize the order of classes with same size
  287.       hi = y;
  288.       randomize(classOrder, lo, hi);
  289.       for(int yy=lo; yy<=hi; yy++){
  290.   if(m_ClassOrder == FREQ_ASCEND)
  291.       m_Converter[(int)classOrder[yy]] = (double)yy;
  292.   else
  293.       m_Converter[(int)classOrder[yy]] = (double)(max-yy);
  294.   
  295.       }
  296.       lo = hi = -1;
  297.   }
  298.   else{ // Record in the converting table
  299.       if(m_ClassOrder == FREQ_ASCEND)
  300.   m_Converter[(int)classOrder[y]] = (double)y;
  301.       else
  302.   m_Converter[(int)classOrder[y]] = (double)(max-y);
  303.   }
  304.       }
  305.       break;
  306.   case RANDOM:
  307.       for(int x=0; x < m_Converter.length; x++)     
  308.   m_Converter[x] = (double)x;
  309.       
  310.       randomize(m_Converter, 0, m_Converter.length-1);       
  311.       break;
  312.   default:
  313.       throw new Exception("Class order not defined!"); 
  314.   }
  315.   
  316.   // Reset the class values
  317.   int classIndex = newInsts.classIndex();
  318.   double[] cls = new double[m_ClassCounts.length];
  319.   for(int z=0; z<m_Converter.length; z++){
  320.       newInsts.renameAttributeValue(classIndex, (int)m_Converter[z], m_ClassAttribute.value(z));
  321.       cls[(int)m_Converter[z]] = m_ClassCounts[z];
  322.   }
  323.   m_ClassCounts = cls;
  324.       }
  325.       
  326.       setOutputFormat(newInsts);
  327.       // Process all instances
  328.       for(int xyz=0; xyz<data.numInstances(); xyz++){
  329.   Instance datum = data.instance(xyz);
  330.   if(m_ClassAttribute.isNominal())
  331.       datum.setClassValue(m_Converter[(int)datum.classValue()]);
  332.   // Add back the String attributes
  333.   copyStringValues(datum, false, data, getOutputFormat());
  334.   datum.setDataset(getOutputFormat());
  335.   push(datum);
  336.       }
  337.     flushInput();
  338.     m_NewBatch = true;
  339.     return (numPendingOutput() != 0);
  340.   }
  341.   /**
  342.    * Helper function to randomize the given double array from the
  343.    * the given low index to the given high index
  344.    *
  345.    * @param array the given double array
  346.    * @param low low index
  347.    * @param high high index
  348.    */
  349.   private void randomize(double[] array, int low, int high){
  350.     for(int y=low; y <= high; y++){
  351.       int swapPos = m_Random.nextInt(high-y+1) + y;
  352.       double temp = array[y];
  353.       array[y] = array[swapPos];
  354.       array[swapPos] = temp;
  355.     }
  356.   }
  357.     
  358.   /**
  359.    * Get the class distribution of the sorted class values.  If class is numeric
  360.    * it returns null 
  361.    *
  362.    * @return the class counts
  363.    */
  364.   public double[] getClassCounts(){ 
  365.     if(m_ClassAttribute.isNominal())
  366.       return m_ClassCounts; 
  367.     else
  368.       return null;
  369.   }
  370.     /**
  371.      * Convert the given class distribution back to the distributions
  372.      * with the original internal class index
  373.      * 
  374.      * @param before the given class distribution
  375.      * @return the distribution converted back
  376.      */
  377.     public double[] distributionsByOriginalIndex (double[] before){
  378. double[] after = new double[m_Converter.length];
  379. for(int i=0; i < m_Converter.length; i++) 
  380.     after[i] = before[(int)m_Converter[i]];
  381. return after;
  382.     }
  383.   /**
  384.    * Return the original internal class value given the randomized 
  385.    * class value, i.e. the string presentations of the two indices
  386.    * are the same.  It's useful when the filter is used within a classifier  
  387.    * so that the filtering procedure should be transparent to the 
  388.    * evaluation
  389.    *
  390.    * @param value the given value
  391.    * @return the original internal value, -1 if not found
  392.    * @exception if the coverter table is not set yet
  393.    */
  394.   public double originalValue(double value)throws Exception{
  395.     if(m_Converter == null)
  396.       throw new IllegalStateException("Coverter table not defined yet!");
  397.     for(int i=0; i < m_Converter.length; i++)
  398.       if((int)value == (int)m_Converter[i])
  399. return (double)i;
  400.     return -1;
  401.   }   
  402.   /**
  403.    * Main method for testing this class.
  404.    *
  405.    * @param argv should contain arguments to the filter: use -h for help
  406.    */
  407.   public static void main(String [] argv) {
  408.     try {
  409.       if (Utils.getFlag('b', argv)) {
  410. Filter.batchFilterFile(new ClassOrder(), argv);
  411.       } else {
  412. Filter.filterFile(new ClassOrder(), argv);
  413.       }
  414.     } catch (Exception ex) {
  415.       System.out.println(ex.getMessage());
  416.     }
  417.   }
  418. }