
org.jpmml.evaluator.Functions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pmml-evaluator Show documentation
Show all versions of pmml-evaluator Show documentation
JPMML class model evaluator
/*
* Copyright (c) 2015 Villu Ruusmann
*
* This file is part of JPMML-Evaluator
*
* JPMML-Evaluator is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JPMML-Evaluator 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with JPMML-Evaluator. If not, see .
*/
package org.jpmml.evaluator;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import org.apache.commons.math3.stat.descriptive.rank.Max;
import org.apache.commons.math3.stat.descriptive.rank.Min;
import org.apache.commons.math3.stat.descriptive.summary.Product;
import org.apache.commons.math3.stat.descriptive.summary.Sum;
import org.dmg.pmml.DataType;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;
import org.joda.time.Seconds;
import org.jpmml.evaluator.functions.AbstractFunction;
import org.jpmml.evaluator.functions.AggregateFunction;
import org.jpmml.evaluator.functions.ArithmeticFunction;
import org.jpmml.evaluator.functions.BinaryBooleanFunction;
import org.jpmml.evaluator.functions.ComparisonFunction;
import org.jpmml.evaluator.functions.EqualityFunction;
import org.jpmml.evaluator.functions.FpMathFunction;
import org.jpmml.evaluator.functions.MathFunction;
import org.jpmml.evaluator.functions.StringFunction;
import org.jpmml.evaluator.functions.UnaryBooleanFunction;
import org.jpmml.evaluator.functions.ValueFunction;
import org.jpmml.evaluator.functions.ValueListFunction;
public class Functions {
private Functions(){
}
public static final ArithmeticFunction PLUS = new ArithmeticFunction("+"){
@Override
public Double evaluate(Number left, Number right){
return Double.valueOf(left.doubleValue() + right.doubleValue());
}
};
public static final ArithmeticFunction MINUS = new ArithmeticFunction("-"){
@Override
public Double evaluate(Number left, Number right){
return Double.valueOf(left.doubleValue() - right.doubleValue());
}
};
public static final ArithmeticFunction MULTIPLY = new ArithmeticFunction("*"){
@Override
public Double evaluate(Number left, Number right){
return Double.valueOf(left.doubleValue() * right.doubleValue());
}
};
public static final ArithmeticFunction DIVIDE = new ArithmeticFunction("/"){
@Override
public Number evaluate(Number left, Number right){
if(left instanceof Integer && right instanceof Integer){
return Integer.valueOf(left.intValue() / right.intValue());
}
return Double.valueOf(left.doubleValue() / right.doubleValue());
}
};
public static final AggregateFunction MIN = new AggregateFunction("min"){
@Override
public Min createStatistic(){
return new Min();
}
};
public static final AggregateFunction MAX = new AggregateFunction("max"){
@Override
public Max createStatistic(){
return new Max();
}
};
public static final AggregateFunction AVG = new AggregateFunction("avg"){
@Override
public Mean createStatistic(){
return new Mean();
}
@Override
public DataType getResultType(DataType dataType){
return integerToDouble(dataType);
}
};
public static final AggregateFunction SUM = new AggregateFunction("sum"){
@Override
public Sum createStatistic(){
return new Sum();
}
};
public static final AggregateFunction PRODUCT = new AggregateFunction("product"){
@Override
public Product createStatistic(){
return new Product();
}
};
public static final FpMathFunction LOG10 = new FpMathFunction("log10"){
@Override
public Double evaluate(Number value){
return Math.log10(value.doubleValue());
}
};
public static final FpMathFunction LN = new FpMathFunction("ln"){
@Override
public Double evaluate(Number value){
return Math.log(value.doubleValue());
}
};
public static final FpMathFunction EXP = new FpMathFunction("exp"){
@Override
public Double evaluate(Number value){
return Math.exp(value.doubleValue());
}
};
public static final FpMathFunction SQRT = new FpMathFunction("sqrt"){
@Override
public Double evaluate(Number value){
return Math.sqrt(value.doubleValue());
}
};
public static final MathFunction ABS = new MathFunction("abs"){
@Override
public Double evaluate(Number value){
return Math.abs(value.doubleValue());
}
};
public static final AbstractFunction POW = new AbstractFunction("pow"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
FieldValue left = arguments.get(0);
FieldValue right = arguments.get(1);
DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType());
Double result = Math.pow((left.asNumber()).doubleValue(), (right.asNumber()).doubleValue());
return FieldValueUtil.create(cast(dataType, result));
}
};
public static final AbstractFunction THRESHOLD = new AbstractFunction("threshold"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
FieldValue left = arguments.get(0);
FieldValue right = arguments.get(1);
DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType());
Integer result = ((left.asNumber()).doubleValue() > (right.asNumber()).doubleValue()) ? Values.INTEGER_ONE : Values.INTEGER_ZERO;
return FieldValueUtil.create(cast(dataType, result));
}
};
public static final MathFunction FLOOR = new MathFunction("floor"){
@Override
public Double evaluate(Number number){
return Math.floor(number.doubleValue());
}
};
public static final MathFunction CEIL = new MathFunction("ceil"){
@Override
public Double evaluate(Number number){
return Math.ceil(number.doubleValue());
}
};
public static final MathFunction ROUND = new MathFunction("round"){
@Override
public Double evaluate(Number number){
return (double)Math.round(number.doubleValue());
}
};
public static final ValueFunction IS_MISSING = new ValueFunction("isMissing"){
@Override
public Boolean evaluate(FieldValue value){
return Boolean.valueOf(value == null);
}
};
public static final ValueFunction IS_NOT_MISSING = new ValueFunction("isNotMissing"){
@Override
public Boolean evaluate(FieldValue value){
return Boolean.valueOf(value != null);
}
};
public static final EqualityFunction EQUAL = new EqualityFunction("equal"){
@Override
public Boolean evaluate(boolean equals){
return Boolean.valueOf(equals);
}
};
public static final EqualityFunction NOT_EQUAL = new EqualityFunction("notEqual"){
@Override
public Boolean evaluate(boolean equals){
return Boolean.valueOf(!equals);
}
};
public static final ComparisonFunction LESS_THAN = new ComparisonFunction("lessThan"){
@Override
public Boolean evaluate(int order){
return Boolean.valueOf(order < 0);
}
};
public static final ComparisonFunction LESS_OR_EQUAL = new ComparisonFunction("lessOrEqual"){
@Override
public Boolean evaluate(int order){
return Boolean.valueOf(order <= 0);
}
};
public static final ComparisonFunction GREATER_THAN = new ComparisonFunction("greaterThan"){
@Override
public Boolean evaluate(int order){
return Boolean.valueOf(order > 0);
}
};
public static final ComparisonFunction GREATER_OR_EQUAL = new ComparisonFunction("greaterOrEqual"){
@Override
public Boolean evaluate(int order){
return Boolean.valueOf(order >= 0);
}
};
public static final BinaryBooleanFunction AND = new BinaryBooleanFunction("and"){
@Override
public Boolean evaluate(Boolean left, Boolean right){
return Boolean.valueOf(left.booleanValue() & right.booleanValue());
}
};
public static final BinaryBooleanFunction OR = new BinaryBooleanFunction("or"){
@Override
public Boolean evaluate(Boolean left, Boolean right){
return Boolean.valueOf(left.booleanValue() | right.booleanValue());
}
};
public static final UnaryBooleanFunction NOT = new UnaryBooleanFunction("not"){
@Override
public Boolean evaluate(Boolean value){
return Boolean.valueOf(!value.booleanValue());
}
};
public static final ValueListFunction IS_IN = new ValueListFunction("isIn"){
@Override
public Boolean evaluate(int index){
return Boolean.valueOf(index >= 0);
}
};
public static final ValueListFunction IS_NOT_IN = new ValueListFunction("isNotIn"){
@Override
public Boolean evaluate(int index){
return Boolean.valueOf(index < 0);
}
};
public static final AbstractFunction IF = new AbstractFunction("if"){
@Override
public FieldValue evaluate(List arguments){
if((arguments.size() < 2 || arguments.size() > 3)){
throw new FunctionException(this, "Expected 2 or 3 arguments, but got " + arguments.size() + " arguments");
}
FieldValue flag = arguments.get(0);
if(flag == null){
throw new FunctionException(this, "Missing arguments");
} // End if
if(flag.asBoolean()){
FieldValue trueValue = arguments.get(1);
// "The THEN part is required"
if(trueValue == null){
throw new FunctionException(this, "Missing arguments");
}
return trueValue;
} else
{
FieldValue falseValue = (arguments.size() > 2 ? arguments.get(2) : null);
// "The ELSE part is optional. If the ELSE part is absent, then a missing value is returned"
if(falseValue == null){
return null;
}
return falseValue;
}
}
};
public static final StringFunction UPPERCASE = new StringFunction("uppercase"){
@Override
public String evaluate(String value){
return value.toUpperCase();
}
};
public static final StringFunction LOWERCASE = new StringFunction("lowercase"){
@Override
public String evaluate(String value){
return value.toLowerCase();
}
};
public static final AbstractFunction SUBSTRING = new AbstractFunction("substring"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 3);
String string = (arguments.get(0)).asString();
int position = (arguments.get(1)).asInteger();
if(position < 1){
throw new FunctionException(this, "Invalid position value " + position + ". Must be equal or greater than 1");
}
// "The first character of a string is located at position 1 (not position 0)"
int javaPosition = Math.min(position - 1, string.length());
int length = (arguments.get(2)).asInteger();
if(length < 0){
throw new FunctionException(this, "Invalid length value " + length);
}
int javaLength = Math.min(length, (string.length() - javaPosition));
// This expression must never throw a StringIndexOutOfBoundsException
String result = string.substring(javaPosition, javaPosition + javaLength);
return FieldValueUtil.create(result);
}
};
public static final StringFunction TRIM_BLANKS = new StringFunction("trimBlanks"){
@Override
public String evaluate(String value){
return value.trim();
}
};
public static final AbstractFunction CONCAT = new AbstractFunction("concat"){
@Override
public FieldValue evaluate(List arguments){
checkVariableArguments(arguments, 2, true);
StringBuilder sb = new StringBuilder();
Iterable values = Iterables.filter(arguments, Predicates.notNull());
for(FieldValue value : values){
String string = (String)TypeUtil.cast(DataType.STRING, value.getValue());
sb.append(string);
}
return FieldValueUtil.create(sb.toString());
}
};
public static final AbstractFunction REPLACE = new AbstractFunction("replace"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 3);
String input = (arguments.get(0)).asString();
String pattern = (arguments.get(1)).asString();
String replacement = (arguments.get(2)).asString();
Matcher matcher = Pattern.compile(pattern).matcher(input);
String result = matcher.replaceAll(replacement);
return FieldValueUtil.create(result);
}
};
public static final AbstractFunction MATCHES = new AbstractFunction("matches"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
String input = (arguments.get(0)).asString();
String pattern = (arguments.get(1)).asString();
Matcher matcher = Pattern.compile(pattern).matcher(input);
// "The string is considered to match the pattern if any substring matches the pattern"
Boolean result = Boolean.valueOf(matcher.find());
return FieldValueUtil.create(result);
}
};
public static final AbstractFunction FORMAT_NUMBER = new AbstractFunction("formatNumber"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
FieldValue value = arguments.get(0);
FieldValue pattern = arguments.get(1);
String result;
// According to the java.util.Formatter javadoc, Java formatting is more strict than C's printf formatting.
// For example, in Java, if a conversion is incompatible with a flag, an exception will be thrown. In C's printf, inapplicable flags are silently ignored.
try {
result = String.format(pattern.asString(), value.asNumber());
} catch(IllegalFormatException ife){
throw new FunctionException(this, formatMessage("Invalid format value \"" + pattern.asString() + "\"", ife));
}
return FieldValueUtil.create(result);
}
};
public static final AbstractFunction FORMAT_DATETIME = new AbstractFunction("formatDatetime"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
FieldValue value = arguments.get(0);
FieldValue pattern = arguments.get(1);
String result;
try {
result = String.format(translatePattern(pattern.asString()), (value.asDateTime()).toDate());
} catch(IllegalFormatException ife){
throw new FunctionException(this, formatMessage("Invalid format value \"" + pattern.asString() + "\"", ife));
}
return FieldValueUtil.create(result);
}
private String translatePattern(String pattern){
StringBuilder sb = new StringBuilder();
for(int i = 0; i < pattern.length(); i++){
char c = pattern.charAt(i);
sb.append(c);
if(c == '%'){
// Every %[conversion] has to become %1$t[conversion]
// Here, "1$" denotes the first argument, and "t" denotes the prefix for date and time conversion characters
if(i < (pattern.length() - 1) && pattern.charAt(i + 1) != '%'){
sb.append("1$t");
}
}
}
return sb.toString();
}
};
public static final AbstractFunction DATE_DAYS_SINCE_YEAR = new AbstractFunction("dateDaysSinceYear"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
LocalDate instant = (arguments.get(0)).asLocalDate();
int year = (arguments.get(1)).asInteger();
DaysSinceDate period = new DaysSinceDate(year, instant);
return FieldValueUtil.create(period.intValue());
}
};
public static final AbstractFunction DATE_SECONDS_SINCE_MIDNIGHT = new AbstractFunction("dateSecondsSinceMidnight"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 1);
LocalTime instant = (arguments.get(0)).asLocalTime();
Seconds seconds = Seconds.seconds(instant.getHourOfDay() * 60 * 60 + instant.getMinuteOfHour() * 60 + instant.getSecondOfMinute());
SecondsSinceMidnight period = new SecondsSinceMidnight(seconds);
return FieldValueUtil.create(period.intValue());
}
};
public static final AbstractFunction DATE_SECONDS_SINCE_YEAR = new AbstractFunction("dateSecondsSinceYear"){
@Override
public FieldValue evaluate(List arguments){
checkArguments(arguments, 2);
LocalDateTime instant = (arguments.get(0)).asLocalDateTime();
int year = (arguments.get(1)).asInteger();
SecondsSinceDate period = new SecondsSinceDate(year, instant);
return FieldValueUtil.create(period.intValue());
}
};
static
private String formatMessage(String message, Exception cause){
String causeMessage = cause.getMessage();
if(causeMessage != null){
message += " (" + causeMessage + ")";
}
return message;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy