Discretize.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 24k
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.  *    Discretize.java
  18.  *    Copyright (C) 1999 Eibe Frank,Len Trigg
  19.  *
  20.  */
  21. package weka.filters.unsupervised.attribute;
  22. import weka.filters.*;
  23. import java.io.*;
  24. import java.util.*;
  25. import weka.core.*;
  26. /** 
  27.  * An instance filter that discretizes a range of numeric attributes in 
  28.  * the dataset into nominal attributes. Discretization is by simple binning.<p>
  29.  *
  30.  * Valid filter-specific options are: <p>
  31.  *
  32.  * -B num <br>
  33.  * Specifies the (maximum) number of bins to divide numeric attributes into.
  34.  * Default = 10.<p>
  35.  *
  36.  * -F <br>
  37.  * Use equal-frequency instead of equal-width discretization if 
  38.  * class-based discretisation is turned off.<p>
  39.  *
  40.  * -O <br>
  41.  * Optimize the number of bins using a leave-one-out estimate of the 
  42.  * entropy (for equal-width binning).<p>
  43.  *
  44.  * -R col1,col2-col4,... <br>
  45.  * Specifies list of columns to Discretize. First
  46.  * and last are valid indexes. (default: none) <p>
  47.  *
  48.  * -V <br>
  49.  * Invert matching sense.<p>
  50.  *
  51.  * -D <br>
  52.  * Make binary nominal attributes. <p>
  53.  * 
  54.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  55.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  56.  * @version $Revision: 1.1 $
  57.  */
  58. public class Discretize extends Filter 
  59.   implements UnsupervisedFilter, OptionHandler, WeightedInstancesHandler {
  60.   /** Stores which columns to Discretize */
  61.   protected Range m_DiscretizeCols = new Range();
  62.   /** The number of bins to divide the attribute into */
  63.   protected int m_NumBins = 10;
  64.   /** Store the current cutpoints */
  65.   protected double [][] m_CutPoints = null;
  66.   /** Output binary attributes for discretized attributes. */
  67.   protected boolean m_MakeBinary = false;
  68.   /** Find the number of bins using cross-validated entropy. */
  69.   protected boolean m_FindNumBins = false;
  70.   /** Use equal-frequency binning if unsupervised discretization turned on */
  71.   protected boolean m_UseEqualFrequency = false;
  72.   /** Constructor - initialises the filter */
  73.   public Discretize() {
  74.     setAttributeIndices("first-last");
  75.   }
  76.   /**
  77.    * Gets an enumeration describing the available options.
  78.    *
  79.    * @return an enumeration of all the available options.
  80.    */
  81.   public Enumeration listOptions() {
  82.     Vector newVector = new Vector(7);
  83.     newVector.addElement(new Option(
  84.               "tSpecifies the (maximum) number of bins to divide numeric"
  85.       + " attributes into.n"
  86.       + "t(default = 10)",
  87.               "B", 1, "-B <num>"));
  88.     newVector.addElement(new Option(
  89.               "tUse equal-frequency instead of equal-width discretization.",
  90.               "F", 0, "-F"));
  91.     newVector.addElement(new Option(
  92.               "tOptimize number of bins using leave-one-out estimaten"+
  93.       "tof estimated entropy (for equal-width discretization).",
  94.               "O", 0, "-O"));
  95.     newVector.addElement(new Option(
  96.               "tSpecifies list of columns to Discretize. First"
  97.       + " and last are valid indexes.n"
  98.       + "t(default none)",
  99.               "R", 1, "-R <col1,col2-col4,...>"));
  100.     newVector.addElement(new Option(
  101.               "tInvert matching sense of column indexes.",
  102.               "V", 0, "-V"));
  103.     newVector.addElement(new Option(
  104.               "tOutput binary attributes for discretized attributes.",
  105.               "D", 0, "-D"));
  106.     return newVector.elements();
  107.   }
  108.   /**
  109.    * Parses the options for this object. Valid options are: <p>
  110.    *
  111.    * -B num <br>
  112.    * Specifies the (maximum) number of bins to divide numeric attributes into.
  113.    * Default = 10.<p>
  114.    *
  115.    * -F <br>
  116.    * Use equal-frequency instead of equal-width discretization if 
  117.    * class-based discretisation is turned off.<p>
  118.    *
  119.    * -O <br>
  120.    * Optimize the number of bins using a leave-one-out estimate of the 
  121.    * entropy (for equal-width binning).<p>
  122.    *
  123.    * -R col1,col2-col4,... <br>
  124.    * Specifies list of columns to Discretize. First
  125.    * and last are valid indexes. (default none) <p>
  126.    *
  127.    * -V <br>
  128.    * Invert matching sense.<p>
  129.    *
  130.    * -D <br>
  131.    * Make binary nominal attributes. <p>
  132.    * 
  133.    * @param options the list of options as an array of strings
  134.    * @exception Exception if an option is not supported
  135.    */
  136.   public void setOptions(String[] options) throws Exception {
  137.     setMakeBinary(Utils.getFlag('D', options));
  138.     setUseEqualFrequency(Utils.getFlag('F', options));
  139.     setFindNumBins(Utils.getFlag('O', options));
  140.     setInvertSelection(Utils.getFlag('V', options));
  141.     String numBins = Utils.getOption('B', options);
  142.     if (numBins.length() != 0) {
  143.       setBins(Integer.parseInt(numBins));
  144.     } else {
  145.       setBins(10);
  146.     }
  147.     
  148.     String convertList = Utils.getOption('R', options);
  149.     if (convertList.length() != 0) {
  150.       setAttributeIndices(convertList);
  151.     } else {
  152.       setAttributeIndices("first-last");
  153.     }
  154.     if (getInputFormat() != null) {
  155.       setInputFormat(getInputFormat());
  156.     }
  157.   }
  158.   /**
  159.    * Gets the current settings of the filter.
  160.    *
  161.    * @return an array of strings suitable for passing to setOptions
  162.    */
  163.   public String [] getOptions() {
  164.     String [] options = new String [12];
  165.     int current = 0;
  166.     if (getMakeBinary()) {
  167.       options[current++] = "-D";
  168.     }
  169.     if (getUseEqualFrequency()) {
  170.       options[current++] = "-F";
  171.     }
  172.     if (getFindNumBins()) {
  173.       options[current++] = "-O";
  174.     }
  175.     if (getInvertSelection()) {
  176.       options[current++] = "-V";
  177.     }
  178.     options[current++] = "-B"; options[current++] = "" + getBins();
  179.     if (!getAttributeIndices().equals("")) {
  180.       options[current++] = "-R"; options[current++] = getAttributeIndices();
  181.     }
  182.     while (current < options.length) {
  183.       options[current++] = "";
  184.     }
  185.     return options;
  186.   }
  187.   /**
  188.    * Sets the format of the input instances.
  189.    *
  190.    * @param instanceInfo an Instances object containing the input instance
  191.    * structure (any instances contained in the object are ignored - only the
  192.    * structure is required).
  193.    * @return true if the outputFormat may be collected immediately
  194.    * @exception Exception if the input format can't be set successfully
  195.    */
  196.   public boolean setInputFormat(Instances instanceInfo) throws Exception {
  197.     super.setInputFormat(instanceInfo);
  198.     m_DiscretizeCols.setUpper(instanceInfo.numAttributes() - 1);
  199.     m_CutPoints = null;
  200.     
  201.     if (getFindNumBins() && getUseEqualFrequency()) {
  202.       throw new IllegalArgumentException("Bin number optimization in conjunction "+
  203.  "with equal-frequency binning not implemented.");
  204.     }
  205.     // If we implement loading cutfiles, then load 
  206.     //them here and set the output format
  207.     return false;
  208.   }
  209.   
  210.   /**
  211.    * Input an instance for filtering. Ordinarily the instance is processed
  212.    * and made available for output immediately. Some filters require all
  213.    * instances be read before producing output.
  214.    *
  215.    * @param instance the input instance
  216.    * @return true if the filtered instance may now be
  217.    * collected with output().
  218.    * @exception IllegalStateException if no input format has been defined.
  219.    */
  220.   public boolean input(Instance instance) {
  221.     if (getInputFormat() == null) {
  222.       throw new IllegalStateException("No input instance format defined");
  223.     }
  224.     if (m_NewBatch) {
  225.       resetQueue();
  226.       m_NewBatch = false;
  227.     }
  228.     
  229.     if (m_CutPoints != null) {
  230.       convertInstance(instance);
  231.       return true;
  232.     }
  233.     bufferInput(instance);
  234.     return false;
  235.   }
  236.   /**
  237.    * Signifies that this batch of input to the filter is finished. If the 
  238.    * filter requires all instances prior to filtering, output() may now 
  239.    * be called to retrieve the filtered instances.
  240.    *
  241.    * @return true if there are instances pending output
  242.    * @exception IllegalStateException if no input structure has been defined
  243.    */
  244.   public boolean batchFinished() {
  245.     if (getInputFormat() == null) {
  246.       throw new IllegalStateException("No input instance format defined");
  247.     }
  248.     if (m_CutPoints == null) {
  249.       calculateCutPoints();
  250.       setOutputFormat();
  251.       // If we implement saving cutfiles, save the cuts here
  252.       // Convert pending input instances
  253.       for(int i = 0; i < getInputFormat().numInstances(); i++) {
  254. convertInstance(getInputFormat().instance(i));
  255.       }
  256.     } 
  257.     flushInput();
  258.     m_NewBatch = true;
  259.     return (numPendingOutput() != 0);
  260.   }
  261.   /**
  262.    * Returns a string describing this filter
  263.    *
  264.    * @return a description of the filter suitable for
  265.    * displaying in the explorer/experimenter gui
  266.    */
  267.   public String globalInfo() {
  268.     return "An instance filter that discretizes a range of numeric"
  269.       + " attributes in the dataset into nominal attributes."
  270.       + " Discretization is by simple binning.";
  271.   }
  272.   
  273.   /**
  274.    * Returns the tip text for this property
  275.    *
  276.    * @return tip text for this property suitable for
  277.    * displaying in the explorer/experimenter gui
  278.    */
  279.   public String findNumBinsTipText() {
  280.     return "Optimize number of equal-width bins using leave-one-out.";
  281.   }
  282.   /**
  283.    * Get the value of FindNumBins.
  284.    *
  285.    * @return Value of FindNumBins.
  286.    */
  287.   public boolean getFindNumBins() {
  288.     
  289.     return m_FindNumBins;
  290.   }
  291.   
  292.   /**
  293.    * Set the value of FindNumBins.
  294.    *
  295.    * @param newFindNumBins Value to assign to FindNumBins.
  296.    */
  297.   public void setFindNumBins(boolean newFindNumBins) {
  298.     
  299.     m_FindNumBins = newFindNumBins;
  300.   }
  301.   
  302.   /**
  303.    * Returns the tip text for this property
  304.    *
  305.    * @return tip text for this property suitable for
  306.    * displaying in the explorer/experimenter gui
  307.    */
  308.   public String makeBinaryTipText() {
  309.     return "Make resulting attributes binary.";
  310.   }
  311.   /**
  312.    * Gets whether binary attributes should be made for discretized ones.
  313.    *
  314.    * @return true if attributes will be binarized
  315.    */
  316.   public boolean getMakeBinary() {
  317.     return m_MakeBinary;
  318.   }
  319.   /** 
  320.    * Sets whether binary attributes should be made for discretized ones.
  321.    *
  322.    * @param makeBinary if binary attributes are to be made
  323.    */
  324.   public void setMakeBinary(boolean makeBinary) {
  325.     m_MakeBinary = makeBinary;
  326.   }
  327.   
  328.   /**
  329.    * Get the value of UseEqualFrequency.
  330.    *
  331.    * @return Value of UseEqualFrequency.
  332.    */
  333.   public boolean getUseEqualFrequency() {
  334.     
  335.     return m_UseEqualFrequency;
  336.   }
  337.   
  338.   /**
  339.    * Set the value of UseEqualFrequency.
  340.    *
  341.    * @param newUseEqualFrequency Value to assign to UseEqualFrequency.
  342.    */
  343.   public void setUseEqualFrequency(boolean newUseEqualFrequency) {
  344.     
  345.     m_UseEqualFrequency = newUseEqualFrequency;
  346.   }
  347.   /**
  348.    * Returns the tip text for this property
  349.    *
  350.    * @return tip text for this property suitable for
  351.    * displaying in the explorer/experimenter gui
  352.    */
  353.   public String binsTipText() {
  354.     return "Number of bins.";
  355.   }
  356.   /**
  357.    * Gets the number of bins numeric attributes will be divided into
  358.    *
  359.    * @return the number of bins.
  360.    */
  361.   public int getBins() {
  362.     return m_NumBins;
  363.   }
  364.   /**
  365.    * Sets the number of bins to divide each selected numeric attribute into
  366.    *
  367.    * @param numBins the number of bins
  368.    */
  369.   public void setBins(int numBins) {
  370.     m_NumBins = numBins;
  371.   }
  372.   /**
  373.    * Returns the tip text for this property
  374.    *
  375.    * @return tip text for this property suitable for
  376.    * displaying in the explorer/experimenter gui
  377.    */
  378.   public String invertSelectionTipText() {
  379.     return "Set attribute selection mode. If false, only selected"
  380.       + " (numeric) attributes in the range will be discretized; if"
  381.       + " true, only non-selected attributes will be discretized.";
  382.   }
  383.   /**
  384.    * Gets whether the supplied columns are to be removed or kept
  385.    *
  386.    * @return true if the supplied columns will be kept
  387.    */
  388.   public boolean getInvertSelection() {
  389.     return m_DiscretizeCols.getInvert();
  390.   }
  391.   /**
  392.    * Sets whether selected columns should be removed or kept. If true the 
  393.    * selected columns are kept and unselected columns are deleted. If false
  394.    * selected columns are deleted and unselected columns are kept.
  395.    *
  396.    * @param invert the new invert setting
  397.    */
  398.   public void setInvertSelection(boolean invert) {
  399.     m_DiscretizeCols.setInvert(invert);
  400.   }
  401.   /**
  402.    * Returns the tip text for this property
  403.    *
  404.    * @return tip text for this property suitable for
  405.    * displaying in the explorer/experimenter gui
  406.    */
  407.   public String attributeIndicesTipText() {
  408.     return "Specify range of attributes to act on."
  409.       + " This is a comma separated list of attribute indices, with"
  410.       + " "first" and "last" valid values. Specify an inclusive"
  411.       + " range with "-". E.g: "first-3,5,6-10,last".";
  412.   }
  413.   /**
  414.    * Gets the current range selection
  415.    *
  416.    * @return a string containing a comma separated list of ranges
  417.    */
  418.   public String getAttributeIndices() {
  419.     return m_DiscretizeCols.getRanges();
  420.   }
  421.   /**
  422.    * Sets which attributes are to be Discretized (only numeric
  423.    * attributes among the selection will be Discretized).
  424.    *
  425.    * @param rangeList a string representing the list of attributes. Since
  426.    * the string will typically come from a user, attributes are indexed from
  427.    * 1. <br>
  428.    * eg: first-3,5,6-last
  429.    * @exception IllegalArgumentException if an invalid range list is supplied 
  430.    */
  431.   public void setAttributeIndices(String rangeList) {
  432.     m_DiscretizeCols.setRanges(rangeList);
  433.   }
  434.   /**
  435.    * Sets which attributes are to be Discretized (only numeric
  436.    * attributes among the selection will be Discretized).
  437.    *
  438.    * @param attributes an array containing indexes of attributes to Discretize.
  439.    * Since the array will typically come from a program, attributes are indexed
  440.    * from 0.
  441.    * @exception IllegalArgumentException if an invalid set of ranges
  442.    * is supplied 
  443.    */
  444.   public void setAttributeIndicesArray(int [] attributes) {
  445.     setAttributeIndices(Range.indicesToRangeList(attributes));
  446.   }
  447.   /**
  448.    * Gets the cut points for an attribute
  449.    *
  450.    * @param the index (from 0) of the attribute to get the cut points of
  451.    * @return an array containing the cutpoints (or null if the
  452.    * attribute requested isn't being Discretized
  453.    */
  454.   public double [] getCutPoints(int attributeIndex) {
  455.     if (m_CutPoints == null) {
  456.       return null;
  457.     }
  458.     return m_CutPoints[attributeIndex];
  459.   }
  460.   /** Generate the cutpoints for each attribute */
  461.   protected void calculateCutPoints() {
  462.     Instances copy = null;
  463.     m_CutPoints = new double [getInputFormat().numAttributes()] [];
  464.     for(int i = getInputFormat().numAttributes() - 1; i >= 0; i--) {
  465.       if ((m_DiscretizeCols.isInRange(i)) && 
  466.   (getInputFormat().attribute(i).isNumeric())) {
  467. if (m_FindNumBins) {
  468.   findNumBins(i);
  469. } else if (!m_UseEqualFrequency) {
  470.   calculateCutPointsByEqualWidthBinning(i);
  471. } else {
  472.   calculateCutPointsByEqualFrequencyBinning(i);
  473. }
  474.       }
  475.     }
  476.   }
  477.  
  478.   /**
  479.    * Set cutpoints for a single attribute.
  480.    *
  481.    * @param index the index of the attribute to set cutpoints for
  482.    */
  483.   protected void calculateCutPointsByEqualWidthBinning(int index) {
  484.     // Scan for max and min values
  485.     double max = 0, min = 1, currentVal;
  486.     Instance currentInstance;
  487.     for(int i = 0; i < getInputFormat().numInstances(); i++) {
  488.       currentInstance = getInputFormat().instance(i);
  489.       if (!currentInstance.isMissing(index)) {
  490. currentVal = currentInstance.value(index);
  491. if (max < min) {
  492.   max = min = currentVal;
  493. }
  494. if (currentVal > max) {
  495.   max = currentVal;
  496. }
  497. if (currentVal < min) {
  498.   min = currentVal;
  499. }
  500.       }
  501.     }
  502.     double binWidth = (max - min) / m_NumBins;
  503.     double [] cutPoints = null;
  504.     if ((m_NumBins > 1) && (binWidth > 0)) {
  505.       cutPoints = new double [m_NumBins - 1];
  506.       for(int i = 1; i < m_NumBins; i++) {
  507. cutPoints[i - 1] = min + binWidth * i;
  508.       }
  509.     }
  510.     m_CutPoints[index] = cutPoints;
  511.   }
  512.  
  513.   /**
  514.    * Set cutpoints for a single attribute.
  515.    *
  516.    * @param index the index of the attribute to set cutpoints for
  517.    */
  518.   protected void calculateCutPointsByEqualFrequencyBinning(int index) {
  519.     // Copy data so that it can be sorted
  520.     Instances data = new Instances(getInputFormat());
  521.     // Sort input data
  522.     data.sort(index);
  523.     // Compute weight of instances without missing values
  524.     double sumOfWeights = 0;
  525.     for (int i = 0; i < data.numInstances(); i++) {
  526.       if (data.instance(i).isMissing(index)) {
  527. break;
  528.       } else {
  529. sumOfWeights += data.instance(i).weight();
  530.       }
  531.     }
  532.     double freq = sumOfWeights / m_NumBins;
  533.     // Compute break points
  534.     double[] cutPoints = new double[m_NumBins - 1];
  535.     double counter = 0;
  536.     int cpindex = 0;
  537.     for (int i = 0; i < data.numInstances() - 1; i++) {
  538.       // Stop if value missing
  539.       if (data.instance(i).isMissing(index)) {
  540. break;
  541.       }
  542.       counter += data.instance(i).weight();
  543.       // Do we have a potential breakpoint?
  544.       if (data.instance(i).value(index) < 
  545.   data.instance(i + 1).value(index)) {
  546. if (counter >= freq) {
  547.   cutPoints[cpindex] = (data.instance(i).value(index) +
  548. data.instance(i + 1).value(index)) / 2;
  549.   cpindex++;
  550.   counter = counter - freq;
  551. }
  552.       }
  553.     }
  554.     // Did we find any cutpoints?
  555.     if (cpindex == 0) {
  556.       m_CutPoints[index] = null;
  557.     } else {
  558.       double[] cp = new double[cpindex];
  559.       for (int i = 0; i < cpindex; i++) {
  560. cp[i] = cutPoints[i];
  561.       }
  562.       m_CutPoints[index] = cp;
  563.     }
  564.   }
  565.   /**
  566.    * Optimizes the number of bins using leave-one-out cross-validation.
  567.    *
  568.    * @param index the attribute index
  569.    */
  570.   protected void findNumBins(int index) {
  571.     double min = Double.MAX_VALUE, max = -Double.MIN_VALUE, binWidth = 0, 
  572.       entropy, bestEntropy = Double.MAX_VALUE, currentVal;
  573.     double[] distribution;
  574.     int bestNumBins  = 1;
  575.     Instance currentInstance;
  576.     // Find minimum and maximum
  577.     for (int i = 0; i < getInputFormat().numInstances(); i++) {
  578.       currentInstance = getInputFormat().instance(i);
  579.       if (!currentInstance.isMissing(index)) {
  580. currentVal = currentInstance.value(index);
  581. if (currentVal > max) {
  582.   max = currentVal;
  583. }
  584. if (currentVal < min) {
  585.   min = currentVal;
  586. }
  587.       }
  588.     }
  589.     // Find best number of bins
  590.     for (int i = 0; i < m_NumBins; i++) {
  591.       distribution = new double[i + 1];
  592.       binWidth = (max - min) / (i + 1);
  593.       // Compute distribution
  594.       for (int j = 0; j < getInputFormat().numInstances(); j++) {
  595. currentInstance = getInputFormat().instance(j);
  596. if (!currentInstance.isMissing(index)) {
  597.   for (int k = 0; k < i + 1; k++) {
  598.     if (currentInstance.value(index) <= 
  599. (min + (((double)k + 1) * binWidth))) {
  600.       distribution[k] += currentInstance.weight();
  601.       break;
  602.     }
  603.   }
  604. }
  605.       }
  606.       // Compute cross-validated entropy
  607.       entropy = 0;
  608.       for (int k = 0; k < i + 1; k++) {
  609. if (distribution[k] < 2) {
  610.   entropy = Double.MAX_VALUE;
  611.   break;
  612. }
  613. entropy -= distribution[k] * Math.log((distribution[k] - 1) / 
  614.       binWidth);
  615.       }
  616.       // Best entropy so far?
  617.       if (entropy < bestEntropy) {
  618. bestEntropy = entropy;
  619. bestNumBins = i + 1;
  620.       }
  621.     }
  622.     // Compute cut points
  623.     double [] cutPoints = null;
  624.     if ((bestNumBins > 1) && (binWidth > 0)) {
  625.       cutPoints = new double [bestNumBins - 1];
  626.       for(int i = 1; i < bestNumBins; i++) {
  627. cutPoints[i - 1] = min + binWidth * i;
  628.       }
  629.     }
  630.     m_CutPoints[index] = cutPoints;
  631.    }
  632.   /**
  633.    * Set the output format. Takes the currently defined cutpoints and 
  634.    * m_InputFormat and calls setOutputFormat(Instances) appropriately.
  635.    */
  636.   protected void setOutputFormat() {
  637.     if (m_CutPoints == null) {
  638.       setOutputFormat(null);
  639.       return;
  640.     }
  641.     FastVector attributes = new FastVector(getInputFormat().numAttributes());
  642.     int classIndex = getInputFormat().classIndex();
  643.     for(int i = 0; i < getInputFormat().numAttributes(); i++) {
  644.       if ((m_DiscretizeCols.isInRange(i)) 
  645.   && (getInputFormat().attribute(i).isNumeric())) {
  646. if (!m_MakeBinary) {
  647.   FastVector attribValues = new FastVector(1);
  648.   if (m_CutPoints[i] == null) {
  649.     attribValues.addElement("'All'");
  650.   } else {
  651.     for(int j = 0; j <= m_CutPoints[i].length; j++) {
  652.       if (j == 0) {
  653. attribValues.addElement("'(-inf-"
  654. + Utils.doubleToString(m_CutPoints[i][j], 6) + "]'");
  655.       } else if (j == m_CutPoints[i].length) {
  656. attribValues.addElement("'("
  657. + Utils.doubleToString(m_CutPoints[i][j - 1], 6) 
  658. + "-inf)'");
  659.       } else {
  660. attribValues.addElement("'("
  661. + Utils.doubleToString(m_CutPoints[i][j - 1], 6) + "-"
  662. + Utils.doubleToString(m_CutPoints[i][j], 6) + "]'");
  663.       }
  664.     }
  665.   }
  666.   attributes.addElement(new Attribute(getInputFormat().
  667.       attribute(i).name(),
  668.       attribValues));
  669. } else {
  670.   if (m_CutPoints[i] == null) {
  671.     FastVector attribValues = new FastVector(1);
  672.     attribValues.addElement("'All'");
  673.     attributes.addElement(new Attribute(getInputFormat().
  674. attribute(i).name(),
  675. attribValues));
  676.   } else {
  677.     if (i < getInputFormat().classIndex()) {
  678.       classIndex += m_CutPoints[i].length - 1;
  679.     }
  680.     for(int j = 0; j < m_CutPoints[i].length; j++) {
  681.       FastVector attribValues = new FastVector(2);
  682.       attribValues.addElement("'(-inf-"
  683.       + Utils.doubleToString(m_CutPoints[i][j], 6) + "]'");
  684.       attribValues.addElement("'("
  685.       + Utils.doubleToString(m_CutPoints[i][j], 6) + "-inf)'");
  686.       attributes.addElement(new Attribute(getInputFormat().
  687.   attribute(i).name(),
  688.   attribValues));
  689.     }
  690.   }
  691. }
  692.       } else {
  693. attributes.addElement(getInputFormat().attribute(i).copy());
  694.       }
  695.     }
  696.     Instances outputFormat = 
  697.       new Instances(getInputFormat().relationName(), attributes, 0);
  698.     outputFormat.setClassIndex(classIndex);
  699.     setOutputFormat(outputFormat);
  700.   }
  701.   /**
  702.    * Convert a single instance over. The converted instance is added to 
  703.    * the end of the output queue.
  704.    *
  705.    * @param instance the instance to convert
  706.    */
  707.   protected void convertInstance(Instance instance) {
  708.     int index = 0;
  709.     double [] vals = new double [outputFormatPeek().numAttributes()];
  710.     // Copy and convert the values
  711.     for(int i = 0; i < getInputFormat().numAttributes(); i++) {
  712.       if (m_DiscretizeCols.isInRange(i) && 
  713.   getInputFormat().attribute(i).isNumeric()) {
  714. int j;
  715. double currentVal = instance.value(i);
  716. if (m_CutPoints[i] == null) {
  717.   if (instance.isMissing(i)) {
  718.     vals[index] = Instance.missingValue();
  719.   } else {
  720.     vals[index] = 0;
  721.   }
  722.   index++;
  723. } else {
  724.   if (!m_MakeBinary) {
  725.     if (instance.isMissing(i)) {
  726.       vals[index] = Instance.missingValue();
  727.     } else {
  728.       for (j = 0; j < m_CutPoints[i].length; j++) {
  729. if (currentVal <= m_CutPoints[i][j]) {
  730.   break;
  731. }
  732.       }
  733.               vals[index] = j;
  734.     }
  735.     index++;
  736.   } else {
  737.     for (j = 0; j < m_CutPoints[i].length; j++) {
  738.       if (instance.isMissing(i)) {
  739.                 vals[index] = Instance.missingValue();
  740.       } else if (currentVal <= m_CutPoints[i][j]) {
  741.                 vals[index] = 0;
  742.       } else {
  743.                 vals[index] = 1;
  744.       }
  745.       index++;
  746.     }
  747.   }   
  748. }
  749.       } else {
  750.         vals[index] = instance.value(i);
  751. index++;
  752.       }
  753.     }
  754.     
  755.     Instance inst = null;
  756.     if (instance instanceof SparseInstance) {
  757.       inst = new SparseInstance(instance.weight(), vals);
  758.     } else {
  759.       inst = new Instance(instance.weight(), vals);
  760.     }
  761.     copyStringValues(inst, false, instance.dataset(), getInputStringIndex(),
  762.                      getOutputFormat(), getOutputStringIndex());
  763.     inst.setDataset(getOutputFormat());
  764.     push(inst);
  765.   }
  766.   /**
  767.    * Main method for testing this class.
  768.    *
  769.    * @param argv should contain arguments to the filter: use -h for help
  770.    */
  771.   public static void main(String [] argv) {
  772.     try {
  773.       if (Utils.getFlag('b', argv)) {
  774.   Filter.batchFilterFile(new Discretize(), argv);
  775.       } else {
  776. Filter.filterFile(new Discretize(), argv);
  777.       }
  778.     } catch (Exception ex) {
  779.       System.out.println(ex.getMessage());
  780.     }
  781.   }
  782. }