AddExpression.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 21k
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.  *    AddExpression.java
  18.  *    Copyright (C) 2000 Mark Hall
  19.  *
  20.  */
  21. package weka.filters.unsupervised.attribute;
  22. import weka.filters.*;
  23. import java.io.*;
  24. import java.util.StringTokenizer;
  25. import java.util.Stack;
  26. import java.util.Vector;
  27. import java.util.Enumeration;
  28. import weka.core.Utils;
  29. import weka.core.OptionHandler;
  30. import weka.core.Option;
  31. import weka.core.Instance;
  32. import weka.core.SparseInstance;
  33. import weka.core.Instances;
  34. import weka.core.Attribute;
  35. /**
  36.  * Applys a mathematical expression involving attributes and numeric
  37.  * constants to a dataset. A new attribute is appended after the last
  38.  * attribute that contains the result of applying the expression. 
  39.  * Supported operators are: +, -, *, /, ^, log, abs, cos, exp, sqrt, 
  40.  * floor, ceil, rint, tan, sin, (, ). Attributes are specified
  41.  * by prefixing with 'a', eg. a7 is attribute number 7 (starting from 1). <p>
  42.  * 
  43.  * Valid filter-specific options are:<p>
  44.  * 
  45.  * -E expression <br>
  46.  * Specify the expression to apply. Eg. a1^2*a5/log(a7*4.0). <p>
  47.  *
  48.  * -N name <br>
  49.  * Specify a name for the new attribute. Default is to name it with the
  50.  * expression provided with the -E option. <p>
  51.  *
  52.  * -D <br>
  53.  * Debug. Names the attribute with the postfix parse of the expression. <p>
  54.  *
  55.  * @author Mark Hall (mhall@cs.waikato.ac.nz)
  56.  * @version $Revision: 1.1 $
  57.  */
  58. public class AddExpression extends Filter 
  59.   implements UnsupervisedFilter, StreamableFilter, OptionHandler {
  60.   /**
  61.    * Inner class handling an attribute index as an operand
  62.    */
  63.   private class AttributeOperand implements Serializable {
  64.     /** the index of the attribute */
  65.     protected int m_attributeIndex;
  66.     /** true if the value of the attribute are to be multiplied by -1 */
  67.     protected boolean m_negative;
  68.     public AttributeOperand(String operand, boolean sign) throws Exception {
  69.       // strip the leading 'a' and set the index
  70.       m_attributeIndex = (Integer.parseInt(operand.substring(1)))-1;
  71.       m_negative = sign;
  72.     }
  73.     /**
  74.      * Return a string describing this object
  75.      * @return a string descibing the attribute operand
  76.      */
  77.     public String toString() {
  78.       String result = "";
  79.       if (m_negative) {
  80. result += '-';
  81.       }
  82.       return result+"a"+(m_attributeIndex+1);
  83.     }
  84.   }
  85.   /**
  86.    * Inner class for storing numeric constant opperands
  87.    */
  88.   private class NumericOperand implements Serializable {
  89.     /** numeric constant */
  90.     protected double m_numericConst;
  91.     public NumericOperand(String operand, boolean sign) throws Exception {
  92.       m_numericConst = Double.valueOf(operand).doubleValue();
  93.       if (sign) {
  94. m_numericConst *= -1.0;
  95.       }
  96.     }
  97.     
  98.     /**
  99.      * Return a string describing this object
  100.      * @return a string descibing the numeric operand
  101.      */
  102.     public String toString() {
  103.       return ""+m_numericConst;
  104.     }
  105.   }
  106.   /**
  107.    * Inner class for storing operators
  108.    */
  109.   private class Operator implements Serializable {
  110.     /** the operator */
  111.     protected char m_operator;
  112.     public Operator(char opp) throws IllegalArgumentException {
  113.       if (!isOperator(opp)) {
  114. throw new IllegalArgumentException("Unrecognized operator:" + opp);
  115.       }
  116.       m_operator = opp;
  117.     }
  118.     /**
  119.      * Apply this operator to the supplied arguments
  120.      * @param first the first argument
  121.      * @param second the second argument
  122.      * @return the result
  123.      */
  124.     protected double applyOperator(double first, double second) {
  125.       switch (m_operator) {
  126.       case '+' :
  127. return (first+second);
  128.       case '-' :
  129. return (first-second);
  130.       case '*' :
  131. return (first*second);
  132.       case '/' :
  133. return (first/second);
  134.       case '^' :
  135. return Math.pow(first,second);
  136.       }
  137.       return Double.NaN;
  138.     }
  139.     /**
  140.      * Apply this operator (function) to the supplied argument
  141.      * @param value the argument
  142.      * @return the result
  143.      */
  144.     protected double applyFunction(double value) {
  145.       switch (m_operator) {
  146.       case 'l' :
  147. return Math.log(value);
  148.       case 'b' :
  149. return Math.abs(value);
  150.       case 'c' :
  151. return Math.cos(value);
  152.       case 'e' :
  153. return Math.exp(value);
  154.       case 's' :
  155. return Math.sqrt(value);
  156.       case 'f' :
  157. return Math.floor(value);
  158.       case 'h' :
  159. return Math.ceil(value);
  160.       case 'r' :
  161. return Math.rint(value);
  162.       case 't' :
  163. return Math.tan(value);
  164.       case 'n' :
  165. return Math.sin(value);
  166.       }
  167.       return Double.NaN;
  168.     }
  169.     /**
  170.      * Return a string describing this object
  171.      * @return a string descibing the operator
  172.      */
  173.     public String toString() {
  174.       return ""+m_operator;
  175.     }
  176.   }
  177.   /** The infix expression */
  178.   private String m_infixExpression = "a1^2";
  179.   /** Operator stack */
  180.   private Stack m_operatorStack = new Stack();
  181.   /** Supported operators. l = log, b = abs, c = cos, e = exp, s = sqrt, 
  182.       f = floor, h = ceil, r = rint, t = tan, n = sin */
  183.   private static final String OPERATORS = "+-*/()^lbcesfhrtn";
  184.   private static final String UNARY_FUNCTIONS = "lbcesfhrtn";
  185.   /** Holds the expression in postfix form */
  186.   private Vector m_postFixExpVector;
  187.   /** True if the next numeric constant or attribute index is negative */
  188.   private boolean m_signMod = false;
  189.   /** Holds the previous token */
  190.   private String m_previousTok = "";
  191.   /** Name of the new attribute. "expression"  length string will use the 
  192.       provided expression as the new attribute name */
  193.   private String m_attributeName="expression";
  194.   /** If true, makes the attribute name equal to the postfix parse of the
  195.       expression */
  196.   private boolean m_Debug = false;
  197.   /**
  198.    * Returns a string describing this filter
  199.    *
  200.    * @return a description of the filter suitable for
  201.    * displaying in the explorer/experimenter gui
  202.    */
  203.   public String globalInfo() {
  204.     return "An instance filter that creates a new attribute by applying a "
  205.       +"mathematical expression to existing attributes. The expression "
  206.       +"can contain attribute references and numeric constants. Supported "
  207.       +"opperators are :  +, -, *, /, ^, log, abs, cos, exp, sqrt, "
  208.       +"floor, ceil, rint, tan, sin, (, ). Attributes are specified "
  209.       +"by prefixing with 'a', eg. a7 is attribute number 7 (starting from 1)."
  210.       +" Example expression : a1^2*a5/log(a7*4.0)."; 
  211.   }
  212.   /**
  213.    * Handles the processing of an infix operand to postfix
  214.    * @param tok the infix operand
  215.    * @exception Exception if there is difficulty parsing the operand
  216.    */
  217.   private void handleOperand(String tok) throws Exception {
  218.     // if it contains an 'a' then its an attribute index
  219.     if (tok.indexOf('a') != -1) {
  220.       m_postFixExpVector.addElement(new AttributeOperand(tok,m_signMod));
  221.     } else {
  222.       try {
  223. // should be a numeric constant
  224. m_postFixExpVector.addElement(new NumericOperand(tok, m_signMod));
  225.       } catch (NumberFormatException ne) {
  226. throw new Exception("Trouble parsing numeric constant");
  227.       }
  228.     }
  229.     m_signMod = false;
  230.   }
  231.   /**
  232.    * Handles the processing of an infix operator to postfix
  233.    * @param tok the infix operator
  234.    * @exception Exception if there is difficulty parsing the operator
  235.    */
  236.   private void handleOperator(String tok) throws Exception {
  237.     boolean push = true;
  238.     char tokchar = tok.charAt(0);
  239.     if (tokchar == ')') {
  240.       String popop = " ";
  241.       do {
  242. popop = (String)(m_operatorStack.pop());
  243. if (popop.charAt(0) != '(') {
  244.   m_postFixExpVector.addElement(new Operator(popop.charAt(0)));
  245. }
  246.       } while (popop.charAt(0) != '(');
  247.     } else {
  248.       int infixToc = infixPriority(tok.charAt(0));
  249.       while (!m_operatorStack.empty() && 
  250.      stackPriority(((String)(m_operatorStack.peek())).charAt(0)) 
  251.      >= infixToc) {
  252. // try an catch double operators and see if the current one can
  253. // be interpreted as the sign of an upcoming number
  254. if (m_previousTok.length() == 1 && 
  255.     isOperator(m_previousTok.charAt(0)) &&
  256.     m_previousTok.charAt(0) != ')') {
  257.   if (tok.charAt(0) == '-') {
  258.     m_signMod = true;
  259.   } else {
  260.     m_signMod = false;
  261.   }
  262.   push = false;
  263.   break;
  264. } else {
  265.   String popop = (String)(m_operatorStack.pop());
  266.   m_postFixExpVector.addElement(new Operator(popop.charAt(0)));
  267. }
  268.       }
  269.       if (m_postFixExpVector.size() == 0) {
  270. if (tok.charAt(0) == '-') {
  271.   m_signMod = true;
  272.   push = false;
  273. }
  274.       }
  275.       if (push) {
  276. m_operatorStack.push(tok);
  277.       }
  278.     }
  279.   }
  280.   /**
  281.    * Converts a string containing a mathematical expression in infix form
  282.    * to postfix form. The result is stored in the vector m_postfixExpVector
  283.    * @param infixExp the infix expression to convert
  284.    * @exception Exception if something goes wrong during the conversion
  285.    */
  286.   private void convertInfixToPostfix(String infixExp) throws Exception {
  287.     infixExp = Utils.removeSubstring(infixExp, " ");
  288.     infixExp = Utils.replaceSubstring(infixExp,"log","l");
  289.     infixExp = Utils.replaceSubstring(infixExp,"abs","b");
  290.     infixExp = Utils.replaceSubstring(infixExp,"cos","c");
  291.     infixExp = Utils.replaceSubstring(infixExp,"exp","e");
  292.     infixExp = Utils.replaceSubstring(infixExp,"sqrt","s");
  293.     infixExp = Utils.replaceSubstring(infixExp,"floor","f");
  294.     infixExp = Utils.replaceSubstring(infixExp,"ceil","h");
  295.     infixExp = Utils.replaceSubstring(infixExp,"rint","r");
  296.     infixExp = Utils.replaceSubstring(infixExp,"tan","t");
  297.     infixExp = Utils.replaceSubstring(infixExp,"sin","n");
  298.     StringTokenizer tokenizer = new StringTokenizer(infixExp, OPERATORS, true);
  299.     m_postFixExpVector = new Vector();
  300.     while (tokenizer.hasMoreTokens()) {
  301.       String tok = tokenizer.nextToken();
  302.       
  303.       if (tok.length() > 1) {
  304. handleOperand(tok);
  305.       } else {
  306. // probably an operator, but could be a single char operand
  307. if (isOperator(tok.charAt(0))) {
  308.   handleOperator(tok);
  309. } else {
  310.   // should be a numeric constant
  311.   handleOperand(tok);
  312. }
  313.       }
  314.       m_previousTok = tok;
  315.     }
  316.     while (!m_operatorStack.empty()) {
  317.       String popop = (String)(m_operatorStack.pop());
  318.       if (popop.charAt(0) == '(' || popop.charAt(0) == ')') {
  319. throw new Exception("Mis-matched parenthesis!");
  320.       }
  321.       m_postFixExpVector.addElement(new Operator(popop.charAt(0)));
  322.     }
  323.   }
  324.   /**
  325.    * Evaluate the expression using the supplied array of attribute values.
  326.    * The result is stored in the last element of the array. Assumes that
  327.    * the infix expression has been converted to postfix and stored in
  328.    * m_postFixExpVector
  329.    * @param vals the values to apply the expression to
  330.    * @exception Exception if something goes wrong
  331.    */
  332.   private void evaluateExpression(double [] vals) throws Exception {
  333.     Stack operands = new Stack();
  334.     for (int i=0;i<m_postFixExpVector.size();i++) {
  335.       Object nextob = m_postFixExpVector.elementAt(i);
  336.       if (nextob instanceof NumericOperand) {
  337. operands.push(new Double(((NumericOperand)nextob).m_numericConst));
  338.       } else if (nextob instanceof AttributeOperand) {
  339. double value = vals[((AttributeOperand)nextob).m_attributeIndex];
  340. if (value == Instance.missingValue()) {
  341.   vals[vals.length-1] = Instance.missingValue();
  342.   break;
  343. }
  344. if (((AttributeOperand)nextob).m_negative) {
  345.   value = -value;
  346. }
  347. operands.push(new Double(value));
  348.       } else if (nextob instanceof Operator) {
  349. char op = ((Operator)nextob).m_operator;
  350. if (isUnaryFunction(op)) {
  351.   double operand = ((Double)operands.pop()).doubleValue();
  352.   double result = ((Operator)nextob).applyFunction(operand);
  353.   operands.push(new Double(result));
  354. } else {
  355.   double second = ((Double)operands.pop()).doubleValue();
  356.   double first = ((Double)operands.pop()).doubleValue();
  357.   double result = ((Operator)nextob).applyOperator(first,second);
  358.   operands.push(new Double(result));
  359. }
  360.       } else {
  361. throw new Exception("Unknown object in postfix vector!");
  362.       }
  363.     }
  364.     if (operands.size() != 1) {
  365.       throw new Exception("Problem applying function");
  366.     }
  367.     Double result = ((Double)operands.pop());
  368.     if (result.isNaN() || result.isInfinite()) {
  369.       vals[vals.length-1] = Instance.missingValue();
  370.     } else {
  371.       vals[vals.length-1] = result.doubleValue();
  372.     }
  373.   }
  374.   /**
  375.    * Returns true if a token is an operator
  376.    * @param tok the token to check
  377.    * @return true if the supplied token is an operator
  378.    */
  379.   private boolean isOperator(char tok) {
  380.     if (OPERATORS.indexOf(tok) == -1) {
  381.       return false;
  382.     }
  383.     return true;
  384.   }
  385.   /**
  386.    * Returns true if a token is a unary function
  387.    * @param tok the token to check
  388.    * @return true if the supplied token is a unary function
  389.    */
  390.   private boolean isUnaryFunction(char tok) {
  391.     if (UNARY_FUNCTIONS.indexOf(tok) == -1) {
  392.       return false;
  393.     }
  394.     return true;
  395.   }
  396.   /**
  397.    * Return the infix priority of an operator
  398.    * @param char the operator
  399.    * @return the infix priority
  400.    */
  401.   private int infixPriority(char opp) throws IllegalArgumentException {
  402.     switch (opp) {
  403.     case 'l' : 
  404.     case 'b' :
  405.     case 'c' :
  406.     case 'e' :
  407.     case 's' :
  408.     case 'f' :
  409.     case 'h' :
  410.     case 'r' :
  411.     case 't' :
  412.     case 'n' :
  413.        return 3;
  414.     case '^' :
  415.       return 2;
  416.     case '*' : 
  417.       return 2;
  418.     case '/' : 
  419.       return 2;
  420.     case '+' :
  421.       return 1;
  422.     case '-' :
  423.       return 1;
  424.     case '(' :
  425.       return 4;
  426.     case ')' :
  427.       return 0;
  428.     default :
  429.       throw new IllegalArgumentException("Unrecognized operator:" + opp);
  430.     }
  431.   }
  432.   /**
  433.    * Return the stack priority of an operator
  434.    * @param char the operator
  435.    * @return the stack priority
  436.    */
  437.   private int stackPriority(char opp) throws IllegalArgumentException {
  438.      switch (opp) {
  439.      case 'l' :
  440.      case 'b' :
  441.      case 'c' :
  442.      case 'e' :
  443.      case 's' :
  444.      case 'f' :
  445.      case 'h' :
  446.      case 'r' :
  447.      case 't' :
  448.      case 'n' :
  449.        return 3;
  450.      case '^' :
  451.        return 2;
  452.     case '*' : 
  453.       return 2;
  454.     case '/' : 
  455.       return 2;
  456.     case '+' :
  457.       return 1;
  458.     case '-' :
  459.       return 1;
  460.     case '(' :
  461.       return 0;
  462.     case ')' :
  463.       return -1;
  464.     default :
  465.       throw new IllegalArgumentException("Unrecognized operator:" + opp);
  466.     }
  467.   }
  468.   /**
  469.    * Returns an enumeration describing the available options.
  470.    *
  471.    * @return an enumeration of all the available options.
  472.    */
  473.   public Enumeration listOptions() {
  474.     Vector newVector = new Vector(3); 
  475.     newVector.addElement(new Option(
  476.      "tSpecify the expression to apply. Eg a1^2*a5/log(a7*4.0)."
  477.      +"ntSupported opperators: ,+, -, *, /, ^, log, abs, cos, "
  478.      +"ntexp, sqrt, floor, ceil, rint, tan, sin, (, )",
  479.      "E",1,"-E <expression>"));
  480.     newVector.addElement(new Option(
  481.      "tSpecify the name for the new attribute. (default is the "
  482.      +"expression provided with -E)",
  483.      "N",1,"-N <name>"));
  484.     newVector.addElement(new Option(
  485.      "tDebug. Names attribute with the postfix parse of the "
  486.      +"expression.","D",0,"-D"));
  487.     return newVector.elements();
  488.   }
  489.   /**
  490.    * Parses a list of options for this object. Valid options are:<p>
  491.    *
  492.    * -E expression <br>
  493.    * Specify the expression to apply. Eg. a1^2*a5/log(a7*4.0). <p>
  494.    *
  495.    * -N name <br>
  496.    * Specify a name for the new attribute. Default is to name it with the
  497.    * expression provided with the -E option. <p>
  498.    *
  499.    * -D <br>
  500.    * Debug. Names the attribute with the postfix parse of the expression. <p>
  501.    *
  502.    * @param options the list of options as an array of strings
  503.    * @exception Exception if an option is not supported
  504.    */
  505.   public void setOptions(String[] options) throws Exception {
  506.     String expString = Utils.getOption('E', options);
  507.     if (expString.length() != 0) {
  508.       setExpression(expString);
  509.     } else {
  510.       throw new Exception("Must specify an expression with the -E option");
  511.     }
  512.     String name = Utils.getOption('N',options);
  513.     if (name.length() != 0) {
  514.       setName(name);
  515.     }
  516.     setDebug(Utils.getFlag('D', options));
  517.   }
  518.   
  519.   /**
  520.    * Gets the current settings of the filter.
  521.    *
  522.    * @return an array of strings suitable for passing to setOptions
  523.    */
  524.   public String [] getOptions() {
  525.     String [] options = new String [5];
  526.     int current = 0;
  527.     
  528.     options[current++] = "-E"; options[current++] = getExpression();
  529.     options[current++] = "-N"; options[current++] = getName();
  530.     if (getDebug()) {
  531.       options[current++] = "-D";
  532.     }
  533.     
  534.     while (current < options.length) {
  535.       options[current++] = "";
  536.     }
  537.     return options;
  538.   }
  539.   /**
  540.    * Returns the tip text for this property
  541.    *
  542.    * @return tip text for this property suitable for
  543.    * displaying in the explorer/experimenter gui
  544.    */
  545.   public String nameTipText() {
  546.     return "Set the name of the new attribute.";
  547.   }
  548.   /**
  549.    * Set the name for the new attribute. The string "expression" can
  550.    * be used to make the name of the new attribute equal to the expression
  551.    * provided.
  552.    * @param name the name of the new attribute
  553.    */
  554.   public void setName(String name) {
  555.     m_attributeName = name;
  556.   }
  557.   /**
  558.    * Returns the name of the new attribute
  559.    * @return the name of the new attribute
  560.    */
  561.   public String getName() {
  562.     return m_attributeName;
  563.   }
  564.   /**
  565.    * Returns the tip text for this property
  566.    *
  567.    * @return tip text for this property suitable for
  568.    * displaying in the explorer/experimenter gui
  569.    */
  570.   public String debugTipText() {
  571.     return "Set debug mode. If true then the new attribute will be named with "
  572.       +"the postfix parse of the supplied expression.";
  573.   }
  574.   
  575.   /**
  576.    * Set debug mode. Causes the new attribute to be named with the postfix
  577.    * parse of the expression
  578.    * @param d true if debug mode is to be used
  579.    */
  580.   public void setDebug(boolean d) {
  581.     m_Debug = d;
  582.   }
  583.   /**
  584.    * Gets whether debug is set
  585.    * @return true if debug is set
  586.    */
  587.   public boolean getDebug() {
  588.     return m_Debug;
  589.   }
  590.   /**
  591.    * Returns the tip text for this property
  592.    *
  593.    * @return tip text for this property suitable for
  594.    * displaying in the explorer/experimenter gui
  595.    */
  596.   public String expressionTipText() {
  597.     return "Set the math expression to apply. Eg. a1^2*a5/log(a7*4.0)";
  598.   }
  599.   /**
  600.    * Set the expression to apply
  601.    * @param expr a mathematical expression to apply
  602.    */
  603.   public void setExpression(String expr) {
  604.     m_infixExpression = expr;
  605.   }
  606.   /**
  607.    * Get the expression
  608.    * @return the expression
  609.    */
  610.   public String getExpression() {
  611.     return m_infixExpression;
  612.   }
  613.   /**
  614.    * Sets the format of the input instances.
  615.    *
  616.    * @param instanceInfo an Instances object containing the input instance
  617.    * structure (any instances contained in the object are ignored - only the
  618.    * structure is required).
  619.    * @return true if the outputFormat may be collected immediately
  620.    * @exception Exception if the format couldn't be set successfully
  621.    */
  622.   public boolean setInputFormat(Instances instanceInfo) throws Exception {
  623.     convertInfixToPostfix(new String(m_infixExpression));
  624.     super.setInputFormat(instanceInfo);
  625.     Instances outputFormat = new Instances(instanceInfo, 0);
  626.     Attribute newAttribute;
  627.     if (m_Debug) {
  628.       newAttribute = new Attribute(m_postFixExpVector.toString());
  629.     } else if (m_attributeName.compareTo("expression") != 0) {
  630.       newAttribute = new Attribute(m_attributeName);
  631.     } else {
  632.       newAttribute = new Attribute(m_infixExpression);
  633.     }
  634.     outputFormat.insertAttributeAt(newAttribute, 
  635.    instanceInfo.numAttributes());
  636.     setOutputFormat(outputFormat);
  637.     return true;
  638.   }
  639.   /**
  640.    * Input an instance for filtering. Ordinarily the instance is processed
  641.    * and made available for output immediately. Some filters require all
  642.    * instances be read before producing output.
  643.    *
  644.    * @param instance the input instance
  645.    * @return true if the filtered instance may now be
  646.    * collected with output().
  647.    * @exception IllegalStateException if no input format has been defined.
  648.    * @exception Exception if there was a problem during the filtering.
  649.    */
  650.   public boolean input(Instance instance) throws Exception {
  651.     if (getInputFormat() == null) {
  652.       throw new IllegalStateException("No input instance format defined");
  653.     }
  654.     if (m_NewBatch) {
  655.       resetQueue();
  656.       m_NewBatch = false;
  657.     }
  658.     double[] vals = new double[instance.numAttributes()+1];
  659.     for(int i = 0; i < instance.numAttributes(); i++) {
  660.       if (instance.isMissing(i)) {
  661. vals[i] = Instance.missingValue();
  662.       } else {
  663. vals[i] = instance.value(i);
  664.       }
  665.     }
  666.     evaluateExpression(vals);
  667.     Instance inst = null;
  668.     if (instance instanceof SparseInstance) {
  669.       inst = new SparseInstance(instance.weight(), vals);
  670.     } else {
  671.       inst = new Instance(instance.weight(), vals);
  672.     }
  673.     copyStringValues(inst, false, instance.dataset(), getOutputFormat());
  674.     inst.setDataset(getOutputFormat());
  675.     push(inst);
  676.     return true;
  677.   }
  678.   
  679.   /**
  680.    * Main method for testing this class.
  681.    *
  682.    * @param args should contain arguments to the filter: use -h for help
  683.    */
  684.   public static void main(String [] args) {
  685.     try {
  686.       if (Utils.getFlag('b', args)) {
  687. Filter.batchFilterFile(new AddExpression(), args);
  688.       } else {
  689. Filter.filterFile(new AddExpression(), args);
  690.       }
  691.     } catch (Exception ex) {
  692.       System.out.println(ex.getMessage());
  693.     }
  694.   }
  695. }