/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program 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.
*
* This program 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 this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.operator.generator;
import java.util.List;
import com.rapidminer.example.Attribute;
import com.rapidminer.example.Attributes;
import com.rapidminer.example.ExampleSet;
import com.rapidminer.example.utils.ExampleSets;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.OperatorVersion;
import com.rapidminer.operator.ProcessSetupError.Severity;
import com.rapidminer.operator.SimpleProcessSetupError;
import com.rapidminer.operator.UserError;
import com.rapidminer.operator.error.AttributeNotFoundError;
import com.rapidminer.operator.io.AbstractExampleSource;
import com.rapidminer.operator.ports.OutputPort;
import com.rapidminer.operator.ports.metadata.AttributeMetaData;
import com.rapidminer.operator.ports.metadata.ExampleSetMetaData;
import com.rapidminer.operator.ports.metadata.MetaData;
import com.rapidminer.operator.ports.metadata.SimpleMetaDataError;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeExpression;
import com.rapidminer.parameter.ParameterTypeExpression.OperatorVersionCallable;
import com.rapidminer.parameter.ParameterTypeList;
import com.rapidminer.parameter.ParameterTypeString;
import com.rapidminer.parameter.ParameterTypeStringCategory;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.tools.Ontology;
import com.rapidminer.tools.expression.ExampleResolver;
import com.rapidminer.tools.expression.Expression;
import com.rapidminer.tools.expression.ExpressionException;
import com.rapidminer.tools.expression.ExpressionParser;
import com.rapidminer.tools.expression.internal.ExpressionParserUtils;
import com.rapidminer.tools.expression.internal.UnknownResolverVariableException;
/**
* This operator produces an {@link ExampleSet} containing only one example whose Attribute values
* are derived from a user specified list.
*
* @author Sebastian Land
*/
public class UserSpecificationDataGenerator extends AbstractExampleSource {
public static final String PARAMETER_VALUES = "attribute_values";
public static final String PARAMETER_ATTRIBUTE_NAME = "attribute_name";
public static final String PARAMETER_ATTRIBUTE_VALUE = "attribute_value";
/**
* The parameter name for "The name of the attribute of which the type should be
* changed."
*/
public static final String PARAMETER_NAME = "name";
/**
* The parameter name for "The target type of the attribute (only changed if parameter
* change_attribute_type is true)."
*/
public static final String PARAMETER_TARGET_ROLE = "target_role";
public static final String PARAMETER_ROLES = "set_additional_roles";
private static final String REGULAR_NAME = "regular";
private static final String[] TARGET_ROLES = new String[] { REGULAR_NAME, Attributes.ID_NAME, Attributes.LABEL_NAME,
Attributes.PREDICTION_NAME, Attributes.CLUSTER_NAME, Attributes.WEIGHT_NAME, Attributes.BATCH_NAME };
public UserSpecificationDataGenerator(OperatorDescription description) {
super(description);
}
@Override
public MetaData getGeneratedMetaData() throws OperatorException {
ExampleSetMetaData emd = new ExampleSetMetaData();
emd.setNumberOfExamples(1);
emd.attributesAreKnown();
ExampleResolver exampleResolver = new ExampleResolver(emd);
ExpressionParser expParser = ExpressionParserUtils.createAllModulesParser(this, exampleResolver);
try {
for (String[] nameFunctionPair : getParameterList(PARAMETER_VALUES)) {
String name = nameFunctionPair[0];
String function = nameFunctionPair[1];
try {
Expression parsedExp = expParser.parse(function);
// generate AttributeMetaData and add it to the example resolver and ExampleSet
// meta data
AttributeMetaData amd = ExpressionParserUtils.generateAttributeMetaData(emd, name,
parsedExp.getExpressionType());
exampleResolver.addAttributeMetaData(amd);
emd.addAttribute(amd);
} catch (ExpressionException e) {
OutputPort portByIndex = this.getOutputPorts().getPortByIndex(0);
if (e.getCause() != null && e.getCause() instanceof UnknownResolverVariableException) {
// in case a resolver variable cannot be resolved, return a new attribute
// with nominal type
emd.addAttribute(new AttributeMetaData(name, Ontology.NOMINAL));
} else {
// in all other cases abort meta data generation, add an error and return
// empty meta data
portByIndex.addError(new SimpleMetaDataError(Severity.ERROR, portByIndex,
"cannot_create_exampleset_metadata", e.getShortMessage()));
return new ExampleSetMetaData();
}
}
}
// now proceed with Attribute rules
if (isParameterSet(PARAMETER_ROLES)) {
List<String[]> list = getParameterList(PARAMETER_ROLES);
for (String[] pairs : list) {
setRoleMetaData(emd, pairs[0], pairs[1]);
}
}
} catch (UndefinedParameterError e) {
}
return emd;
}
private void setRoleMetaData(ExampleSetMetaData metaData, String name, String targetRole) {
AttributeMetaData amd = metaData.getAttributeByName(name);
if (amd != null) {
if (targetRole != null) {
if (REGULAR_NAME.equals(targetRole)) {
amd.setRegular();
} else {
AttributeMetaData oldRole = metaData.getAttributeByRole(targetRole);
if (oldRole != null && oldRole != amd) {
addError(new SimpleProcessSetupError(Severity.WARNING, this.getPortOwner(), "already_contains_role",
targetRole));
metaData.removeAttribute(oldRole);
}
amd.setRole(targetRole);
}
}
}
}
@Override
public ExampleSet createExampleSet() throws OperatorException {
ExampleSet exampleSet = ExampleSets.from().withBlankSize(1).build();
List<String[]> paramValues = getParameterList(PARAMETER_VALUES);
// init operator progress
List<String[]> roleList = null;
if (isParameterSet(PARAMETER_ROLES)) {
roleList = getParameterList(PARAMETER_ROLES);
getProgress().setTotal(paramValues.size() + roleList.size());
} else {
getProgress().setTotal(paramValues.size());
}
// create resolver and parser
ExampleResolver resolver = new ExampleResolver(exampleSet);
ExpressionParser parser = ExpressionParserUtils.createAllModulesParser(this, resolver);
for (String[] nameFunctionPair : paramValues) {
String name = nameFunctionPair[0];
String function = nameFunctionPair[1];
try {
ExpressionParserUtils.addAttribute(exampleSet, name, function, parser, resolver, this);
} catch (ExpressionException e) {
throw ExpressionParserUtils.convertToUserError(this, function, e);
}
getProgress().step();
}
// now set roles
if (roleList != null) {
for (String[] pairs : roleList) {
setRole(exampleSet, pairs[0], pairs[1]);
getProgress().step();
}
}
getProgress().complete();
return exampleSet;
}
private void setRole(ExampleSet exampleSet, String name, String newRole) throws UserError {
Attribute attribute = exampleSet.getAttributes().get(name);
if (attribute == null) {
throw new AttributeNotFoundError(this, PARAMETER_ROLES, name);
}
exampleSet.getAttributes().remove(attribute);
if (newRole == null || newRole.trim().length() == 0) {
throw new UndefinedParameterError(PARAMETER_TARGET_ROLE, this);
}
if (newRole.equals(REGULAR_NAME)) {
exampleSet.getAttributes().addRegular(attribute);
} else {
exampleSet.getAttributes().setSpecialAttribute(attribute, newRole);
}
}
@Override
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
types.add(new ParameterTypeList(PARAMETER_VALUES,
"This parameter defines the attributes and their values in the single example returned.",
new ParameterTypeString(PARAMETER_ATTRIBUTE_NAME, "This is the name of the generated attribute.", false),
new ParameterTypeExpression(PARAMETER_ATTRIBUTE_VALUE,
"An expression that is parsed to derive the value of this attribute.",
new OperatorVersionCallable(this)),
false));
types.add(new ParameterTypeList(PARAMETER_ROLES, "This parameter defines additional attribute role combinations.",
new ParameterTypeString(PARAMETER_NAME, "The name of the attribute whose role should be changed.", false,
false),
new ParameterTypeStringCategory(PARAMETER_TARGET_ROLE,
"The target role of the attribute (only changed if parameter change_attribute_type is true).",
TARGET_ROLES, TARGET_ROLES[0]),
false));
return types;
}
@Override
public OperatorVersion[] getIncompatibleVersionChanges() {
return ExpressionParserUtils.addIncompatibleExpressionParserChange(super.getIncompatibleVersionChanges());
}
}