/**
* 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.example.table;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.rapidminer.example.AttributeTypeException;
import com.rapidminer.example.Example;
/**
* This is an efficient implementation of a {@link NominalMapping} which can be used for binominal
* attributes, i.e. for attributes with only two different values.
*
* @author Ingo Mierswa
*/
public class BinominalMapping implements NominalMapping {
private static final long serialVersionUID = 6566553739308153153L;
/** The index of the first value. */
private static final int FIRST_VALUE_INDEX = 0;
/** The index of the second value. */
private static final int SECOND_VALUE_INDEX = 1;
/**
* Nominal index of the value that will be treated as the "positive" value of this attribute.
*/
public static final int POSITIVE_INDEX = SECOND_VALUE_INDEX;
/**
* Nominal index of the value that will be treated as the "negative" value of this attribute.
*/
public static final int NEGATIVE_INDEX = FIRST_VALUE_INDEX;
/** The first nominal value. */
private String firstValue = null;
/** The second nominal value. */
private String secondValue = null;
public BinominalMapping() {}
/** Clone constructor. */
private BinominalMapping(BinominalMapping mapping) {
this.firstValue = mapping.firstValue;
this.secondValue = mapping.secondValue;
}
/** Clone constructor. */
/* pp */ BinominalMapping(NominalMapping mapping) {
if (mapping.size() > 0) {
firstValue = mapping.mapIndex(0);
}
if (mapping.size() > 1) {
secondValue = mapping.mapIndex(1);
}
}
@Override
public Object clone() {
return new BinominalMapping(this);
}
@Override
public boolean equals(NominalMapping mapping) {
if (mapping.size() != size()) {
return false;
}
for (String value : mapping.getValues()) {
if (!value.equals(firstValue) && !value.equals(secondValue)) {
return false;
}
}
return true;
}
/**
* Returns the index for the nominal attribute value <code>str</code>. If the string is unknown,
* a new index value is assigned. Returns -1, if str is null.
*/
@Override
public int mapString(String str) {
if (str == null) {
return -1;
}
// lookup string
int index = getIndex(str);
if (index < 0) {
// if string is not found, set it
if (firstValue == null) {
firstValue = str;
return FIRST_VALUE_INDEX;
} else if (secondValue == null) {
secondValue = str;
return SECOND_VALUE_INDEX;
} else {
throw new AttributeTypeException(
"Cannot map another string for binary attribute: already mapped two strings (" + firstValue + ", "
+ secondValue + "). The third string that was tried to add: '" + str + "'");
}
} else {
return index;
}
}
/**
* Returns the index of the given nominal value or -1 if this value was not mapped before by
* invoking the method {@link #mapIndex(int)}.
*/
@Override
public int getIndex(String str) {
if (str.equals(firstValue)) {
return FIRST_VALUE_INDEX;
} else if (str.equals(secondValue)) {
return SECOND_VALUE_INDEX;
} else {
return -1;
}
}
/**
* Returns the attribute value, that is associated with this index. Index counting starts with
* 0. <b>WARNING:</b> In order to iterate over all values please use the collection returned by
* {@link #getValues()}.
*/
@Override
public String mapIndex(int index) {
switch (index) {
case FIRST_VALUE_INDEX:
return firstValue;
case SECOND_VALUE_INDEX:
return secondValue;
default:
throw new AttributeTypeException(
"Cannot map index of binary attribute to nominal value: index " + index + " is out of bounds!");
}
}
/**
* Sets the given mapping. Please note that this will overwrite existing mappings and might
* cause data changes in this way.
*/
@Override
public void setMapping(String nominalValue, int index) {
if (index == FIRST_VALUE_INDEX) {
firstValue = nominalValue;
} else if (index == SECOND_VALUE_INDEX) {
secondValue = nominalValue;
} else {
throw new AttributeTypeException("Cannot set mapping of binary attribute to index '" + index + "'.");
}
}
/**
* Returns the index of the first value if this attribute is a classification attribute, i.e. if
* it is binominal.
*/
@Override
public int getNegativeIndex() {
return NEGATIVE_INDEX;
}
/**
* Returns the index of the second value if this attribute is a classification attribute. Works
* for all binominal attributes.
*/
@Override
public int getPositiveIndex() {
return POSITIVE_INDEX;
}
@Override
public String getNegativeString() {
return firstValue;
}
@Override
public String getPositiveString() {
return secondValue;
}
/** Returns the values of the attribute as an enumeration of strings. */
@Override
public List<String> getValues() {
if (firstValue == null) {
return Collections.emptyList();
} else if (secondValue == null) {
return Arrays.asList(firstValue);
} else {
return Arrays.asList(firstValue, secondValue);
}
}
/** Returns the number of different nominal values. */
@Override
public int size() {
if (firstValue == null) {
return 0;
} else if (secondValue == null) {
return 1;
} else {
return 2;
}
}
/**
* This method rearranges the string to number mappings such that they are in alphabetical
* order. <br>
* <b>VERY IMPORTANT NOTE:</b> Do not call this method when this attribute is already associated
* with an {@link AbstractExampleTable} and it already contains {@link Example}s. All examples
* will be messed up!
*/
@Override
public void sortMappings() {
if (size() == 2) {
if (firstValue.compareTo(secondValue) > 0) {
String dummy = secondValue;
secondValue = firstValue;
firstValue = dummy;
}
}
}
/** Clears all mappings for nominal values. */
@Override
public void clear() {
firstValue = null;
secondValue = null;
}
}