Attribute.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 19k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    Attribute.java
  18.  *    Copyright (C) 1999 Eibe Frank
  19.  *
  20.  */
  21. package weka.core;
  22. import java.io.*;
  23. import java.util.*;
  24. /** 
  25.  * Class for handling an attribute. Once an attribute has been created,
  26.  * it can't be changed. <p>
  27.  *
  28.  * Three attribute types are supported:
  29.  * <ul>
  30.  *    <li> numeric: <ul>
  31.  *         This type of attribute represents a floating-point number.
  32.  *    </ul>
  33.  *    <li> nominal: <ul>
  34.  *         This type of attribute represents a fixed set of nominal values.
  35.  *    </ul>
  36.  *    <li> string: <ul>
  37.  *         This type of attribute represents a dynamically expanding set of
  38.  *         nominal values. String attributes are not used by the learning
  39.  *         schemes in Weka. They can be used, for example,  to store an 
  40.  *         identifier with each instance in a dataset.
  41.  *    </ul>
  42.  * </ul>
  43.  * Typical usage (code from the main() method of this class): <p>
  44.  *
  45.  * <code>
  46.  * ... <br>
  47.  *
  48.  * // Create numeric attributes "length" and "weight" <br>
  49.  * Attribute length = new Attribute("length"); <br>
  50.  * Attribute weight = new Attribute("weight"); <br><br>
  51.  * 
  52.  * // Create vector to hold nominal values "first", "second", "third" <br>
  53.  * FastVector my_nominal_values = new FastVector(3); <br>
  54.  * my_nominal_values.addElement("first"); <br>
  55.  * my_nominal_values.addElement("second"); <br>
  56.  * my_nominal_values.addElement("third"); <br><br>
  57.  *
  58.  * // Create nominal attribute "position" <br>
  59.  * Attribute position = new Attribute("position", my_nominal_values);<br>
  60.  *
  61.  * ... <br>
  62.  * </code><p>
  63.  *
  64.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  65.  * @version $Revision: 1.19.2.3 $
  66.  */
  67. public class Attribute implements Copyable, Serializable {
  68.   /** Constant set for numeric attributes. */
  69.   public final static int NUMERIC = 0;
  70.   /** Constant set for nominal attributes. */
  71.   public final static int NOMINAL = 1;
  72.   /** Constant set for attributes with string values. */
  73.   public final static int STRING = 2;
  74.   /** Strings longer than this will be stored compressed. */
  75.   private final static int STRING_COMPRESS_THRESHOLD = 200;
  76.   /** The attribute's name. */
  77.   private String m_Name;
  78.   /** The attribute's type. */
  79.   private int m_Type;
  80.   /** The attribute's values (if nominal or string). */
  81.   private FastVector m_Values;
  82.   /** Mapping of values to indices (if nominal or string). */
  83.   private Hashtable m_Hashtable;
  84.   /** The attribute's index. */
  85.   private int m_Index;
  86.   /**
  87.    * Constructor for a numeric attribute.
  88.    *
  89.    * @param attributeName the name for the attribute
  90.    */
  91.   public Attribute(String attributeName) {
  92.     m_Name = attributeName;
  93.     m_Index = -1;
  94.     m_Values = null;
  95.     m_Hashtable = null;
  96.     m_Type = NUMERIC;
  97.   }
  98.   /**
  99.    * Constructor for nominal attributes and string attributes.
  100.    * If a null vector of attribute values is passed to the method,
  101.    * the attribute is assumed to be a string.
  102.    *
  103.    * @param attributeName the name for the attribute
  104.    * @param attributeValues a vector of strings denoting the 
  105.    * attribute values. Null if the attribute is a string attribute.
  106.    */
  107.   public Attribute(String attributeName, 
  108.    FastVector attributeValues) {
  109.     m_Name = attributeName;
  110.     m_Index = -1;
  111.     if (attributeValues == null) {
  112.       m_Values = new FastVector();
  113.       m_Hashtable = new Hashtable();
  114.       m_Type = STRING;
  115.     } else {
  116.       m_Values = new FastVector(attributeValues.size());
  117.       m_Hashtable = new Hashtable(attributeValues.size());
  118.       for (int i = 0; i < attributeValues.size(); i++) {
  119. Object store = attributeValues.elementAt(i);
  120. if (((String)store).length() > STRING_COMPRESS_THRESHOLD) {
  121.   try {
  122.     store = new SerializedObject(attributeValues.elementAt(i), true);
  123.   } catch (Exception ex) {
  124.     System.err.println("Couldn't compress nominal attribute value -"
  125.        + " storing uncompressed.");
  126.   }
  127. }
  128. m_Values.addElement(store);
  129. m_Hashtable.put(store, new Integer(i));
  130.       }
  131.       m_Type = NOMINAL;
  132.     }
  133.   }
  134.   /**
  135.    * Produces a shallow copy of this attribute.
  136.    *
  137.    * @return a copy of this attribute with the same index
  138.    */
  139.   public Object copy() {
  140.     Attribute copy = new Attribute(m_Name);
  141.     copy.m_Index = m_Index;
  142.     if (!isNominal() && !isString())
  143.       return copy;
  144.     copy.m_Type = m_Type;
  145.     copy.m_Values = m_Values;
  146.     copy.m_Hashtable = m_Hashtable;
  147.  
  148.     return copy;
  149.   }
  150.   /**
  151.    * Returns an enumeration of all the attribute's values if
  152.    * the attribute is nominal or a string, null otherwise. 
  153.    *
  154.    * @return enumeration of all the attribute's values
  155.    */
  156.   public final Enumeration enumerateValues() {
  157.     if (isNominal() || isString()) {
  158.       final Enumeration ee = m_Values.elements();
  159.       return new Enumeration () {
  160.           public boolean hasMoreElements() {
  161.             return ee.hasMoreElements();
  162.           }
  163.           public Object nextElement() {
  164.             Object oo = ee.nextElement();
  165.             if (oo instanceof SerializedObject) {
  166.               return ((SerializedObject)oo).getObject();
  167.             } else {
  168.               return oo;
  169.             }
  170.           }
  171.         };
  172.     }
  173.     return null;
  174.   }
  175.   /**
  176.    * Tests if given attribute is equal to this attribute.
  177.    *
  178.    * @param other the Object to be compared to this attribute
  179.    * @return true if the given attribute is equal to this attribute
  180.    */
  181.   public final boolean equals(Object other) {
  182.     if ((other == null) || !(other.getClass().equals(this.getClass()))) {
  183.       return false;
  184.     }
  185.     Attribute att = (Attribute) other;
  186.     if (!m_Name.equals(att.m_Name)) {
  187.       return false;
  188.     }
  189.     if (isNumeric() && att.isNumeric()) {
  190.       return true;
  191.     }
  192.     if (isNumeric() || att.isNumeric()) {
  193.       return false;
  194.     }
  195.     if (m_Values.size() != att.m_Values.size()) {
  196.       return false;
  197.     }
  198.     for (int i = 0; i < m_Values.size(); i++) {
  199.       if (!m_Values.elementAt(i).equals(att.m_Values.elementAt(i))) {
  200. return false;
  201.       }
  202.     }
  203.     return true;
  204.   }
  205.   /**
  206.    * Returns the index of this attribute.
  207.    *
  208.    * @return the index of this attribute
  209.    */
  210.   public final int index() {
  211.     return m_Index;
  212.   }
  213.   /**
  214.    * Returns the index of a given attribute value. (The index of
  215.    * the first occurence of this value.)
  216.    *
  217.    * @param value the value for which the index is to be returned
  218.    * @return the index of the given attribute value if attribute
  219.    * is nominal or a string, -1 if it is numeric or the value 
  220.    * can't be found
  221.    */
  222.   public final int indexOfValue(String value) {
  223.     if (!isNominal() && !isString())
  224.       return -1;
  225.     Object store = value;
  226.     if (value.length() > STRING_COMPRESS_THRESHOLD) {
  227.       try {
  228.         store = new SerializedObject(value, true);
  229.       } catch (Exception ex) {
  230.         System.err.println("Couldn't compress string attribute value -"
  231.                            + " searching uncompressed.");
  232.       }
  233.     }
  234.     Integer val = (Integer)m_Hashtable.get(store);
  235.     if (val == null) return -1;
  236.     else return val.intValue();
  237.   }
  238.   /**
  239.    * Test if the attribute is nominal.
  240.    *
  241.    * @return true if the attribute is nominal
  242.    */
  243.   public final boolean isNominal() {
  244.     return (m_Type == NOMINAL);
  245.   }
  246.   /**
  247.    * Tests if the attribute is numeric.
  248.    *
  249.    * @return true if the attribute is numeric
  250.    */
  251.   public final boolean isNumeric() {
  252.     return (m_Type == NUMERIC);
  253.   }
  254.   /**
  255.    * Tests if the attribute is a string.
  256.    *
  257.    * @return true if the attribute is a string
  258.    */
  259.   public final boolean isString() {
  260.     return (m_Type == STRING);
  261.   }
  262.   /**
  263.    * Returns the attribute's name.
  264.    *
  265.    * @return the attribute's name as a string
  266.    */
  267.   public final String name() {
  268.     return m_Name;
  269.   }
  270.   
  271.   /**
  272.    * Returns the number of attribute values. Returns 0 for numeric attributes.
  273.    *
  274.    * @return the number of attribute values
  275.    */
  276.   public final int numValues() {
  277.     if (!isNominal() && !isString()) {
  278.       return 0;
  279.     } else {
  280.       return m_Values.size();
  281.     }
  282.   }
  283.   /**
  284.    * Returns a description of this attribute in ARFF format. Quotes
  285.    * strings if they contain whitespace characters, or if they
  286.    * are a question mark.
  287.    *
  288.    * @return a description of this attribute as a string
  289.    */
  290.   public final String toString() {
  291.     
  292.     StringBuffer text = new StringBuffer();
  293.     
  294.     text.append("@attribute " + Utils.quote(m_Name) + " ");
  295.     if (isNominal()) {
  296.       text.append('{');
  297.       Enumeration enum = enumerateValues();
  298.       while (enum.hasMoreElements()) {
  299. text.append(Utils.quote((String) enum.nextElement()));
  300. if (enum.hasMoreElements())
  301.   text.append(',');
  302.       }
  303.       text.append('}');
  304.     } else {
  305.       if (isNumeric()) {
  306. text.append("numeric");
  307.       } else {
  308. text.append("string");
  309.       }
  310.     }
  311.     return text.toString();
  312.   }
  313.   /**
  314.    * Returns the attribute's type as an integer.
  315.    *
  316.    * @returns the attribute's type.
  317.    */
  318.   public final int type() {
  319.     return m_Type;
  320.   }
  321.   /**
  322.    * Returns a value of a nominal or string attribute. 
  323.    * Returns an empty string if the attribute is neither
  324.    * nominal nor a string attribute.
  325.    *
  326.    * @param valIndex the value's index
  327.    * @return the attribute's value as a string
  328.    */
  329.   public final String value(int valIndex) {
  330.     
  331.     if (!isNominal() && !isString()) {
  332.       return "";
  333.     } else {
  334.       Object val = m_Values.elementAt(valIndex);
  335.       
  336.       // If we're storing strings compressed, uncompress it.
  337.       if (val instanceof SerializedObject) {
  338.         val = ((SerializedObject)val).getObject();
  339.       }
  340.       return (String) val;
  341.     }
  342.   }
  343.   /**
  344.    * Constructor for a numeric attribute with a particular index.
  345.    *
  346.    * @param attributeName the name for the attribute
  347.    * @param index the attribute's index
  348.    */
  349.   Attribute(String attributeName, int index) {
  350.     this(attributeName);
  351.     m_Index = index;
  352.   }
  353.   /**
  354.    * Constructor for nominal attributes and string attributes with
  355.    * a particular index.
  356.    * If a null vector of attribute values is passed to the method,
  357.    * the attribute is assumed to be a string.
  358.    *
  359.    * @param attributeName the name for the attribute
  360.    * @param attributeValues a vector of strings denoting the attribute values.
  361.    * Null if the attribute is a string attribute.
  362.    * @param index the attribute's index
  363.    */
  364.   Attribute(String attributeName, FastVector attributeValues, 
  365.     int index) {
  366.     this(attributeName, attributeValues);
  367.     m_Index = index;
  368.   }
  369.   /**
  370.    * Adds a string value to the list of valid strings for attributes
  371.    * of type STRING and returns the index of the string.
  372.    *
  373.    * @param value The string value to add
  374.    * @return the index assigned to the string, or -1 if the attribute is not
  375.    * of type Attribute.STRING 
  376.    */
  377.   public int addStringValue(String value) {
  378.     if (!isString()) {
  379.       return -1;
  380.     }
  381.     Object store = value;
  382.     if (value.length() > STRING_COMPRESS_THRESHOLD) {
  383.       try {
  384.         store = new SerializedObject(value, true);
  385.       } catch (Exception ex) {
  386.         System.err.println("Couldn't compress string attribute value -"
  387.                            + " storing uncompressed.");
  388.       }
  389.     }
  390.     Integer index = (Integer)m_Hashtable.get(store);
  391.     if (index != null) {
  392.       return index.intValue();
  393.     } else {
  394.       int intIndex = m_Values.size();
  395.       m_Values.addElement(store);
  396.       m_Hashtable.put(store, new Integer(intIndex));
  397.       return intIndex;
  398.     }
  399.   }
  400.   /**
  401.    * Adds a string value to the list of valid strings for attributes
  402.    * of type STRING and returns the index of the string. This method is
  403.    * more efficient than addStringValue(String) for long strings.
  404.    *
  405.    * @param src The Attribute containing the string value to add.
  406.    * @param int index the index of the string value in the source attribute.
  407.    * @return the index assigned to the string, or -1 if the attribute is not
  408.    * of type Attribute.STRING 
  409.    */
  410.   public int addStringValue(Attribute src, int index) {
  411.     if (!isString()) {
  412.       return -1;
  413.     }
  414.     Object store = src.m_Values.elementAt(index);
  415.     Integer oldIndex = (Integer)m_Hashtable.get(store);
  416.     if (oldIndex != null) {
  417.       return oldIndex.intValue();
  418.     } else {
  419.       int intIndex = m_Values.size();
  420.       m_Values.addElement(store);
  421.       m_Hashtable.put(store, new Integer(intIndex));
  422.       return intIndex;
  423.     }
  424.   }
  425.   /**
  426.    * Adds an attribute value. Creates a fresh list of attribute
  427.    * values before adding it.
  428.    *
  429.    * @param value the attribute value
  430.    */
  431.   final void addValue(String value) {
  432.     m_Values = (FastVector)m_Values.copy();
  433.     m_Hashtable = (Hashtable)m_Hashtable.clone();
  434.     forceAddValue(value);
  435.   }
  436.   /**
  437.    * Produces a shallow copy of this attribute with a new name.
  438.    *
  439.    * @param newName the name of the new attribute
  440.    * @return a copy of this attribute with the same index
  441.    */
  442.   final Attribute copy(String newName) {
  443.     Attribute copy = new Attribute(newName);
  444.     copy.m_Index = m_Index;
  445.     if (!isNominal() && !isString())
  446.       return copy;
  447.     copy.m_Type = m_Type;
  448.     copy.m_Values = m_Values;
  449.     copy.m_Hashtable = m_Hashtable;
  450.  
  451.     return copy;
  452.   }
  453.   /**
  454.    * Removes a value of a nominal or string attribute. Creates a 
  455.    * fresh list of attribute values before removing it.
  456.    *
  457.    * @param index the value's index
  458.    * @exception IllegalArgumentException if the attribute is not nominal
  459.    */
  460.   final void delete(int index) {
  461.     
  462.     if (!isNominal() && !isString()) 
  463.       throw new IllegalArgumentException("Can only remove value of" +
  464.                                          "nominal or string attribute!");
  465.     else {
  466.       m_Values = (FastVector)m_Values.copy();
  467.       m_Values.removeElementAt(index);
  468.       Hashtable hash = new Hashtable(m_Hashtable.size());
  469.       Enumeration enum = m_Hashtable.keys();
  470.       while (enum.hasMoreElements()) {
  471. Object string = enum.nextElement();
  472. Integer valIndexObject = (Integer)m_Hashtable.get(string);
  473. int valIndex = valIndexObject.intValue();
  474. if (valIndex > index) {
  475.   hash.put(string, new Integer(valIndex - 1));
  476. } else if (valIndex < index) {
  477.   hash.put(string, valIndexObject);
  478. }
  479.       }
  480.       m_Hashtable = hash;
  481.     }
  482.   }
  483.   /**
  484.    * Adds an attribute value.
  485.    *
  486.    * @param value the attribute value
  487.    */
  488.   final void forceAddValue(String value) {
  489.     Object store = value;
  490.     if (value.length() > STRING_COMPRESS_THRESHOLD) {
  491.       try {
  492.         store = new SerializedObject(value, true);
  493.       } catch (Exception ex) {
  494.         System.err.println("Couldn't compress string attribute value -"
  495.                            + " storing uncompressed.");
  496.       }
  497.     }
  498.     m_Values.addElement(store);
  499.     m_Hashtable.put(store, new Integer(m_Values.size() - 1));
  500.   }
  501.   /**
  502.    * Sets the index of this attribute.
  503.    *
  504.    * @param the index of this attribute
  505.    */
  506.   final void setIndex(int index) {
  507.     m_Index = index;
  508.   }
  509.   /**
  510.    * Sets a value of a nominal attribute or string attribute.
  511.    * Creates a fresh list of attribute values before it is set.
  512.    *
  513.    * @param index the value's index
  514.    * @param string the value
  515.    * @exception IllegalArgumentException if the attribute is not nominal or 
  516.    * string.
  517.    */
  518.   final void setValue(int index, String string) {
  519.     if (!isNominal() && !isString()) {
  520.       throw new IllegalArgumentException("Can only set value of nominal"+
  521.                                          "or string attribute!");
  522.     } else {
  523.       m_Values = (FastVector)m_Values.copy();
  524.       m_Hashtable = (Hashtable)m_Hashtable.clone();
  525.       Object store = string;
  526.       if (string.length() > STRING_COMPRESS_THRESHOLD) {
  527.         try {
  528.           store = new SerializedObject(string, true);
  529.         } catch (Exception ex) {
  530.           System.err.println("Couldn't compress string attribute value -"
  531.                              + " storing uncompressed.");
  532.         }
  533.       }
  534.       m_Hashtable.remove(m_Values.elementAt(index));
  535.       m_Values.setElementAt(store, index);
  536.       m_Hashtable.put(store, new Integer(index));
  537.     }
  538.   }
  539.   /**
  540.    * Simple main method for testing this class.
  541.    */
  542.   public static void main(String[] ops) {
  543.     try {
  544.       
  545.       // Create numeric attributes "length" and "weight"
  546.       Attribute length = new Attribute("length");
  547.       Attribute weight = new Attribute("weight");
  548.       
  549.       // Create vector to hold nominal values "first", "second", "third" 
  550.       FastVector my_nominal_values = new FastVector(3); 
  551.       my_nominal_values.addElement("first"); 
  552.       my_nominal_values.addElement("second"); 
  553.       my_nominal_values.addElement("third"); 
  554.       
  555.       // Create nominal attribute "position" 
  556.       Attribute position = new Attribute("position", my_nominal_values);
  557.       // Print the name of "position"
  558.       System.out.println("Name of "position": " + position.name());
  559.       // Print the values of "position"
  560.       Enumeration attValues = position.enumerateValues();
  561.       while (attValues.hasMoreElements()) {
  562. String string = (String)attValues.nextElement();
  563. System.out.println("Value of "position": " + string);
  564.       }
  565.       // Shallow copy attribute "position"
  566.       Attribute copy = (Attribute) position.copy();
  567.       // Test if attributes are the same
  568.       System.out.println("Copy is the same as original: " + copy.equals(position));
  569.       // Print index of attribute "weight" (should be unset: -1)
  570.       System.out.println("Index of attribute "weight" (should be -1): " + 
  571.  weight.index());
  572.       // Print index of value "first" of attribute "position"
  573.       System.out.println("Index of value "first" of "position" (should be 0): " +
  574.  position.indexOfValue("first"));
  575.       // Tests type of attribute "position"
  576.       System.out.println(""position" is numeric: " + position.isNumeric());
  577.       System.out.println(""position" is nominal: " + position.isNominal());
  578.       System.out.println(""position" is string: " + position.isString());
  579.       // Prints name of attribute "position"
  580.       System.out.println("Name of "position": " + position.name());
  581.     
  582.       // Prints number of values of attribute "position"
  583.       System.out.println("Number of values for "position": " + position.numValues());
  584.       // Prints the values (againg)
  585.       for (int i = 0; i < position.numValues(); i++) {
  586. System.out.println("Value " + i + ": " + position.value(i));
  587.       }
  588.       // Prints the attribute "position" in ARFF format
  589.       System.out.println(position);
  590.       // Checks type of attribute "position" using constants
  591.       switch (position.type()) {
  592.       case Attribute.NUMERIC:
  593. System.out.println(""position" is numeric");
  594. break;
  595.       case Attribute.NOMINAL:
  596. System.out.println(""position" is nominal");
  597. break;
  598.       case Attribute.STRING:
  599. System.out.println(""position" is string");
  600. break;
  601.       default:
  602. System.out.println(""position" has unknown type");
  603.       }
  604.     } catch (Exception e) {
  605.       e.printStackTrace();
  606.     }
  607.   }
  608. }
  609.