📄 attributeexpressionfilter.java
字号:
/**
*
* AgentAcademy - an open source Data Mining framework for
* training intelligent agents
*
* Copyright (C) 2001-2003 AA Consortium.
*
* This library is open source software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation;
* either version 2.0 of the License, or (at your option) any later
* version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*/
package org.agentacademy.modules.dataminer.filters;
/**
* <p>Title: The Data Miner prototype</p>
* <p>Description: A prototype for the DataMiner (DM), the Agent Academy (AA) module responsible for performing data mining on the contents of the Agent Use Repository (AUR). The extracted knowledge is to be sent back to the AUR in the form of a PMML document.</p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: CERTH</p>
* @author asymeon
* @version 0.3
*/
import java.io.*;
import java.util.StringTokenizer;
import java.util.Stack;
import java.util.Vector;
import java.util.Enumeration;
import org.agentacademy.modules.dataminer.core.*;
/**
* Applys a mathematical expression involving attributes and numeric
* constants to a dataset. A new attribute is appended after the last
* attribute that contains the result of applying the expression.
* Supported operators are: +, -, *, /, ^, log, abs, cos, exp, sqrt,
* floor, ceil, rint, tan, sin, (, ). Attributes are specified
* by prefixing with 'a', eg. a7 is attribute number 7 (starting from 1). <p>
*
* Valid filter-specific options are:<p>
*
* -E expression <br>
* Specify the expression to apply. Eg. a1^2*a5/log(a7*4.0). <p>
*
* -N name <br>
* Specify a name for the new attribute. Default is to name it with the
* expression provided with the -E option. <p>
*
* -D <br>
* Debug. Names the attribute with the postfix parse of the expression. <p>
*
*/
public class AttributeExpressionFilter extends Filter
implements OptionHandler {
/**
* Inner class handling an attribute index as an operand
*/
private class AttributeOperand implements Serializable {
/** the index of the attribute */
protected int m_attributeIndex;
/** true if the value of the attribute are to be multiplied by -1 */
protected boolean m_negative;
public AttributeOperand(String operand, boolean sign) throws Exception {
// strip the leading 'a' and set the index
m_attributeIndex = (Integer.parseInt(operand.substring(1)))-1;
m_negative = sign;
}
/**
* Return a string describing this object
* @return a string descibing the attribute operand
*/
public String toString() {
String result = "";
if (m_negative) {
result += '-';
}
return result+"a"+(m_attributeIndex+1);
}
}
/**
* Inner class for storing numeric constant opperands
*/
private class NumericOperand implements Serializable {
/** numeric constant */
protected double m_numericConst;
public NumericOperand(String operand, boolean sign) throws Exception {
m_numericConst = Double.valueOf(operand).doubleValue();
if (sign) {
m_numericConst *= -1.0;
}
}
/**
* Return a string describing this object
* @return a string descibing the numeric operand
*/
public String toString() {
return ""+m_numericConst;
}
}
/**
* Inner class for storing operators
*/
private class Operator implements Serializable {
/** the operator */
protected char m_operator;
public Operator(char opp) throws IllegalArgumentException {
if (!isOperator(opp)) {
throw new IllegalArgumentException("Unrecognized operator:" + opp);
}
m_operator = opp;
}
/**
* Apply this operator to the supplied arguments
* @param first the first argument
* @param second the second argument
* @return the result
*/
protected double applyOperator(double first, double second) {
switch (m_operator) {
case '+' :
return (first+second);
case '-' :
return (first-second);
case '*' :
return (first*second);
case '/' :
return (first/second);
case '^' :
return Math.pow(first,second);
}
return Double.NaN;
}
/**
* Apply this operator (function) to the supplied argument
* @param value the argument
* @return the result
*/
protected double applyFunction(double value) {
switch (m_operator) {
case 'l' :
return Math.log(value);
case 'b' :
return Math.abs(value);
case 'c' :
return Math.cos(value);
case 'e' :
return Math.exp(value);
case 's' :
return Math.sqrt(value);
case 'f' :
return Math.floor(value);
case 'h' :
return Math.ceil(value);
case 'r' :
return Math.rint(value);
case 't' :
return Math.tan(value);
case 'n' :
return Math.sin(value);
}
return Double.NaN;
}
/**
* Return a string describing this object
* @return a string descibing the operator
*/
public String toString() {
return ""+m_operator;
}
}
/** The infix expression */
private String m_infixExpression = "a1^2";
/** Operator stack */
private Stack m_operatorStack = new Stack();
/** Supported operators. l = log, b = abs, c = cos, e = exp, s = sqrt,
f = floor, h = ceil, r = rint, t = tan, n = sin */
private static final String OPERATORS = "+-*/()^lbcesfhrtn";
private static final String UNARY_FUNCTIONS = "lbcesfhrtn";
/** Holds the expression in postfix form */
private Vector m_postFixExpVector;
/** True if the next numeric constant or attribute index is negative */
private boolean m_signMod = false;
/** Holds the previous token */
private String m_previousTok = "";
/** Name of the new attribute. "expression" length string will use the
provided expression as the new attribute name */
private String m_attributeName="expression";
/** If true, makes the attribute name equal to the postfix parse of the
expression */
private boolean m_Debug = false;
/**
* Returns a string describing this filter
*
* @return a description of the filter suitable for
* displaying in the explorer/experimenter gui
*/
public String globalInfo() {
return "An instance filter that creates a new attribute by applying a "
+"mathematical expression to existing attributes. The expression "
+"can contain attribute references and numeric constants. Supported "
+"opperators are : +, -, *, /, ^, log, abs, cos, exp, sqrt, "
+"floor, ceil, rint, tan, sin, (, ). Attributes are specified "
+"by prefixing with 'a', eg. a7 is attribute number 7 (starting from 1)."
+" Example expression : a1^2*a5/log(a7*4.0).";
}
/**
* Handles the processing of an infix operand to postfix
* @param tok the infix operand
* @exception Exception if there is difficulty parsing the operand
*/
private void handleOperand(String tok) throws Exception {
// if it contains an 'a' then its an attribute index
if (tok.indexOf('a') != -1) {
m_postFixExpVector.addElement(new AttributeOperand(tok,m_signMod));
} else {
try {
// should be a numeric constant
m_postFixExpVector.addElement(new NumericOperand(tok, m_signMod));
} catch (NumberFormatException ne) {
throw new Exception("Trouble parsing numeric constant");
}
}
m_signMod = false;
}
/**
* Handles the processing of an infix operator to postfix
* @param tok the infix operator
* @exception Exception if there is difficulty parsing the operator
*/
private void handleOperator(String tok) throws Exception {
boolean push = true;
char tokchar = tok.charAt(0);
if (tokchar == ')') {
String popop = " ";
do {
popop = (String)(m_operatorStack.pop());
if (popop.charAt(0) != '(') {
m_postFixExpVector.addElement(new Operator(popop.charAt(0)));
}
} while (popop.charAt(0) != '(');
} else {
int infixToc = infixPriority(tok.charAt(0));
while (!m_operatorStack.empty() &&
stackPriority(((String)(m_operatorStack.peek())).charAt(0))
>= infixToc) {
// try an catch double operators and see if the current one can
// be interpreted as the sign of an upcoming number
if (m_previousTok.length() == 1 &&
isOperator(m_previousTok.charAt(0)) &&
m_previousTok.charAt(0) != ')') {
if (tok.charAt(0) == '-') {
m_signMod = true;
} else {
m_signMod = false;
}
push = false;
break;
} else {
String popop = (String)(m_operatorStack.pop());
m_postFixExpVector.addElement(new Operator(popop.charAt(0)));
}
}
if (m_postFixExpVector.size() == 0) {
if (tok.charAt(0) == '-') {
m_signMod = true;
push = false;
}
}
if (push) {
m_operatorStack.push(tok);
}
}
}
/**
* Converts a string containing a mathematical expression in infix form
* to postfix form. The result is stored in the vector m_postfixExpVector
* @param infixExp the infix expression to convert
* @exception Exception if something goes wrong during the conversion
*/
private void convertInfixToPostfix(String infixExp) throws Exception {
infixExp = Utils.removeSubstring(infixExp, " ");
infixExp = Utils.replaceSubstring(infixExp,"log","l");
infixExp = Utils.replaceSubstring(infixExp,"abs","b");
infixExp = Utils.replaceSubstring(infixExp,"cos","c");
infixExp = Utils.replaceSubstring(infixExp,"exp","e");
infixExp = Utils.replaceSubstring(infixExp,"sqrt","s");
infixExp = Utils.replaceSubstring(infixExp,"floor","f");
infixExp = Utils.replaceSubstring(infixExp,"ceil","h");
infixExp = Utils.replaceSubstring(infixExp,"rint","r");
infixExp = Utils.replaceSubstring(infixExp,"tan","t");
infixExp = Utils.replaceSubstring(infixExp,"sin","n");
StringTokenizer tokenizer = new StringTokenizer(infixExp, OPERATORS, true);
m_postFixExpVector = new Vector();
while (tokenizer.hasMoreTokens()) {
String tok = tokenizer.nextToken();
if (tok.length() > 1) {
handleOperand(tok);
} else {
// probably an operator, but could be a single char operand
if (isOperator(tok.charAt(0))) {
handleOperator(tok);
} else {
// should be a numeric constant
handleOperand(tok);
}
}
m_previousTok = tok;
}
while (!m_operatorStack.empty()) {
String popop = (String)(m_operatorStack.pop());
if (popop.charAt(0) == '(' || popop.charAt(0) == ')') {
throw new Exception("Mis-matched parenthesis!");
}
m_postFixExpVector.addElement(new Operator(popop.charAt(0)));
}
}
/**
* Evaluate the expression using the supplied array of attribute values.
* The result is stored in the last element of the array. Assumes that
* the infix expression has been converted to postfix and stored in
* m_postFixExpVector
* @param vals the values to apply the expression to
* @exception Exception if something goes wrong
*/
private void evaluateExpression(double [] vals) throws Exception {
Stack operands = new Stack();
for (int i=0;i<m_postFixExpVector.size();i++) {
Object nextob = m_postFixExpVector.elementAt(i);
if (nextob instanceof NumericOperand) {
operands.push(new Double(((NumericOperand)nextob).m_numericConst));
} else if (nextob instanceof AttributeOperand) {
double value = vals[((AttributeOperand)nextob).m_attributeIndex];
if (value == Instance.missingValue()) {
vals[vals.length-1] = Instance.missingValue();
break;
}
if (((AttributeOperand)nextob).m_negative) {
value = -value;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -