/**
* 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.preprocessing.filter;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import com.rapidminer.example.Attribute;
import com.rapidminer.example.Attributes;
import com.rapidminer.example.Example;
import com.rapidminer.example.ExampleSet;
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.annotation.ResourceConsumptionEstimator;
import com.rapidminer.operator.error.AttributeNotFoundError;
import com.rapidminer.operator.ports.metadata.AttributeMetaData;
import com.rapidminer.operator.ports.metadata.AttributeSetPrecondition;
import com.rapidminer.operator.ports.metadata.ExampleSetMetaData;
import com.rapidminer.operator.ports.metadata.MetaData;
import com.rapidminer.operator.preprocessing.AbstractDataProcessing;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeAttribute;
import com.rapidminer.parameter.ParameterTypeBoolean;
import com.rapidminer.parameter.ParameterTypeInt;
import com.rapidminer.parameter.ParameterTypeList;
import com.rapidminer.parameter.ParameterTypeString;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.tools.OperatorResourceConsumptionHandler;
/**
* This operator simply sets the value for the specified example and attribute to the given value.
*
* If offers the possibility to define more than one attribute value pair.
*
*
* @author Ingo Mierswa, Sebastian Land
*/
public class SetData extends AbstractDataProcessing {
public static final String PARAMETER_ATTRIBUTE_NAME = "attribute_name";
public static final String PARAMETER_EXAMPLE_INDEX = "example_index";
public static final String PARAMETER_COUNT_BACKWARDS = "count_backwards";
public static final String PARAMETER_VALUE = "value";
public static final String PARAMETER_ADDITIONAL_VALUES = "additional_values";
/**
* Incompatible version, old version writes into the exampleset, if original output port is not
* connected.
*/
private static final OperatorVersion VERSION_MAY_WRITE_INTO_DATA = new OperatorVersion(7, 1, 1);
public SetData(OperatorDescription description) {
super(description);
getExampleSetInputPort().addPrecondition(
new AttributeSetPrecondition(getExampleSetInputPort(), AttributeSetPrecondition.getAttributesByParameter(
this, PARAMETER_ATTRIBUTE_NAME)));
}
@Override
protected MetaData modifyMetaData(ExampleSetMetaData metaData) throws UndefinedParameterError {
// performing meta data propagation for single attribute definition
if (isParameterSet(PARAMETER_VALUE) && isParameterSet(PARAMETER_ATTRIBUTE_NAME)) {
AttributeMetaData targetAttribute = metaData.getAttributeByName(getParameterAsString(PARAMETER_ATTRIBUTE_NAME));
setMetaData(getParameterAsString(PARAMETER_VALUE), targetAttribute, "parameter_must_be_numerical",
new Object[] { PARAMETER_VALUE });
}
// now doing same for all other defined values
List<String[]> list = getParameterList(PARAMETER_ADDITIONAL_VALUES);
for (String[] pair : list) {
AttributeMetaData targetAttribute = metaData.getAttributeByName(pair[0]);
setMetaData(pair[1], targetAttribute, "parameter_list_must_be_numerical",
new Object[] { PARAMETER_ADDITIONAL_VALUES });
}
return metaData;
}
private void setMetaData(String value, AttributeMetaData targetAttribute, String i18nCode, Object[] i18nArguments) {
if (targetAttribute != null) {
if (targetAttribute.isNominal()) {
targetAttribute.getValueSet().add(value);
} else {
try {
targetAttribute.getValueRange().add(Double.parseDouble(value));
} catch (NumberFormatException e) {
// add warning
addError(new SimpleProcessSetupError(Severity.WARNING, getPortOwner(), i18nCode, i18nArguments));
}
}
}
}
@Override
public ExampleSet apply(ExampleSet exampleSet) throws OperatorException {
Attributes attributes = exampleSet.getAttributes();
// searching example by index
int exampleIndex = getParameterAsInt(PARAMETER_EXAMPLE_INDEX);
if (exampleIndex == 0) {
throw new UserError(this, 207, new Object[] { "0", PARAMETER_EXAMPLE_INDEX,
"only positive or negative indices are allowed" });
}
if (getParameterAsBoolean(PARAMETER_COUNT_BACKWARDS)) {
exampleIndex = exampleSet.size() - exampleIndex;
} else {
exampleIndex--;
}
if (exampleIndex >= exampleSet.size()) {
throw new UserError(this, 110, exampleIndex);
}
Example example = exampleSet.getExample(exampleIndex);
// now set single value of first parameter
if (isParameterSet(PARAMETER_ATTRIBUTE_NAME) && isParameterSet(PARAMETER_VALUE)) {
String attributeName = getParameter(PARAMETER_ATTRIBUTE_NAME);
String value = getParameterAsString(PARAMETER_VALUE);
setData(example, attributeName, value, attributes, PARAMETER_ATTRIBUTE_NAME);
}
// now set each defined additional value.
List<String[]> list = getParameterList(PARAMETER_ADDITIONAL_VALUES);
for (String[] pair : list) {
setData(example, pair[0], pair[1], attributes, PARAMETER_ADDITIONAL_VALUES);
}
return exampleSet;
}
private void setData(Example example, String attributeName, String value, Attributes attributes, String paramKey)
throws UserError {
Attribute attribute = attributes.get(attributeName);
if (attribute == null) {
throw new AttributeNotFoundError(this, paramKey, attributeName);
}
if (attribute.isNominal()) {
example.setValue(attribute, attribute.getMapping().mapString(value));
} else {
try {
double doubleValue = Double.parseDouble(value);
example.setValue(attribute, doubleValue);
} catch (NumberFormatException e) {
throw new UserError(this, 211, PARAMETER_VALUE, value);
}
}
}
@Override
public List<ParameterType> getParameterTypes() {
List<ParameterType> types = super.getParameterTypes();
ParameterType type = new ParameterTypeInt(PARAMETER_EXAMPLE_INDEX,
"The index of the example for which the value should be set. Counting starts at 1.", 1, Integer.MAX_VALUE,
false);
type.setExpert(false);
types.add(type);
types.add(new ParameterTypeBoolean(
PARAMETER_COUNT_BACKWARDS,
"If checked, the last counting order is inverted and hence the last example is addressed by index 1, the before last by index 2 and so on.",
false, true));
types.add(new ParameterTypeAttribute(PARAMETER_ATTRIBUTE_NAME,
"The name of the attribute for which the value should be set.", getExampleSetInputPort(), true, false));
types.add(new ParameterTypeString(PARAMETER_VALUE, "The value which should be set.", true, false));
types.add(new ParameterTypeList(PARAMETER_ADDITIONAL_VALUES,
"This list allows to set additional values of the addressed example.", new ParameterTypeAttribute(
PARAMETER_ATTRIBUTE_NAME, "The name of the attribute for which the value should be set.",
getExampleSetInputPort(), true), new ParameterTypeString(PARAMETER_VALUE,
"The value which should be set.", true, false), false));
return types;
}
@Override
public boolean writesIntoExistingData() {
if (getCompatibilityLevel().isAbove(VERSION_MAY_WRITE_INTO_DATA)) {
return true;
} else {
// old version: true only if original output port is connected
return isOriginalOutputConnected();
}
}
@Override
public ResourceConsumptionEstimator getResourceConsumptionEstimator() {
return OperatorResourceConsumptionHandler.getResourceConsumptionEstimator(getInputPort(), SetData.class, null);
}
@Override
public OperatorVersion[] getIncompatibleVersionChanges() {
return (OperatorVersion[]) ArrayUtils.addAll(super.getIncompatibleVersionChanges(),
new OperatorVersion[] { VERSION_MAY_WRITE_INTO_DATA });
}
}