/* * RapidMiner * * Copyright (C) 2001-2008 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.example.table; import java.util.Arrays; import java.util.LinkedList; 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 * @version $Id: BinominalMapping.java,v 1.3 2008/05/09 19:22:44 ingomierswa Exp $ */ 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; /** 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); } public Object clone() { return new BinominalMapping(this); } /** * 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. */ 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!"); } } 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)}. */ 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()}. */ 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. */ 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. */ public int getNegativeIndex() { return FIRST_VALUE_INDEX; } /** * Returns the index of the second value if this attribute is a * classification attribute. Works for all binominal attributes. */ public int getPositiveIndex() { return SECOND_VALUE_INDEX; } public String getNegativeString() { return firstValue; } public String getPositiveString() { return secondValue; } /** Returns the values of the attribute as an enumeration of strings. */ public List<String> getValues() { if (firstValue == null) return new LinkedList<String>(); else if (secondValue == null) return Arrays.asList(new String[] { firstValue }); else return Arrays.asList(new String[] { firstValue, secondValue }); } /** Returns the number of different nominal values. */ 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! */ public void sortMappings() { if (size() == 2) { if (firstValue.compareTo(secondValue) > 0) { String dummy = secondValue; secondValue = firstValue; firstValue = dummy; } } } /** Clears all mappings for nominal values. */ public void clear() { firstValue = null; secondValue = null; } }