Resample.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.  *    Resample.java
  18.  *    Copyright (C) 1999 Intelligenesis Corp.
  19.  *
  20.  */
  21. package weka.filters.supervised.instance;
  22. import weka.filters.*;
  23. import weka.core.Instance;
  24. import weka.core.Instances;
  25. import weka.core.OptionHandler;
  26. import weka.core.Option;
  27. import weka.core.Utils;
  28. import java.util.Random;
  29. import java.util.Enumeration;
  30. import java.util.Vector;
  31. /** 
  32.  * Produces a random subsample of a dataset. The original dataset must
  33.  * fit entirely in memory. The number of instances in the generated
  34.  * dataset may be specified. The dataset must have a nominal class
  35.  * attribute. If not, use the unsupervised version. The filter can be
  36.  * made to maintain the class distribution in the subsample, or to bias
  37.  * the class distribution toward a uniform distribution. When used in batch
  38.  * mode, subsequent batches are <b>not</b> resampled.
  39.  *
  40.  * Valid options are:<p>
  41.  *
  42.  * -S num <br>
  43.  * Specify the random number seed (default 1).<p>
  44.  *
  45.  * -B num <br>
  46.  * Specify a bias towards uniform class distribution. 0 = distribution
  47.  * in input data, 1 = uniform class distribution (default 0). <p>
  48.  *
  49.  * -Z percent <br>
  50.  * Specify the size of the output dataset, as a percentage of the input
  51.  * dataset (default 100). <p>
  52.  *
  53.  * @author Len Trigg (len@intelligenesis.net)
  54.  * @version $Revision: 1.1 $ 
  55.  **/
  56. public class Resample extends Filter implements SupervisedFilter,
  57. OptionHandler {
  58.   /** The subsample size, percent of original set, default 100% */
  59.   private double m_SampleSizePercent = 100;
  60.   
  61.   /** The random number generator seed */
  62.   private int m_RandomSeed = 1;
  63.   
  64.   /** The degree of bias towards uniform (nominal) class distribution */
  65.   private double m_BiasToUniformClass = 0;
  66.   /** True if the first batch has been done */
  67.   private boolean m_FirstBatchDone = false;
  68.   /**
  69.    * Returns an enumeration describing the available options.
  70.    *
  71.    * @return an enumeration of all the available options.
  72.    */
  73.   public Enumeration listOptions() {
  74.     Vector newVector = new Vector(1);
  75.     newVector.addElement(new Option(
  76.               "tSpecify the random number seed (default 1)",
  77.               "S", 1, "-S <num>"));
  78.     newVector.addElement(new Option(
  79.               "tThe size of the output dataset, as a percentage ofn"
  80.               +"tthe input dataset (default 100)",
  81.               "Z", 1, "-Z <num>"));
  82.     newVector.addElement(new Option(
  83.               "tBias factor towards uniform class distribution.n"
  84.               +"t0 = distribution in input data -- 1 = uniform distribution.n"
  85.               +"t(default 0)",
  86.               "B", 1, "-B <num>"));
  87.     return newVector.elements();
  88.   }
  89.   /**
  90.    * Parses a list of options for this object. Valid options are:<p>
  91.    *
  92.    * -S num <br>
  93.    * Specify the random number seed (default 1).<p>
  94.    *
  95.    * -B num <br>
  96.    * Specify a bias towards uniform class distribution. 0 = distribution
  97.    * in input data, 1 = uniform class distribution (default 0). <p>
  98.    *
  99.    * -Z percent <br>
  100.    * Specify the size of the output dataset, as a percentage of the input
  101.    * dataset (default 100). <p>
  102.    *
  103.    * @param options the list of options as an array of strings
  104.    * @exception Exception if an option is not supported
  105.    */
  106.   public void setOptions(String[] options) throws Exception {
  107.     
  108.     String seedString = Utils.getOption('S', options);
  109.     if (seedString.length() != 0) {
  110.       setRandomSeed(Integer.parseInt(seedString));
  111.     } else {
  112.       setRandomSeed(1);
  113.     }
  114.     String biasString = Utils.getOption('B', options);
  115.     if (biasString.length() != 0) {
  116.       setBiasToUniformClass(Double.valueOf(biasString).doubleValue());
  117.     } else {
  118.       setBiasToUniformClass(0);
  119.     }
  120.     String sizeString = Utils.getOption('Z', options);
  121.     if (sizeString.length() != 0) {
  122.       setSampleSizePercent(Double.valueOf(sizeString).doubleValue());
  123.     } else {
  124.       setSampleSizePercent(100);
  125.     }
  126.     if (getInputFormat() != null) {
  127.       setInputFormat(getInputFormat());
  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 [6];
  137.     int current = 0;
  138.     options[current++] = "-B"; 
  139.     options[current++] = "" + getBiasToUniformClass();
  140.     options[current++] = "-S"; options[current++] = "" + getRandomSeed();
  141.     options[current++] = "-Z"; options[current++] = "" + getSampleSizePercent();
  142.     while (current < options.length) {
  143.       options[current++] = "";
  144.     }
  145.     return options;
  146.   }
  147.   
  148.   
  149.   /**
  150.    * Gets the bias towards a uniform class. A value of 0 leaves the class
  151.    * distribution as-is, a value of 1 ensures the class distributions are
  152.    * uniform in the output data.
  153.    *
  154.    * @return the current bias
  155.    */
  156.   public double getBiasToUniformClass() {
  157.     return m_BiasToUniformClass;
  158.   }
  159.   
  160.   /**
  161.    * Sets the bias towards a uniform class. A value of 0 leaves the class
  162.    * distribution as-is, a value of 1 ensures the class distributions are
  163.    * uniform in the output data.
  164.    *
  165.    * @param newBiasToUniformClass the new bias value, between 0 and 1.
  166.    */
  167.   public void setBiasToUniformClass(double newBiasToUniformClass) {
  168.     m_BiasToUniformClass = newBiasToUniformClass;
  169.   }
  170.   
  171.   /**
  172.    * Gets the random number seed.
  173.    *
  174.    * @return the random number seed.
  175.    */
  176.   public int getRandomSeed() {
  177.     return m_RandomSeed;
  178.   }
  179.   
  180.   /**
  181.    * Sets the random number seed.
  182.    *
  183.    * @param newSeed the new random number seed.
  184.    */
  185.   public void setRandomSeed(int newSeed) {
  186.     m_RandomSeed = newSeed;
  187.   }
  188.   
  189.   /**
  190.    * Gets the subsample size as a percentage of the original set.
  191.    *
  192.    * @return the subsample size
  193.    */
  194.   public double getSampleSizePercent() {
  195.     return m_SampleSizePercent;
  196.   }
  197.   
  198.   /**
  199.    * Sets the size of the subsample, as a percentage of the original set.
  200.    *
  201.    * @param newSampleSizePercent the subsample set size, between 0 and 100.
  202.    */
  203.   public void setSampleSizePercent(double newSampleSizePercent) {
  204.     m_SampleSizePercent = newSampleSizePercent;
  205.   }
  206.   
  207.   /**
  208.    * Sets the format of the input instances.
  209.    *
  210.    * @param instanceInfo an Instances object containing the input 
  211.    * instance structure (any instances contained in the object are 
  212.    * ignored - only the structure is required).
  213.    * @return true if the outputFormat may be collected immediately
  214.    * @exception Exception if the input format can't be set 
  215.    * successfully
  216.    */
  217.   public boolean setInputFormat(Instances instanceInfo) 
  218.        throws Exception {
  219.     if (instanceInfo.classIndex() < 0 || !instanceInfo.classAttribute().isNominal()) {
  220.       throw new IllegalArgumentException("Supervised resample requires nominal class");
  221.     }
  222.     super.setInputFormat(instanceInfo);
  223.     setOutputFormat(instanceInfo);
  224.     m_FirstBatchDone = false;
  225.     return true;
  226.   }
  227.   /**
  228.    * Input an instance for filtering. Filter requires all
  229.    * training instances be read before producing output.
  230.    *
  231.    * @param instance the input instance
  232.    * @return true if the filtered instance may now be
  233.    * collected with output().
  234.    * @exception IllegalStateException if no input structure has been defined
  235.    */
  236.   public boolean input(Instance instance) {
  237.     if (getInputFormat() == null) {
  238.       throw new IllegalStateException("No input instance format defined");
  239.     }
  240.     if (m_NewBatch) {
  241.       resetQueue();
  242.       m_NewBatch = false;
  243.     }
  244.     if (m_FirstBatchDone) {
  245.       push(instance);
  246.       return true;
  247.     } else {
  248.       bufferInput(instance);
  249.       return false;
  250.     }
  251.   }
  252.   /**
  253.    * Signify that this batch of input to the filter is finished. 
  254.    * If the filter requires all instances prior to filtering,
  255.    * output() may now be called to retrieve the filtered instances.
  256.    *
  257.    * @return true if there are instances pending output
  258.    * @exception IllegalStateException if no input structure has been defined
  259.    */
  260.   public boolean batchFinished() {
  261.     if (getInputFormat() == null) {
  262.       throw new IllegalStateException("No input instance format defined");
  263.     }
  264.     if (!m_FirstBatchDone) {
  265.       // Do the subsample, and clear the input instances.
  266.       createSubsample();
  267.     }
  268.     flushInput();
  269.     m_NewBatch = true;
  270.     m_FirstBatchDone = true;
  271.     return (numPendingOutput() != 0);
  272.   }
  273.   /**
  274.    * Creates a subsample of the current set of input instances. The output
  275.    * instances are pushed onto the output queue for collection.
  276.    */
  277.   private void createSubsample() {
  278.     int origSize = getInputFormat().numInstances();
  279.     int sampleSize = (int) (origSize * m_SampleSizePercent / 100);
  280.     // Subsample that takes class distribution into consideration
  281.     // Sort according to class attribute.
  282.     getInputFormat().sort(getInputFormat().classIndex());
  283.     
  284.     // Create an index of where each class value starts
  285.     int [] classIndices = new int [getInputFormat().numClasses() + 1];
  286.     int currentClass = 0;
  287.     classIndices[currentClass] = 0;
  288.     for (int i = 0; i < getInputFormat().numInstances(); i++) {
  289.       Instance current = getInputFormat().instance(i);
  290.       if (current.classIsMissing()) {
  291. for (int j = currentClass + 1; j < classIndices.length; j++) {
  292.   classIndices[j] = i;
  293. }
  294. break;
  295.       } else if (current.classValue() != currentClass) {
  296. for (int j = currentClass + 1; j <= current.classValue(); j++) {
  297.   classIndices[j] = i;
  298. }          
  299. currentClass = (int) current.classValue();
  300.       }
  301.     }
  302.     if (currentClass <= getInputFormat().numClasses()) {
  303.       for (int j = currentClass + 1; j < classIndices.length; j++) {
  304. classIndices[j] = getInputFormat().numInstances();
  305.       }
  306.     }
  307.     
  308.     int actualClasses = 0;
  309.     for (int i = 0; i < classIndices.length - 1; i++) {
  310.       if (classIndices[i] != classIndices[i + 1]) {
  311. actualClasses++;
  312.       }
  313.     }
  314.     // Create the new sample
  315.     
  316.     Random random = new Random(m_RandomSeed);
  317.     // Convert pending input instances
  318.     for(int i = 0; i < sampleSize; i++) {
  319.       int index = 0;
  320.       if (random.nextDouble() < m_BiasToUniformClass) {
  321. // Pick a random class (of those classes that actually appear)
  322. int cIndex = Math.abs(random.nextInt()) % actualClasses;
  323. for (int j = 0, k = 0; j < classIndices.length - 1; j++) {
  324.   if ((classIndices[j] != classIndices[j + 1]) 
  325.       && (k++ >= cIndex)) {
  326.     // Pick a random instance of the designated class
  327.     index = classIndices[j] 
  328.       + (Math.abs(random.nextInt()) % (classIndices[j + 1]
  329.        - classIndices[j]));
  330.     break;
  331.   }
  332. }
  333.       } else {
  334. index = (int) (random.nextDouble() * origSize);
  335.       }
  336.       push((Instance)getInputFormat().instance(index).copy());
  337.     }
  338.   }
  339.   
  340.   
  341.   /**
  342.    * Main method for testing this class.
  343.    *
  344.    * @param argv should contain arguments to the filter: 
  345.    * use -h for help
  346.    */
  347.   public static void main(String [] argv) {
  348.     try {
  349.       if (Utils.getFlag('b', argv)) {
  350.   Filter.batchFilterFile(new Resample(), argv);
  351.       } else {
  352. Filter.filterFile(new Resample(), argv);
  353.       }
  354.     } catch (Exception ex) {
  355.       System.out.println(ex.getMessage());
  356.     }
  357.   }
  358. }