/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.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 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.ProcessSetupError.Severity;
import com.rapidminer.operator.SimpleProcessSetupError;
import com.rapidminer.operator.UserError;
import com.rapidminer.operator.annotation.ResourceConsumptionEstimator;
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";
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);
}
// now set each defined additional value.
List<String[]> list = getParameterList(PARAMETER_ADDITIONAL_VALUES);
for (String[] pair : list) {
setData(example, pair[0], pair[1], attributes);
}
return exampleSet;
}
private void setData(Example example, String attributeName, String value, Attributes attributes) throws UserError {
Attribute attribute = attributes.get(attributeName);
if (attribute == null) {
throw new UserError(this, 111, 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() {
return true;
}
@Override
public ResourceConsumptionEstimator getResourceConsumptionEstimator() {
return OperatorResourceConsumptionHandler.getResourceConsumptionEstimator(getInputPort(), SetData.class, null);
}
}