/*
* 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.ExampleSet;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.ProcessSetupError.Severity;
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.ports.metadata.SimpleMetaDataError;
import com.rapidminer.operator.preprocessing.AbstractDataProcessing;
import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeAttribute;
import com.rapidminer.parameter.ParameterTypeList;
import com.rapidminer.parameter.ParameterTypeStringCategory;
import com.rapidminer.parameter.UndefinedParameterError;
import com.rapidminer.tools.OperatorResourceConsumptionHandler;
/**
* <p>
* This operator can be used to change the attribute type of an attribute of the input example set.
* If you want to change the attribute name you should use the {@link ChangeAttributeName} operator.
* </p>
*
* <p>
* The target type indicates if the attribute is a regular attribute (used by learning operators) or a
* special attribute (e.g. a label or id attribute). The following target
* attribute types are possible:
* </p>
* <ul>
* <li>regular: only regular attributes are used as input variables for learning tasks</li>
* <li>id: the id attribute for the example set</li>
* <li>label: target attribute for learning</li>
* <li>prediction: predicted attribute, i.e. the predictions of a learning scheme</li>
* <li>cluster: indicates the membership to a cluster</li>
* <li>weight: indicates the weight of the example</li>
* <li>batch: indicates the membership to an example batch</li>
* </ul>
* <p>
* Users can also define own attribute types by simply using the desired name.
* </p>
*
* @author Ingo Mierswa
*/
public class ChangeAttributeRole extends AbstractDataProcessing {
/** 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_CHANGE_ATTRIBUTES = "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 ChangeAttributeRole(OperatorDescription description) {
super(description);
getExampleSetInputPort().addPrecondition(new AttributeSetPrecondition(getExampleSetInputPort(), AttributeSetPrecondition.getAttributesByParameter(this, PARAMETER_NAME)));
getExampleSetInputPort().addPrecondition(new AttributeSetPrecondition(getExampleSetInputPort(), AttributeSetPrecondition.getAttributesByParameterListEntry(this, PARAMETER_CHANGE_ATTRIBUTES, 0)));
}
@Override
protected MetaData modifyMetaData(ExampleSetMetaData metaData) {
try {
String targetRole = null;
if (isParameterSet(PARAMETER_TARGET_ROLE))
targetRole = getParameterAsString(PARAMETER_TARGET_ROLE);
if (isParameterSet(PARAMETER_NAME)) {
String name = getParameter(PARAMETER_NAME);
setRoleMetaData(metaData, name, targetRole);
}
// now proceed with list
if (isParameterSet(PARAMETER_CHANGE_ATTRIBUTES)) {
List<String[]> list = getParameterList(PARAMETER_CHANGE_ATTRIBUTES);
for (String[] pairs: list) {
setRoleMetaData(metaData, pairs[0], pairs[1]);
}
}
} catch (UndefinedParameterError e) {
}
return metaData;
}
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) {
getInputPort().addError(new SimpleMetaDataError(Severity.WARNING, getInputPort(), "already_contains_role", targetRole));
metaData.removeAttribute(oldRole);
}
amd.setRole(targetRole);
}
}
}
}
@Override
public ExampleSet apply(ExampleSet exampleSet) throws OperatorException {
String name = getParameterAsString(PARAMETER_NAME);
String newRole = getParameterAsString(PARAMETER_TARGET_ROLE);
setRole(exampleSet, name, newRole);
// now do the list
if (isParameterSet(PARAMETER_CHANGE_ATTRIBUTES)) {
List<String[]> list = getParameterList(PARAMETER_CHANGE_ATTRIBUTES);
for (String[] pairs: list) {
setRole(exampleSet, pairs[0], pairs[1]);
}
}
return exampleSet;
}
private void setRole(ExampleSet exampleSet, String name, String newRole) throws UserError {
Attribute attribute = exampleSet.getAttributes().get(name);
if (attribute == null) {
throw new UserError(this, 111, name);
}
exampleSet.getAttributes().remove(attribute);
if ((newRole == null) || (newRole.trim().length() == 0))
throw new UserError(this, 205, PARAMETER_TARGET_ROLE);
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 ParameterTypeAttribute(PARAMETER_NAME, "The name of the attribute whose role should be changed.", getExampleSetInputPort(), false, false));
ParameterType type = 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]);
type.setExpert(false);
types.add(type);
types.add(new ParameterTypeList(PARAMETER_CHANGE_ATTRIBUTES, "This parameter defines additional attribute role combinations.",
new ParameterTypeAttribute(PARAMETER_NAME, "The name of the attribute whose role should be changed.", getExampleSetInputPort(), 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 boolean writesIntoExistingData() {
return false;
}
@Override
public ResourceConsumptionEstimator getResourceConsumptionEstimator() {
return OperatorResourceConsumptionHandler.getResourceConsumptionEstimator(getInputPort(), ChangeAttributeRole.class, null);
}
}