Instances.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 65k
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.  *    Instances.java
  18.  *    Copyright (C) 1999 Eibe Frank
  19.  *
  20.  */
  21. package weka.core;
  22. import java.io.*;
  23. import java.text.ParseException;
  24. import java.util.*;
  25. /**
  26.  * Class for handling an ordered set of weighted instances. <p>
  27.  *
  28.  * Typical usage (code from the main() method of this class): <p>
  29.  *
  30.  * <code>
  31.  * ... <br>
  32.  * 
  33.  * // Read all the instances in the file <br>
  34.  * reader = new FileReader(filename); <br>
  35.  * instances = new Instances(reader); <br><br>
  36.  *
  37.  * // Make the last attribute be the class <br>
  38.  * instances.setClassIndex(instances.numAttributes() - 1); <br><br>
  39.  * 
  40.  * // Print header and instances. <br>
  41.  * System.out.println("nDataset:n"); <br> 
  42.  * System.out.println(instances); <br><br>
  43.  *
  44.  * ... <br>
  45.  * </code><p>
  46.  *
  47.  * All methods that change a set of instances are safe, ie. a change
  48.  * of a set of instances does not affect any other sets of
  49.  * instances. All methods that change a datasets's attribute
  50.  * information clone the dataset before it is changed.
  51.  *
  52.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  53.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  54.  * @version $Revision: 1.36 $ 
  55.  */
  56. public class Instances implements Serializable {
  57.  
  58.   /** The filename extension that should be used for arff files */
  59.   public static String FILE_EXTENSION = ".arff";
  60.   /** The dataset's name. */
  61.   protected String m_RelationName;         
  62.   /** The attribute information. */
  63.   protected FastVector m_Attributes;
  64.   /** The instances. */
  65.   protected FastVector m_Instances;
  66.   /** The class attribute's index */
  67.   protected int m_ClassIndex;
  68.   /** Buffer of values for sparse instance */
  69.   protected double[] m_ValueBuffer;
  70.   /** Buffer of indices for sparse instance */
  71.   protected int[] m_IndicesBuffer;
  72.   /**
  73.    * Reads an ARFF file from a reader, and assigns a weight of
  74.    * one to each instance. Lets the index of the class 
  75.    * attribute be undefined (negative).
  76.    *
  77.    * @param reader the reader
  78.    * @exception IOException if the ARFF file is not read 
  79.    * successfully
  80.    */
  81.   public Instances(Reader reader) throws IOException {
  82.     StreamTokenizer tokenizer;
  83.     tokenizer = new StreamTokenizer(reader);
  84.     initTokenizer(tokenizer);
  85.     readHeader(tokenizer);
  86.     m_ClassIndex = -1;
  87.     m_Instances = new FastVector(1000);
  88.     while (getInstance(tokenizer, true)) {};
  89.     compactify();
  90.   }
  91.  
  92.   /**
  93.    * Reads the header of an ARFF file from a reader and 
  94.    * reserves space for the given number of instances. Lets
  95.    * the class index be undefined (negative).
  96.    *
  97.    * @param reader the reader
  98.    * @param capacity the capacity
  99.    * @exception IllegalArgumentException if the header is not read successfully
  100.    * or the capacity is negative.
  101.    * @exception IOException if there is a problem with the reader.
  102.    */
  103.    public Instances(Reader reader, int capacity) throws IOException {
  104.     StreamTokenizer tokenizer;
  105.     if (capacity < 0) {
  106.       throw new IllegalArgumentException("Capacity has to be positive!");
  107.     }
  108.     tokenizer = new StreamTokenizer(reader); 
  109.     initTokenizer(tokenizer);
  110.     readHeader(tokenizer);
  111.     m_ClassIndex = -1;
  112.     m_Instances = new FastVector(capacity);
  113.   }
  114.   /**
  115.    * Constructor copying all instances and references to
  116.    * the header information from the given set of instances.
  117.    *
  118.    * @param instances the set to be copied
  119.    */
  120.   public Instances(Instances dataset) {
  121.     this(dataset, dataset.numInstances());
  122.     dataset.copyInstances(0, this, dataset.numInstances());
  123.   }
  124.   /**
  125.    * Constructor creating an empty set of instances. Copies references
  126.    * to the header information from the given set of instances. Sets
  127.    * the capacity of the set of instances to 0 if its negative.
  128.    *
  129.    * @param instances the instances from which the header 
  130.    * information is to be taken
  131.    * @param capacity the capacity of the new dataset 
  132.    */
  133.   public Instances(Instances dataset, int capacity) {
  134.     
  135.     if (capacity < 0) {
  136.       capacity = 0;
  137.     }
  138.     
  139.     // Strings only have to be "shallow" copied because
  140.     // they can't be modified.
  141.     m_ClassIndex = dataset.m_ClassIndex;
  142.     m_RelationName = dataset.m_RelationName;
  143.     m_Attributes = dataset.m_Attributes;
  144.     m_Instances = new FastVector(capacity);
  145.   }
  146.   /**
  147.    * Creates a new set of instances by copying a 
  148.    * subset of another set.
  149.    *
  150.    * @param source the set of instances from which a subset 
  151.    * is to be created
  152.    * @param first the index of the first instance to be copied
  153.    * @param toCopy the number of instances to be copied
  154.    * @exception IllegalArgumentException if first and toCopy are out of range
  155.    */
  156.   public Instances(Instances source, int first, int toCopy) {
  157.     
  158.     this(source, toCopy);
  159.     if ((first < 0) || ((first + toCopy) > source.numInstances())) {
  160.       throw new IllegalArgumentException("Parameters first and/or toCopy out "+
  161.                                          "of range");
  162.     }
  163.     source.copyInstances(first, this, toCopy);
  164.   }
  165.   /**
  166.    * Creates an empty set of instances. Uses the given
  167.    * attribute information. Sets the capacity of the set of 
  168.    * instances to 0 if its negative. Given attribute information
  169.    * must not be changed after this constructor has been used.
  170.    *
  171.    * @param name the name of the relation
  172.    * @param attInfo the attribute information
  173.    * @param capacity the capacity of the set
  174.    */
  175.   public Instances(String name, FastVector attInfo, int capacity) {
  176.     m_RelationName = name;
  177.     m_ClassIndex = -1;
  178.     m_Attributes = attInfo;
  179.     for (int i = 0; i < numAttributes(); i++) {
  180.       attribute(i).setIndex(i);
  181.     }
  182.     m_Instances = new FastVector(capacity);
  183.   }
  184.  
  185.   /**
  186.    * Create a copy of the structure, but "cleanse" string types (i.e.
  187.    * doesn't contain references to the strings seen in the past).
  188.    *
  189.    * @return a copy of the instance structure.
  190.    */
  191.   public Instances stringFreeStructure() {
  192.     FastVector atts = (FastVector)m_Attributes.copy();
  193.     for (int i = 0 ; i < atts.size(); i++) {
  194.       Attribute att = (Attribute)atts.elementAt(i);
  195.       if (att.type() == Attribute.STRING) {
  196.         atts.setElementAt(new Attribute(att.name(), (FastVector)null), i);
  197.       }
  198.     }
  199.     Instances result = new Instances(relationName(), atts, 0);
  200.     result.m_ClassIndex = m_ClassIndex;
  201.     return result;
  202.   }
  203.   /**
  204.    * Adds one instance to the end of the set. 
  205.    * Shallow copies instance before it is added. Increases the
  206.    * size of the dataset if it is not large enough. Does not
  207.    * check if the instance is compatible with the dataset.
  208.    *
  209.    * @param instance the instance to be added
  210.    */
  211.   public final void add(Instance instance) {
  212.     Instance newInstance = (Instance)instance.copy();
  213.     newInstance.setDataset(this);
  214.     m_Instances.addElement(newInstance);
  215.   }
  216.   /**
  217.    * Returns an attribute.
  218.    *
  219.    * @param index the attribute's index
  220.    * @return the attribute at the given position
  221.    */ 
  222.   public final Attribute attribute(int index) {
  223.     
  224.     return (Attribute) m_Attributes.elementAt(index);
  225.   }
  226.   /**
  227.    * Returns an attribute given its name. If there is more than
  228.    * one attribute with the same name, it returns the first one.
  229.    * Returns null if the attribute can't be found.
  230.    *
  231.    * @param name the attribute's name
  232.    * @return the attribute with the given name, null if the
  233.    * attribute can't be found
  234.    */ 
  235.   public final Attribute attribute(String name) {
  236.     
  237.     for (int i = 0; i < numAttributes(); i++) {
  238.       if (attribute(i).name().equals(name)) {
  239. return attribute(i);
  240.       }
  241.     }
  242.     return null;
  243.   }
  244.   /**
  245.    * Checks for string attributes in the dataset
  246.    *
  247.    * @return true if string attributes are present, false otherwise
  248.    */
  249.   public boolean checkForStringAttributes() {
  250.     int i = 0;
  251.    
  252.     while (i < m_Attributes.size()) {
  253.       if (attribute(i++).isString()) {
  254. return true;
  255.       }
  256.     }
  257.     return false;
  258.   }
  259.   /**
  260.    * Checks if the given instance is compatible
  261.    * with this dataset. Only looks at the size of
  262.    * the instance and the ranges of the values for 
  263.    * nominal and string attributes.
  264.    *
  265.    * @return true if the instance is compatible with the dataset 
  266.    */
  267.   public final boolean checkInstance(Instance instance) {
  268.     if (instance.numAttributes() != numAttributes()) {
  269.       return false;
  270.     }
  271.     for (int i = 0; i < numAttributes(); i++) {
  272.       if (instance.isMissing(i)) {
  273. continue;
  274.       } else if (attribute(i).isNominal() ||
  275.  attribute(i).isString()) {
  276. if (!(Utils.eq(instance.value(i),
  277.        (double)(int)instance.value(i)))) {
  278.   return false;
  279. } else if (Utils.sm(instance.value(i), 0) ||
  280.    Utils.gr(instance.value(i),
  281.     attribute(i).numValues())) {
  282.   return false;
  283. }
  284.       }
  285.     }
  286.     return true;
  287.   }
  288.   /**
  289.    * Returns the class attribute.
  290.    *
  291.    * @return the class attribute
  292.    * @exception UnassignedClassException if the class is not set
  293.    */
  294.   public final Attribute classAttribute() {
  295.     if (m_ClassIndex < 0) {
  296.       throw new UnassignedClassException("Class index is negative (not set)!");
  297.     }
  298.     return attribute(m_ClassIndex);
  299.   }
  300.   /**
  301.    * Returns the class attribute's index. Returns negative number
  302.    * if it's undefined.
  303.    *
  304.    * @return the class index as an integer
  305.    */
  306.   public final int classIndex() {
  307.     
  308.     return m_ClassIndex;
  309.   }
  310.  
  311.   /**
  312.    * Compactifies the set of instances. Decreases the capacity of
  313.    * the set so that it matches the number of instances in the set.
  314.    */
  315.   public final void compactify() {
  316.     m_Instances.trimToSize();
  317.   }
  318.   /**
  319.    * Removes all instances from the set.
  320.    */
  321.   public final void delete() {
  322.     
  323.     m_Instances = new FastVector();
  324.   }
  325.   /**
  326.    * Removes an instance at the given position from the set.
  327.    *
  328.    * @param index the instance's position
  329.    */
  330.   public final void delete(int index) {
  331.     
  332.     m_Instances.removeElementAt(index);
  333.   }
  334.   /**
  335.    * Deletes an attribute at the given position 
  336.    * (0 to numAttributes() - 1). A deep copy of the attribute
  337.    * information is performed before the attribute is deleted.
  338.    *
  339.    * @param pos the attribute's position
  340.    * @exception IllegalArgumentException if the given index is out of range or the
  341.    * class attribute is being deleted
  342.    */
  343.   public void deleteAttributeAt(int position) {
  344.  
  345.     if ((position < 0) || (position >= m_Attributes.size())) {
  346.       throw new IllegalArgumentException("Index out of range");
  347.     }
  348.     if (position == m_ClassIndex) {
  349.       throw new IllegalArgumentException("Can't delete class attribute");
  350.     }
  351.     freshAttributeInfo();
  352.     if (m_ClassIndex > position) {
  353.       m_ClassIndex--;
  354.     }
  355.     m_Attributes.removeElementAt(position);
  356.     for (int i = position; i < m_Attributes.size(); i++) {
  357.       Attribute current = (Attribute)m_Attributes.elementAt(i);
  358.       current.setIndex(current.index() - 1);
  359.     }
  360.     for (int i = 0; i < numInstances(); i++) {
  361.       instance(i).forceDeleteAttributeAt(position); 
  362.     }
  363.   }
  364.   /**
  365.    * Deletes all string attributes in the dataset. A deep copy of the attribute
  366.    * information is performed before an attribute is deleted.
  367.    *
  368.    * @exception IllegalArgumentException if string attribute couldn't be 
  369.    * successfully deleted (probably because it is the class attribute).
  370.    */
  371.   public void deleteStringAttributes() {
  372.     int i = 0;
  373.     while (i < m_Attributes.size()) {
  374.       if (attribute(i).isString()) {
  375. deleteAttributeAt(i);
  376.       } else {
  377. i++;
  378.       }
  379.     }
  380.   }
  381.   /**
  382.    * Removes all instances with missing values for a particular
  383.    * attribute from the dataset.
  384.    *
  385.    * @param attIndex the attribute's index
  386.    */
  387.   public final void deleteWithMissing(int attIndex) {
  388.     FastVector newInstances = new FastVector(numInstances());
  389.     for (int i = 0; i < numInstances(); i++) {
  390.       if (!instance(i).isMissing(attIndex)) {
  391. newInstances.addElement(instance(i));
  392.       }
  393.     }
  394.     m_Instances = newInstances;
  395.   }
  396.   /**
  397.    * Removes all instances with missing values for a particular
  398.    * attribute from the dataset.
  399.    *
  400.    * @param att the attribute
  401.    */
  402.   public final void deleteWithMissing(Attribute att) {
  403.     deleteWithMissing(att.index());
  404.   }
  405.   /**
  406.    * Removes all instances with a missing class value
  407.    * from the dataset.
  408.    *
  409.    * @exception UnassignedClassException if class is not set
  410.    */
  411.   public final void deleteWithMissingClass() {
  412.     if (m_ClassIndex < 0) {
  413.       throw new UnassignedClassException("Class index is negative (not set)!");
  414.     }
  415.     deleteWithMissing(m_ClassIndex);
  416.   }
  417.   /**
  418.    * Returns an enumeration of all the attributes.
  419.    *
  420.    * @return enumeration of all the attributes.
  421.    */
  422.   public Enumeration enumerateAttributes() {
  423.     return m_Attributes.elements(m_ClassIndex);
  424.   }
  425.   /**
  426.    * Returns an enumeration of all instances in the dataset.
  427.    *
  428.    * @return enumeration of all instances in the dataset
  429.    */
  430.   public final Enumeration enumerateInstances() {
  431.     return m_Instances.elements();
  432.   }
  433.   /**
  434.    * Checks if two headers are equivalent.
  435.    *
  436.    * @param dataset another dataset
  437.    * @return true if the header of the given dataset is equivalent 
  438.    * to this header
  439.    */
  440.   public final boolean equalHeaders(Instances dataset){
  441.     // Check class and all attributes
  442.     if (m_ClassIndex != dataset.m_ClassIndex) {
  443.       return false;
  444.     }
  445.     if (m_Attributes.size() != dataset.m_Attributes.size()) {
  446.       return false;
  447.     }
  448.     for (int i = 0; i < m_Attributes.size(); i++) {
  449.       if (!(attribute(i).equals(dataset.attribute(i)))) {
  450. return false;
  451.       }
  452.     }
  453.     return true;
  454.   }
  455.  
  456.   /**
  457.    * Returns the first instance in the set.
  458.    *
  459.    * @return the first instance in the set
  460.    */
  461.   public final Instance firstInstance() {
  462.     
  463.     return (Instance)m_Instances.firstElement();
  464.   }
  465.  
  466.   /**
  467.    * Inserts an attribute at the given position (0 to 
  468.    * numAttributes()) and sets all values to be missing.
  469.    * Shallow copies the attribute before it is inserted, and performs
  470.    * a deep copy of the existing attribute information.
  471.    *
  472.    * @param att the attribute to be inserted
  473.    * @param pos the attribute's position
  474.    * @exception IllegalArgumentException if the given index is out of range
  475.    */
  476.   public void insertAttributeAt(Attribute att, int position) {
  477.  
  478.     if ((position < 0) ||
  479. (position > m_Attributes.size())) {
  480.       throw new IllegalArgumentException("Index out of range");
  481.     }
  482.     att = (Attribute)att.copy();
  483.     freshAttributeInfo();
  484.     att.setIndex(position);
  485.     m_Attributes.insertElementAt(att, position);
  486.     for (int i = position + 1; i < m_Attributes.size(); i++) {
  487.       Attribute current = (Attribute)m_Attributes.elementAt(i);
  488.       current.setIndex(current.index() + 1);
  489.     }
  490.     for (int i = 0; i < numInstances(); i++) {
  491.       instance(i).forceInsertAttributeAt(position);
  492.     }
  493.     if (m_ClassIndex >= position) {
  494.       m_ClassIndex++;
  495.     }
  496.   }
  497.   /**
  498.    * Returns the instance at the given position.
  499.    *
  500.    * @param index the instance's index
  501.    * @return the instance at the given position
  502.    */
  503.   public final Instance instance(int index) {
  504.     return (Instance)m_Instances.elementAt(index);
  505.   }
  506.   /**
  507.    * Returns the last instance in the set.
  508.    *
  509.    * @return the last instance in the set
  510.    */
  511.   public final Instance lastInstance() {
  512.     
  513.     return (Instance)m_Instances.lastElement();
  514.   }
  515.   /**
  516.    * Returns the mean (mode) for a numeric (nominal) attribute as
  517.    * a floating-point value. Returns 0 if the attribute is neither nominal nor 
  518.    * numeric. If all values are missing it returns zero.
  519.    *
  520.    * @param attIndex the attribute's index
  521.    * @return the mean or the mode
  522.    */
  523.   public final double meanOrMode(int attIndex) {
  524.     double result, found;
  525.     int [] counts;
  526.     if (attribute(attIndex).isNumeric()) {
  527.       result = found = 0;
  528.       for (int j = 0; j < numInstances(); j++) {
  529. if (!instance(j).isMissing(attIndex)) {
  530.   found += instance(j).weight();
  531.   result += instance(j).weight()*instance(j).value(attIndex);
  532. }
  533.       }
  534.       if (Utils.eq(found, 0)) {
  535. return 0;
  536.       } else {
  537. return result / found;
  538.       }
  539.     } else if (attribute(attIndex).isNominal()) {
  540.       counts = new int[attribute(attIndex).numValues()];
  541.       for (int j = 0; j < numInstances(); j++) {
  542. if (!instance(j).isMissing(attIndex)) {
  543.   counts[(int) instance(j).value(attIndex)] += instance(j).weight();
  544. }
  545.       }
  546.       return (double)Utils.maxIndex(counts);
  547.     } else {
  548.       return 0;
  549.     }
  550.   }
  551.   /**
  552.    * Returns the mean (mode) for a numeric (nominal) attribute as a
  553.    * floating-point value.  Returns 0 if the attribute is neither
  554.    * nominal nor numeric.  If all values are missing it returns zero.
  555.    *
  556.    * @param att the attribute
  557.    * @return the mean or the mode 
  558.    */
  559.   public final double meanOrMode(Attribute att) {
  560.     return meanOrMode(att.index());
  561.   }
  562.   /**
  563.    * Returns the number of attributes.
  564.    *
  565.    * @return the number of attributes as an integer
  566.    */
  567.   public final int numAttributes() {
  568.     return m_Attributes.size();
  569.   }
  570.   /**
  571.    * Returns the number of class labels.
  572.    *
  573.    * @return the number of class labels as an integer if the class 
  574.    * attribute is nominal, 1 otherwise.
  575.    * @exception UnassignedClassException if the class is not set
  576.    */
  577.   public final int numClasses() {
  578.     
  579.     if (m_ClassIndex < 0) {
  580.       throw new UnassignedClassException("Class index is negative (not set)!");
  581.     }
  582.     if (!classAttribute().isNominal()) {
  583.       return 1;
  584.     } else {
  585.       return classAttribute().numValues();
  586.     }
  587.   }
  588.   /**
  589.    * Returns the number of distinct values of a given attribute.
  590.    * Returns the number of instances if the attribute is a
  591.    * string attribute. The value 'missing' is not counted.
  592.    *
  593.    * @param attIndex the attribute
  594.    * @return the number of distinct values of a given attribute
  595.    */
  596.   public final int numDistinctValues(int attIndex) {
  597.     if (attribute(attIndex).isNumeric()) {
  598.       double [] attVals = attributeToDoubleArray(attIndex);
  599.       int [] sorted = Utils.sort(attVals);
  600.       double prev = 0;
  601.       int counter = 0;
  602.       for (int i = 0; i < sorted.length; i++) {
  603. Instance current = instance(sorted[i]);
  604. if (current.isMissing(attIndex)) {
  605.   break;
  606. }
  607. if ((i == 0) || 
  608.     Utils.gr(current.value(attIndex), prev)) {
  609.   prev = current.value(attIndex);
  610.   counter++;
  611. }
  612.       }
  613.       return counter;
  614.     } else {
  615.       return attribute(attIndex).numValues();
  616.     }
  617.   }
  618.   /**
  619.    * Returns the number of distinct values of a given attribute.
  620.    * Returns the number of instances if the attribute is a
  621.    * string attribute. The value 'missing' is not counted.
  622.    *
  623.    * @param att the attribute
  624.    * @return the number of distinct values of a given attribute
  625.    */
  626.   public final int numDistinctValues(Attribute att) {
  627.     return numDistinctValues(att.index());
  628.   }
  629.   
  630.   /**
  631.    * Returns the number of instances in the dataset.
  632.    *
  633.    * @return the number of instances in the dataset as an integer
  634.    */
  635.   public final int numInstances() {
  636.     return m_Instances.size();
  637.   }
  638.   /**
  639.    * Shuffles the instances in the set so that they are ordered 
  640.    * randomly.
  641.    *
  642.    * @param random a random number generator
  643.    */
  644.   public final void randomize(Random random) {
  645.     for (int j = numInstances() - 1; j > 0; j--)
  646.       swap(j, random.nextInt(j+1));
  647.   }
  648.   /**
  649.    * Reads a single instance from the reader and appends it
  650.    * to the dataset.  Automatically expands the dataset if it
  651.    * is not large enough to hold the instance. This method does
  652.    * not check for carriage return at the end of the line.
  653.    *
  654.    * @param reader the reader 
  655.    * @return false if end of file has been reached
  656.    * @exception IOException if the information is not read 
  657.    * successfully
  658.    */ 
  659.   public final boolean readInstance(Reader reader) 
  660.        throws IOException {
  661.     StreamTokenizer tokenizer = new StreamTokenizer(reader);
  662.     
  663.     initTokenizer(tokenizer);
  664.     return getInstance(tokenizer, false);
  665.   }    
  666.   /**
  667.    * Returns the relation's name.
  668.    *
  669.    * @return the relation's name as a string
  670.    */
  671.   public final String relationName() {
  672.     return m_RelationName;
  673.   }
  674.   /**
  675.    * Renames an attribute. This change only affects this
  676.    * dataset.
  677.    *
  678.    * @param att the attribute's index
  679.    * @param name the new name
  680.    */
  681.   public final void renameAttribute(int att, String name) {
  682.     Attribute newAtt = attribute(att).copy(name);
  683.     FastVector newVec = new FastVector(numAttributes());
  684.     for (int i = 0; i < numAttributes(); i++) {
  685.       if (i == att) {
  686. newVec.addElement(newAtt);
  687.       } else {
  688. newVec.addElement(attribute(i));
  689.       }
  690.     }
  691.     m_Attributes = newVec;
  692.   }
  693.   /**
  694.    * Renames an attribute. This change only affects this
  695.    * dataset.
  696.    *
  697.    * @param att the attribute
  698.    * @param name the new name
  699.    */
  700.   public final void renameAttribute(Attribute att, String name) {
  701.     renameAttribute(att.index(), name);
  702.   }
  703.   /**
  704.    * Renames the value of a nominal (or string) attribute value. This
  705.    * change only affects this dataset.
  706.    *
  707.    * @param att the attribute's index
  708.    * @param val the value's index
  709.    * @param name the new name 
  710.    */
  711.   public final void renameAttributeValue(int att, int val, String name) {
  712.     Attribute newAtt = (Attribute)attribute(att).copy();
  713.     FastVector newVec = new FastVector(numAttributes());
  714.     newAtt.setValue(val, name);
  715.     for (int i = 0; i < numAttributes(); i++) {
  716.       if (i == att) {
  717. newVec.addElement(newAtt);
  718.       } else {
  719. newVec.addElement(attribute(i));
  720.       }
  721.     }
  722.     m_Attributes = newVec;
  723.   }
  724.   /**
  725.    * Renames the value of a nominal (or string) attribute value. This
  726.    * change only affects this dataset.
  727.    *
  728.    * @param att the attribute
  729.    * @param val the value
  730.    * @param name the new name
  731.    */
  732.   public final void renameAttributeValue(Attribute att, String val, 
  733.                                          String name) {
  734.     int v = att.indexOfValue(val);
  735.     if (v == -1) throw new IllegalArgumentException(val + " not found");
  736.     renameAttributeValue(att.index(), v, name);
  737.   }
  738.   /**
  739.    * Creates a new dataset of the same size using random sampling
  740.    * with replacement.
  741.    *
  742.    * @param random a random number generator
  743.    * @return the new dataset
  744.    */
  745.   public final Instances resample(Random random) {
  746.     Instances newData = new Instances(this, numInstances());
  747.     while (newData.numInstances() < numInstances()) {
  748.       int i = (int) (random.nextDouble() * (double) numInstances());
  749.       newData.add(instance(i));
  750.     }
  751.     return newData;
  752.   }
  753.   /**
  754.    * Creates a new dataset of the same size using random sampling
  755.    * with replacement according to the current instance weights. The
  756.    * weights of the instances in the new dataset are set to one.
  757.    *
  758.    * @param random a random number generator
  759.    * @return the new dataset
  760.    */
  761.   public final Instances resampleWithWeights(Random random) {
  762.     double [] weights = new double[numInstances()];
  763.     boolean foundOne = false;
  764.     for (int i = 0; i < weights.length; i++) {
  765.       weights[i] = instance(i).weight();
  766.       if (!Utils.eq(weights[i], weights[0])) {
  767.         foundOne = true;
  768.       }
  769.     }
  770.     if (foundOne) {
  771.       return resampleWithWeights(random, weights);
  772.     } else {
  773.       return new Instances(this);
  774.     }
  775.   }
  776.   /**
  777.    * Creates a new dataset of the same size using random sampling
  778.    * with replacement according to the given weight vector. The
  779.    * weights of the instances in the new dataset are set to one.
  780.    * The length of the weight vector has to be the same as the
  781.    * number of instances in the dataset, and all weights have to
  782.    * be positive.
  783.    *
  784.    * @param random a random number generator
  785.    * @param weights the weight vector
  786.    * @return the new dataset
  787.    * @exception IllegalArgumentException if the weights array is of the wrong
  788.    * length or contains negative weights.
  789.    */
  790.   public final Instances resampleWithWeights(Random random, 
  791.      double[] weights) {
  792.     if (weights.length != numInstances()) {
  793.       throw new IllegalArgumentException("weights.length != numInstances.");
  794.     }
  795.     Instances newData = new Instances(this, numInstances());
  796.     double[] probabilities = new double[numInstances()];
  797.     double sumProbs = 0, sumOfWeights = Utils.sum(weights);
  798.     for (int i = 0; i < numInstances(); i++) {
  799.       sumProbs += random.nextDouble();
  800.       probabilities[i] = sumProbs;
  801.     }
  802.     Utils.normalize(probabilities, sumProbs / sumOfWeights);
  803.     // Make sure that rounding errors don't mess things up
  804.     probabilities[numInstances() - 1] = sumOfWeights;
  805.     int k = 0; int l = 0;
  806.     sumProbs = 0;
  807.     while ((k < numInstances() && (l < numInstances()))) {
  808.       if (weights[l] < 0) {
  809. throw new IllegalArgumentException("Weights have to be positive.");
  810.       }
  811.       sumProbs += weights[l];
  812.       while ((k < numInstances()) &&
  813.      (probabilities[k] <= sumProbs)) { 
  814. newData.add(instance(l));
  815. newData.instance(k).setWeight(1);
  816. k++;
  817.       }
  818.       l++;
  819.     }
  820.     return newData;
  821.   }
  822.   /** 
  823.    * Sets the class attribute.
  824.    *
  825.    * @param att attribute to be the class
  826.    */
  827.   public final void setClass(Attribute att) {
  828.     m_ClassIndex = att.index();
  829.   }
  830.   /** 
  831.    * Sets the class index of the set.
  832.    * If the class index is negative there is assumed to be no class.
  833.    * (ie. it is undefined)
  834.    *
  835.    * @param classIndex the new class index
  836.    * @exception IllegalArgumentException if the class index is too big or < 0
  837.    */
  838.   public final void setClassIndex(int classIndex) {
  839.     if (classIndex >= numAttributes()) {
  840.       throw new IllegalArgumentException("Invalid class index: " + classIndex);
  841.     }
  842.     m_ClassIndex = classIndex;
  843.   }
  844.   /**
  845.    * Sets the relation's name.
  846.    *
  847.    * @param newName the new relation name.
  848.    */
  849.   public final void setRelationName(String newName) {
  850.     
  851.     m_RelationName = newName;
  852.   }
  853.   /**
  854.    * Sorts the instances based on an attribute. For numeric attributes, 
  855.    * instances are sorted in ascending order. For nominal attributes, 
  856.    * instances are sorted based on the attribute label ordering 
  857.    * specified in the header. Instances with missing values for the 
  858.    * attribute are placed at the end of the dataset.
  859.    *
  860.    * @param attIndex the attribute's index
  861.    */
  862.   public final void sort(int attIndex) {
  863.     int i,j;
  864.     // move all instances with missing values to end
  865.     j = numInstances() - 1;
  866.     i = 0;
  867.     while (i <= j) {
  868.       if (instance(j).isMissing(attIndex)) {
  869. j--;
  870.       } else {
  871. if (instance(i).isMissing(attIndex)) {
  872.   swap(i,j);
  873.   j--;
  874. }
  875. i++;
  876.       }
  877.     }
  878.     quickSort(attIndex, 0, j);
  879.   }
  880.   /**
  881.    * Sorts the instances based on an attribute. For numeric attributes, 
  882.    * instances are sorted into ascending order. For nominal attributes, 
  883.    * instances are sorted based on the attribute label ordering 
  884.    * specified in the header. Instances with missing values for the 
  885.    * attribute are placed at the end of the dataset.
  886.    *
  887.    * @param att the attribute
  888.    */
  889.   public final void sort(Attribute att) {
  890.     sort(att.index());
  891.   }
  892.   /**
  893.    * Stratifies a set of instances according to its class values 
  894.    * if the class attribute is nominal (so that afterwards a 
  895.    * stratified cross-validation can be performed).
  896.    *
  897.    * @param numFolds the number of folds in the cross-validation
  898.    * @exception UnassignedClassException if the class is not set
  899.    */
  900.   public final void stratify(int numFolds) {
  901.     
  902.     if (numFolds <= 0) {
  903.       throw new IllegalArgumentException("Number of folds must be greater than 1");
  904.     }
  905.     if (m_ClassIndex < 0) {
  906.       throw new UnassignedClassException("Class index is negative (not set)!");
  907.     }
  908.     if (classAttribute().isNominal()) {
  909.       // sort by class
  910.       int index = 1;
  911.       while (index < numInstances()) {
  912. Instance instance1 = instance(index - 1);
  913. for (int j = index; j < numInstances(); j++) {
  914.   Instance instance2 = instance(j);
  915.   if ((instance1.classValue() == instance2.classValue()) ||
  916.       (instance1.classIsMissing() && 
  917.        instance2.classIsMissing())) {
  918.     swap(index,j);
  919.     index++;
  920.   }
  921. }
  922. index++;
  923.       }
  924.       stratStep(numFolds);
  925.     }
  926.   }
  927.  
  928.   /**
  929.    * Computes the sum of all the instances' weights.
  930.    *
  931.    * @return the sum of all the instances' weights as a double
  932.    */
  933.   public final double sumOfWeights() {
  934.     
  935.     double sum = 0;
  936.     for (int i = 0; i < numInstances(); i++) {
  937.       sum += instance(i).weight();
  938.     }
  939.     return sum;
  940.   }
  941.   /**
  942.    * Creates the test set for one fold of a cross-validation on 
  943.    * the dataset.
  944.    *
  945.    * @param numFolds the number of folds in the cross-validation. Must
  946.    * be greater than 1.
  947.    * @param numFold 0 for the first fold, 1 for the second, ...
  948.    * @return the test set as a set of weighted instances
  949.    * @exception IllegalArgumentException if the number of folds is less than 2
  950.    * or greater than the number of instances.
  951.    */
  952.   public Instances testCV(int numFolds, int numFold) {
  953.     int numInstForFold, first, offset;
  954.     Instances test;
  955.     
  956.     if (numFolds < 2) {
  957.       throw new IllegalArgumentException("Number of folds must be at least 2!");
  958.     }
  959.     if (numFolds > numInstances()) {
  960.       throw new IllegalArgumentException("Can't have more folds than instances!");
  961.     }
  962.     numInstForFold = numInstances() / numFolds;
  963.     if (numFold < numInstances() % numFolds){
  964.       numInstForFold++;
  965.       offset = numFold;
  966.     }else
  967.       offset = numInstances() % numFolds;
  968.     test = new Instances(this, numInstForFold);
  969.     first = numFold * (numInstances() / numFolds) + offset;
  970.     copyInstances(first, test, numInstForFold);
  971.     return test;
  972.   }
  973.  
  974.   /**
  975.    * Returns the dataset as a string in ARFF format. Strings
  976.    * are quoted if they contain whitespace characters, or if they
  977.    * are a question mark.
  978.    *
  979.    * @return the dataset in ARFF format as a string
  980.    */
  981.   public final String toString() {
  982.     
  983.     StringBuffer text = new StringBuffer();
  984.     
  985.     text.append("@relation " + Utils.quote(m_RelationName) + "nn");
  986.     for (int i = 0; i < numAttributes(); i++) {
  987.       text.append(attribute(i) + "n");
  988.     }
  989.     text.append("n@datan");
  990.     for (int i = 0; i < numInstances(); i++) {
  991.       text.append(instance(i));
  992.       if (i < numInstances() - 1) {
  993. text.append('n');
  994.       }
  995.     }
  996.     return text.toString();
  997.   }
  998.   /**
  999.    * Creates the training set for one fold of a cross-validation 
  1000.    * on the dataset.
  1001.    *
  1002.    * @param numFolds the number of folds in the cross-validation. Must
  1003.    * be greater than 1.
  1004.    * @param numFold 0 for the first fold, 1 for the second, ...
  1005.    * @return the training set as a set of weighted 
  1006.    * instances
  1007.    * @exception IllegalArgumentException if the number of folds is less than 2
  1008.    * or greater than the number of instances.
  1009.    */
  1010.   public Instances trainCV(int numFolds, int numFold) {
  1011.     int numInstForFold, first, offset;
  1012.     Instances train;
  1013.  
  1014.     if (numFolds < 2) {
  1015.       throw new IllegalArgumentException("Number of folds must be at least 2!");
  1016.     }
  1017.     if (numFolds > numInstances()) {
  1018.       throw new IllegalArgumentException("Can't have more folds than instances!");
  1019.     }
  1020.     numInstForFold = numInstances() / numFolds;
  1021.     if (numFold < numInstances() % numFolds) {
  1022.       numInstForFold++;
  1023.       offset = numFold;
  1024.     }else
  1025.       offset = numInstances() % numFolds;
  1026.     train = new Instances(this, numInstances() - numInstForFold);
  1027.     first = numFold * (numInstances() / numFolds) + offset;
  1028.     copyInstances(0, train, first);
  1029.     copyInstances(first + numInstForFold, train,
  1030.   numInstances() - first - numInstForFold);
  1031.     return train;
  1032.   }
  1033.   /**
  1034.    * Computes the variance for a numeric attribute.
  1035.    *
  1036.    * @param attIndex the numeric attribute
  1037.    * @return the variance if the attribute is numeric
  1038.    * @exception IllegalArgumentException if the attribute is not numeric
  1039.    */
  1040.   public final double variance(int attIndex) {
  1041.   
  1042.     double sum = 0, sumSquared = 0, sumOfWeights = 0;
  1043.     if (!attribute(attIndex).isNumeric()) {
  1044.       throw new IllegalArgumentException("Can't compute variance because attribute is " +
  1045.   "not numeric!");
  1046.     }
  1047.     for (int i = 0; i < numInstances(); i++) {
  1048.       if (!instance(i).isMissing(attIndex)) {
  1049. sum += instance(i).weight() * 
  1050.   instance(i).value(attIndex);
  1051. sumSquared += instance(i).weight() * 
  1052.   instance(i).value(attIndex) *
  1053.   instance(i).value(attIndex);
  1054. sumOfWeights += instance(i).weight();
  1055.       }
  1056.     }
  1057.     if (Utils.smOrEq(sumOfWeights, 1)) {
  1058.       return 0;
  1059.     }
  1060.     double result = (sumSquared - (sum * sum / sumOfWeights)) / 
  1061.       (sumOfWeights - 1);
  1062.     // We don't like negative variance
  1063.     if (result < 0) {
  1064.       return 0;
  1065.     } else {
  1066.       return result;
  1067.     }
  1068.   }
  1069.   /**
  1070.    * Computes the variance for a numeric attribute.
  1071.    *
  1072.    * @param att the numeric attribute
  1073.    * @return the variance if the attribute is numeric
  1074.    * @exception IllegalArgumentException if the attribute is not numeric
  1075.    */
  1076.   public final double variance(Attribute att) {
  1077.     
  1078.     return variance(att.index());
  1079.   }
  1080.   
  1081.   /**
  1082.    * Calculates summary statistics on the values that appear in this
  1083.    * set of instances for a specified attribute.
  1084.    *
  1085.    * @param index the index of the attribute to summarize.
  1086.    * @return an AttributeStats object with it's fields calculated.
  1087.    */
  1088.   public AttributeStats attributeStats(int index) {
  1089.     AttributeStats result = new AttributeStats();
  1090.     if (attribute(index).isNominal()) {
  1091.       result.nominalCounts = new int [attribute(index).numValues()];
  1092.     }
  1093.     if (attribute(index).isNumeric()) {
  1094.       result.numericStats = new weka.experiment.Stats();
  1095.     }
  1096.     result.totalCount = numInstances();
  1097.     double [] attVals = attributeToDoubleArray(index);
  1098.     int [] sorted = Utils.sort(attVals);
  1099.     int currentCount = 0;
  1100.     double prev = Instance.missingValue();
  1101.     for (int j = 0; j < numInstances(); j++) {
  1102.       Instance current = instance(sorted[j]);
  1103.       if (current.isMissing(index)) {
  1104. result.missingCount = numInstances() - j;
  1105. break;
  1106.       }
  1107.       if (Utils.eq(current.value(index), prev)) {
  1108. currentCount++;
  1109.       } else {
  1110. result.addDistinct(prev, currentCount);
  1111. currentCount = 1;
  1112. prev = current.value(index);
  1113.       }
  1114.     }
  1115.     result.addDistinct(prev, currentCount);
  1116.     result.distinctCount--; // So we don't count "missing" as a value 
  1117.     return result;
  1118.   }
  1119.   
  1120.   /**
  1121.    * Gets the value of all instances in this dataset for a particular
  1122.    * attribute. Useful in conjunction with Utils.sort to allow iterating
  1123.    * through the dataset in sorted order for some attribute.
  1124.    *
  1125.    * @param index the index of the attribute.
  1126.    * @return an array containing the value of the desired attribute for
  1127.    * each instance in the dataset. 
  1128.    */
  1129.   public double [] attributeToDoubleArray(int index) {
  1130.     double [] result = new double[numInstances()];
  1131.     for (int i = 0; i < result.length; i++) {
  1132.       result[i] = instance(i).value(index);
  1133.     }
  1134.     return result;
  1135.   }
  1136.   /**
  1137.    * Generates a string summarizing the set of instances. Gives a breakdown
  1138.    * for each attribute indicating the number of missing/discrete/unique
  1139.    * values and other information.
  1140.    *
  1141.    * @return a string summarizing the dataset
  1142.    */
  1143.   public String toSummaryString() {
  1144.     StringBuffer result = new StringBuffer();
  1145.     result.append("Relation Name:  ").append(relationName()).append('n');
  1146.     result.append("Num Instances:  ").append(numInstances()).append('n');
  1147.     result.append("Num Attributes: ").append(numAttributes()).append('n');
  1148.     result.append('n');
  1149.     result.append(Utils.padLeft("", 5)).append(Utils.padRight("Name", 25));
  1150.     result.append(Utils.padLeft("Type", 5)).append(Utils.padLeft("Nom", 5));
  1151.     result.append(Utils.padLeft("Int", 5)).append(Utils.padLeft("Real", 5));
  1152.     result.append(Utils.padLeft("Missing", 12));
  1153.     result.append(Utils.padLeft("Unique", 12));
  1154.     result.append(Utils.padLeft("Dist", 6)).append('n');
  1155.     for (int i = 0; i < numAttributes(); i++) {
  1156.       Attribute a = attribute(i);
  1157.       AttributeStats as = attributeStats(i);
  1158.       result.append(Utils.padLeft("" + (i + 1), 4)).append(' ');
  1159.       result.append(Utils.padRight(a.name(), 25)).append(' ');
  1160.       long percent;
  1161.       switch (a.type()) {
  1162.       case Attribute.NOMINAL:
  1163. result.append(Utils.padLeft("Nom", 4)).append(' ');
  1164. percent = Math.round(100.0 * as.intCount / as.totalCount);
  1165. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1166. result.append(Utils.padLeft("" + 0, 3)).append("% ");
  1167. percent = Math.round(100.0 * as.realCount / as.totalCount);
  1168. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1169. break;
  1170.       case Attribute.NUMERIC:
  1171. result.append(Utils.padLeft("Num", 4)).append(' ');
  1172. result.append(Utils.padLeft("" + 0, 3)).append("% ");
  1173. percent = Math.round(100.0 * as.intCount / as.totalCount);
  1174. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1175. percent = Math.round(100.0 * as.realCount / as.totalCount);
  1176. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1177. break;
  1178.       case Attribute.DATE:
  1179. result.append(Utils.padLeft("Dat", 4)).append(' ');
  1180. result.append(Utils.padLeft("" + 0, 3)).append("% ");
  1181. percent = Math.round(100.0 * as.intCount / as.totalCount);
  1182. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1183. percent = Math.round(100.0 * as.realCount / as.totalCount);
  1184. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1185. break;
  1186.       case Attribute.STRING:
  1187. result.append(Utils.padLeft("Str", 4)).append(' ');
  1188. percent = Math.round(100.0 * as.intCount / as.totalCount);
  1189. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1190. result.append(Utils.padLeft("" + 0, 3)).append("% ");
  1191. percent = Math.round(100.0 * as.realCount / as.totalCount);
  1192. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1193. break;
  1194.       default:
  1195. result.append(Utils.padLeft("???", 4)).append(' ');
  1196. result.append(Utils.padLeft("" + 0, 3)).append("% ");
  1197. percent = Math.round(100.0 * as.intCount / as.totalCount);
  1198. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1199. percent = Math.round(100.0 * as.realCount / as.totalCount);
  1200. result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1201. break;
  1202.       }
  1203.       result.append(Utils.padLeft("" + as.missingCount, 5)).append(" /");
  1204.       percent = Math.round(100.0 * as.missingCount / as.totalCount);
  1205.       result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1206.       result.append(Utils.padLeft("" + as.uniqueCount, 5)).append(" /");
  1207.       percent = Math.round(100.0 * as.uniqueCount / as.totalCount);
  1208.       result.append(Utils.padLeft("" + percent, 3)).append("% ");
  1209.       result.append(Utils.padLeft("" + as.distinctCount, 5)).append(' ');
  1210.       result.append('n');
  1211.     }
  1212.     return result.toString();
  1213.   }
  1214.   
  1215.   /**
  1216.    * Reads a single instance using the tokenizer and appends it
  1217.    * to the dataset. Automatically expands the dataset if it
  1218.    * is not large enough to hold the instance.
  1219.    *
  1220.    * @param tokenizer the tokenizer to be used
  1221.    * @param flag if method should test for carriage return after 
  1222.    * each instance
  1223.    * @return false if end of file has been reached
  1224.    * @exception IOException if the information is not read 
  1225.    * successfully
  1226.    */ 
  1227.   protected boolean getInstance(StreamTokenizer tokenizer, 
  1228. boolean flag) 
  1229.        throws IOException {
  1230.     
  1231.     // Check if any attributes have been declared.
  1232.     if (m_Attributes.size() == 0) {
  1233.       errms(tokenizer,"no header information available");
  1234.     }
  1235.     // Check if end of file reached.
  1236.     getFirstToken(tokenizer);
  1237.     if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
  1238.       return false;
  1239.     }
  1240.     
  1241.     // Parse instance
  1242.     if (tokenizer.ttype == '{') {
  1243.       return getInstanceSparse(tokenizer, flag);
  1244.     } else {
  1245.       return getInstanceFull(tokenizer, flag);
  1246.     }
  1247.   }
  1248.   /**
  1249.    * Reads a single instance using the tokenizer and appends it
  1250.    * to the dataset. Automatically expands the dataset if it
  1251.    * is not large enough to hold the instance.
  1252.    *
  1253.    * @param tokenizer the tokenizer to be used
  1254.    * @param flag if method should test for carriage return after 
  1255.    * each instance
  1256.    * @return false if end of file has been reached
  1257.    * @exception IOException if the information is not read 
  1258.    * successfully
  1259.    */ 
  1260.   protected boolean getInstanceSparse(StreamTokenizer tokenizer, 
  1261.       boolean flag) 
  1262.        throws IOException {
  1263.     int valIndex, numValues = 0, maxIndex = -1;
  1264.     
  1265.     // Get values
  1266.     do {
  1267.       
  1268.       // Get index
  1269.       getIndex(tokenizer);
  1270.       if (tokenizer.ttype == '}') {
  1271. break;
  1272.       }
  1273.        
  1274.       // Is index valid?
  1275.       try{
  1276. m_IndicesBuffer[numValues] = Integer.valueOf(tokenizer.sval).intValue();
  1277.       } catch (NumberFormatException e) {
  1278. errms(tokenizer,"index number expected");
  1279.       }
  1280.       if (m_IndicesBuffer[numValues] <= maxIndex) {
  1281. errms(tokenizer,"indices have to be ordered");
  1282.       }
  1283.       if ((m_IndicesBuffer[numValues] < 0) || 
  1284.   (m_IndicesBuffer[numValues] >= numAttributes())) {
  1285. errms(tokenizer,"index out of bounds");
  1286.       }
  1287.       maxIndex = m_IndicesBuffer[numValues];
  1288.       // Get value;
  1289.       getNextToken(tokenizer);
  1290.       // Check if value is missing.
  1291.       if  (tokenizer.ttype == '?') {
  1292. m_ValueBuffer[numValues] = Instance.missingValue();
  1293.       } else {
  1294. // Check if token is valid.
  1295. if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
  1296.   errms(tokenizer,"not a valid value");
  1297. }
  1298.         switch (attribute(m_IndicesBuffer[numValues]).type()) {
  1299.           case Attribute.NOMINAL:
  1300.             // Check if value appears in header.
  1301.             valIndex = 
  1302.               attribute(m_IndicesBuffer[numValues]).indexOfValue(tokenizer.sval);
  1303.             if (valIndex == -1) {
  1304.               errms(tokenizer,"nominal value not declared in header");
  1305.             }
  1306.             m_ValueBuffer[numValues] = (double)valIndex;
  1307.             break;
  1308. case Attribute.NUMERIC:
  1309.   // Check if value is really a number.
  1310.   try{
  1311.     m_ValueBuffer[numValues] = Double.valueOf(tokenizer.sval).
  1312.       doubleValue();
  1313.   } catch (NumberFormatException e) {
  1314.     errms(tokenizer,"number expected");
  1315.   }
  1316.           break;
  1317. case Attribute.STRING:
  1318.   m_ValueBuffer[numValues] = 
  1319.     attribute(m_IndicesBuffer[numValues]).addStringValue(tokenizer.sval);
  1320.           break;
  1321.         case Attribute.DATE:
  1322.           try {
  1323.             m_ValueBuffer[numValues] = 
  1324.               attribute(m_IndicesBuffer[numValues]).parseDate(tokenizer.sval);
  1325.           } catch (ParseException e) {
  1326.             errms(tokenizer,"unparseable date: " + tokenizer.sval);
  1327.           }
  1328.           break;
  1329.         default:
  1330.           errms(tokenizer,"unknown attribute type in column " + m_IndicesBuffer[numValues]);
  1331. }
  1332.       }
  1333.       numValues++;
  1334.     } while (true);
  1335.     if (flag) {
  1336.       getLastToken(tokenizer,true);
  1337.     }
  1338.       
  1339.     // Add instance to dataset
  1340.     double[] tempValues = new double[numValues];
  1341.     int[] tempIndices = new int[numValues];
  1342.     System.arraycopy(m_ValueBuffer, 0, tempValues, 0, numValues);
  1343.     System.arraycopy(m_IndicesBuffer, 0, tempIndices, 0, numValues);
  1344.     add(new SparseInstance(1, tempValues, tempIndices, numAttributes()));
  1345.     return true;
  1346.   }
  1347.   /**
  1348.    * Reads a single instance using the tokenizer and appends it
  1349.    * to the dataset. Automatically expands the dataset if it
  1350.    * is not large enough to hold the instance.
  1351.    *
  1352.    * @param tokenizer the tokenizer to be used
  1353.    * @param flag if method should test for carriage return after 
  1354.    * each instance
  1355.    * @return false if end of file has been reached
  1356.    * @exception IOException if the information is not read 
  1357.    * successfully
  1358.    */ 
  1359.   protected boolean getInstanceFull(StreamTokenizer tokenizer, 
  1360.     boolean flag) 
  1361.        throws IOException {
  1362.     double[] instance = new double[numAttributes()];
  1363.     int index;
  1364.     
  1365.     // Get values for all attributes.
  1366.     for (int i = 0; i < numAttributes(); i++){
  1367.       
  1368.       // Get next token
  1369.       if (i > 0) {
  1370. getNextToken(tokenizer);
  1371.       }
  1372.             
  1373.       // Check if value is missing.
  1374.       if  (tokenizer.ttype == '?') {
  1375. instance[i] = Instance.missingValue();
  1376.       } else {
  1377. // Check if token is valid.
  1378. if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
  1379.   errms(tokenizer,"not a valid value");
  1380. }
  1381.         switch (attribute(i).type()) {
  1382.         case Attribute.NOMINAL:
  1383.   // Check if value appears in header.
  1384.   index = attribute(i).indexOfValue(tokenizer.sval);
  1385.   if (index == -1) {
  1386.     errms(tokenizer,"nominal value not declared in header");
  1387.   }
  1388.   instance[i] = (double)index;
  1389.           break;
  1390. case Attribute.NUMERIC:
  1391.   // Check if value is really a number.
  1392.   try{
  1393.     instance[i] = Double.valueOf(tokenizer.sval).
  1394.       doubleValue();
  1395.   } catch (NumberFormatException e) {
  1396.     errms(tokenizer,"number expected");
  1397.   }
  1398.           break;
  1399. case Attribute.STRING:
  1400.   instance[i] = attribute(i).addStringValue(tokenizer.sval);
  1401.           break;
  1402.         case Attribute.DATE:
  1403.           try {
  1404.             instance[i] = attribute(i).parseDate(tokenizer.sval);
  1405.           } catch (ParseException e) {
  1406.             errms(tokenizer,"unparseable date: " + tokenizer.sval);
  1407.           }
  1408.           break;
  1409.         default:
  1410.           errms(tokenizer,"unknown attribute type in column " + i);
  1411. }
  1412.       }
  1413.     }
  1414.     if (flag) {
  1415.       getLastToken(tokenizer,true);
  1416.     }
  1417.       
  1418.     // Add instance to dataset
  1419.     add(new Instance(1, instance));
  1420.     return true;
  1421.   }
  1422.   /**
  1423.    * Reads and stores header of an ARFF file.
  1424.    *
  1425.    * @param tokenizer the stream tokenizer
  1426.    * @exception IOException if the information is not read 
  1427.    * successfully
  1428.    */ 
  1429.   protected void readHeader(StreamTokenizer tokenizer) 
  1430.      throws IOException{
  1431.     
  1432.     String attributeName;
  1433.     FastVector attributeValues;
  1434.     int i;
  1435.     // Get name of relation.
  1436.     getFirstToken(tokenizer);
  1437.     if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
  1438.       errms(tokenizer,"premature end of file");
  1439.     }
  1440.     if (tokenizer.sval.equalsIgnoreCase("@relation")){
  1441.       getNextToken(tokenizer);
  1442.       m_RelationName = tokenizer.sval;
  1443.       getLastToken(tokenizer,false);
  1444.     } else {
  1445.       errms(tokenizer,"keyword @relation expected");
  1446.     }
  1447.     // Create vectors to hold information temporarily.
  1448.     m_Attributes = new FastVector();
  1449.  
  1450.     // Get attribute declarations.
  1451.     getFirstToken(tokenizer);
  1452.     if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
  1453.       errms(tokenizer,"premature end of file");
  1454.     }
  1455.     while (tokenizer.sval.equalsIgnoreCase("@attribute")) {
  1456.       // Get attribute name.
  1457.       getNextToken(tokenizer);
  1458.       attributeName = tokenizer.sval;
  1459.       getNextToken(tokenizer);
  1460.       // Check if attribute is nominal.
  1461.       if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
  1462. // Attribute is real, integer, or string.
  1463. if (tokenizer.sval.equalsIgnoreCase("real") ||
  1464.     tokenizer.sval.equalsIgnoreCase("integer") ||
  1465.     tokenizer.sval.equalsIgnoreCase("numeric")) {
  1466.   m_Attributes.addElement(new Attribute(attributeName,
  1467.  numAttributes()));
  1468.   readTillEOL(tokenizer);
  1469. } else if (tokenizer.sval.equalsIgnoreCase("string")) {
  1470.   m_Attributes.
  1471.     addElement(new Attribute(attributeName, (FastVector)null,
  1472.      numAttributes()));
  1473.   readTillEOL(tokenizer);
  1474. } else if (tokenizer.sval.equalsIgnoreCase("date")) {
  1475.           String format = null;
  1476.           if (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {
  1477.             if ((tokenizer.ttype != StreamTokenizer.TT_WORD) &&
  1478.                 (tokenizer.ttype != ''') &&
  1479.                 (tokenizer.ttype != '"')) {
  1480.               errms(tokenizer,"not a valid date format");
  1481.             }
  1482.             format = tokenizer.sval;
  1483.             readTillEOL(tokenizer);
  1484.           } else {
  1485.             tokenizer.pushBack();
  1486.           }
  1487.   m_Attributes.addElement(new Attribute(attributeName, format,
  1488.                                                 numAttributes()));
  1489. } else {
  1490.   errms(tokenizer,"no valid attribute type or invalid "+
  1491. "enumeration");
  1492. }
  1493.       } else {
  1494. // Attribute is nominal.
  1495. attributeValues = new FastVector();
  1496. tokenizer.pushBack();
  1497. // Get values for nominal attribute.
  1498. if (tokenizer.nextToken() != '{') {
  1499.   errms(tokenizer,"{ expected at beginning of enumeration");
  1500. }
  1501. while (tokenizer.nextToken() != '}') {
  1502.   if (tokenizer.ttype == StreamTokenizer.TT_EOL) {
  1503.     errms(tokenizer,"} expected at end of enumeration");
  1504.   } else {
  1505.     attributeValues.addElement(tokenizer.sval);
  1506.   }
  1507. }
  1508. if (attributeValues.size() == 0) {
  1509.   errms(tokenizer,"no nominal values found");
  1510. }
  1511. m_Attributes.
  1512.   addElement(new Attribute(attributeName, attributeValues,
  1513.    numAttributes()));
  1514.       }
  1515.       getLastToken(tokenizer,false);
  1516.       getFirstToken(tokenizer);
  1517.       if (tokenizer.ttype == StreamTokenizer.TT_EOF)
  1518. errms(tokenizer,"premature end of file");
  1519.     }
  1520.     // Check if data part follows. We can't easily check for EOL.
  1521.     if (!tokenizer.sval.equalsIgnoreCase("@data")) {
  1522.       errms(tokenizer,"keyword @data expected");
  1523.     }
  1524.     
  1525.     // Check if any attributes have been declared.
  1526.     if (m_Attributes.size() == 0) {
  1527.       errms(tokenizer,"no attributes declared");
  1528.     }
  1529.     // Allocate buffers in case sparse instances have to be read
  1530.     m_ValueBuffer = new double[numAttributes()];
  1531.     m_IndicesBuffer = new int[numAttributes()];
  1532.   }
  1533.   /**
  1534.    * Copies instances from one set to the end of another 
  1535.    * one.
  1536.    *
  1537.    * @param source the source of the instances
  1538.    * @param from the position of the first instance to be copied
  1539.    * @param dest the destination for the instances
  1540.    * @param num the number of instances to be copied
  1541.    */
  1542.   private void copyInstances(int from, Instances dest, int num) {
  1543.     
  1544.     for (int i = 0; i < num; i++) {
  1545.       dest.add(instance(from + i));
  1546.     }
  1547.   }
  1548.   
  1549.   /**
  1550.    * Throws error message with line number and last token read.
  1551.    *
  1552.    * @param theMsg the error message to be thrown
  1553.    * @param tokenizer the stream tokenizer
  1554.    * @throws IOExcpetion containing the error message
  1555.    */
  1556.   private void errms(StreamTokenizer tokenizer, String theMsg) 
  1557.        throws IOException {
  1558.     
  1559.     throw new IOException(theMsg + ", read " + tokenizer.toString());
  1560.   }
  1561.   
  1562.   /**
  1563.    * Replaces the attribute information by a clone of
  1564.    * itself.
  1565.    */
  1566.   private void freshAttributeInfo() {
  1567.     m_Attributes = (FastVector) m_Attributes.copyElements();
  1568.   }
  1569.   /**
  1570.    * Gets next token, skipping empty lines.
  1571.    *
  1572.    * @param tokenizer the stream tokenizer
  1573.    * @exception IOException if reading the next token fails
  1574.    */
  1575.   private void getFirstToken(StreamTokenizer tokenizer) 
  1576.     throws IOException{
  1577.     
  1578.     while (tokenizer.nextToken() == StreamTokenizer.TT_EOL){};
  1579.     if ((tokenizer.ttype == ''') ||
  1580. (tokenizer.ttype == '"')) {
  1581.       tokenizer.ttype = StreamTokenizer.TT_WORD;
  1582.     } else if ((tokenizer.ttype == StreamTokenizer.TT_WORD) &&
  1583.        (tokenizer.sval.equals("?"))){
  1584.       tokenizer.ttype = '?';
  1585.     }
  1586.   }
  1587.   /**
  1588.    * Gets index, checking for a premature and of line.
  1589.    *
  1590.    * @param tokenizer the stream tokenizer
  1591.    * @exception IOException if it finds a premature end of line
  1592.    */
  1593.   private void getIndex(StreamTokenizer tokenizer) throws IOException{
  1594.     
  1595.     if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
  1596.       errms(tokenizer,"premature end of line");
  1597.     }
  1598.     if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
  1599.       errms(tokenizer,"premature end of file");
  1600.     }
  1601.   }
  1602.   
  1603.   /**
  1604.    * Gets token and checks if its end of line.
  1605.    *
  1606.    * @param tokenizer the stream tokenizer
  1607.    * @exception IOException if it doesn't find an end of line
  1608.    */
  1609.   private void getLastToken(StreamTokenizer tokenizer, boolean endOfFileOk) 
  1610.        throws IOException{
  1611.     if ((tokenizer.nextToken() != StreamTokenizer.TT_EOL) &&
  1612. ((tokenizer.ttype != StreamTokenizer.TT_EOF) || !endOfFileOk)) {
  1613.       errms(tokenizer,"end of line expected");
  1614.     }
  1615.   }
  1616.   /**
  1617.    * Gets next token, checking for a premature and of line.
  1618.    *
  1619.    * @param tokenizer the stream tokenizer
  1620.    * @exception IOException if it finds a premature end of line
  1621.    */
  1622.   private void getNextToken(StreamTokenizer tokenizer) 
  1623.        throws IOException{
  1624.     
  1625.     if (tokenizer.nextToken() == StreamTokenizer.TT_EOL) {
  1626.       errms(tokenizer,"premature end of line");
  1627.     }
  1628.     if (tokenizer.ttype == StreamTokenizer.TT_EOF) {
  1629.       errms(tokenizer,"premature end of file");
  1630.     } else if ((tokenizer.ttype == ''') ||
  1631.        (tokenizer.ttype == '"')) {
  1632.       tokenizer.ttype = StreamTokenizer.TT_WORD;
  1633.     } else if ((tokenizer.ttype == StreamTokenizer.TT_WORD) &&
  1634.        (tokenizer.sval.equals("?"))){
  1635.       tokenizer.ttype = '?';
  1636.     }
  1637.   }
  1638.   /**
  1639.    * Initializes the StreamTokenizer used for reading the ARFF file.
  1640.    *
  1641.    * @param tokenizer the stream tokenizer
  1642.    */
  1643.   private void initTokenizer(StreamTokenizer tokenizer){
  1644.     tokenizer.resetSyntax();         
  1645.     tokenizer.whitespaceChars(0, ' ');    
  1646.     tokenizer.wordChars(' '+1,'u00FF');
  1647.     tokenizer.whitespaceChars(',',',');
  1648.     tokenizer.commentChar('%');
  1649.     tokenizer.quoteChar('"');
  1650.     tokenizer.quoteChar(''');
  1651.     tokenizer.ordinaryChar('{');
  1652.     tokenizer.ordinaryChar('}');
  1653.     tokenizer.eolIsSignificant(true);
  1654.   }
  1655.  
  1656.   /**
  1657.    * Returns string including all instances, their weights and
  1658.    * their indices in the original dataset.
  1659.    *
  1660.    * @return description of instance and its weight as a string
  1661.    */
  1662.   private String instancesAndWeights(){
  1663.     StringBuffer text = new StringBuffer();
  1664.     for (int i = 0; i < numInstances(); i++) {
  1665.       text.append(instance(i) + " " + instance(i).weight());
  1666.       if (i < numInstances() - 1) {
  1667. text.append("n");
  1668.       }
  1669.     }
  1670.     return text.toString();
  1671.   }
  1672.   
  1673.   /**
  1674.    * Implements quicksort.
  1675.    *
  1676.    * @param attIndex the attribute's index
  1677.    * @param lo0 the first index of the subset to be sorted
  1678.    * @param hi0 the last index of the subset to be sorted
  1679.    */
  1680.   private void quickSort(int attIndex, int lo0, int hi0) {
  1681.     
  1682.     int lo = lo0, hi = hi0;
  1683.     double mid, midPlus, midMinus;
  1684.     
  1685.     if (hi0 > lo0) {
  1686.       
  1687.       // Arbitrarily establishing partition element as the 
  1688.       // midpoint of the array.
  1689.       mid = instance((lo0 + hi0) / 2).value(attIndex);
  1690.       midPlus = mid + 1e-6;
  1691.       midMinus = mid - 1e-6;
  1692.       // loop through the array until indices cross
  1693.       while(lo <= hi) {
  1694. // find the first element that is greater than or equal to 
  1695. // the partition element starting from the left Index.
  1696. while ((instance(lo).value(attIndex) < 
  1697. midMinus) && (lo < hi0)) {
  1698.   ++lo;
  1699. }
  1700. // find an element that is smaller than or equal to
  1701. // the partition element starting from the right Index.
  1702. while ((instance(hi).value(attIndex)  > 
  1703. midPlus) && (hi > lo0)) {
  1704.   --hi;
  1705. }
  1706. // if the indexes have not crossed, swap
  1707. if(lo <= hi) {
  1708.   swap(lo,hi);
  1709.   ++lo;
  1710.   --hi;
  1711. }
  1712.       }
  1713.       
  1714.       // If the right index has not reached the left side of array
  1715.       // must now sort the left partition.
  1716.       if(lo0 < hi) {
  1717. quickSort(attIndex,lo0,hi);
  1718.       }
  1719.       
  1720.       // If the left index has not reached the right side of array
  1721.       // must now sort the right partition.
  1722.       if(lo < hi0) {
  1723. quickSort(attIndex,lo,hi0);
  1724.       }
  1725.     }
  1726.   }
  1727.   /**
  1728.    * Reads and skips all tokens before next end of line token.
  1729.    *
  1730.    * @param tokenizer the stream tokenizer
  1731.    */
  1732.   private void readTillEOL(StreamTokenizer tokenizer) 
  1733.        throws IOException{
  1734.     
  1735.     while (tokenizer.nextToken() != StreamTokenizer.TT_EOL) {};
  1736.     tokenizer.pushBack();
  1737.   }
  1738.   /**
  1739.    * Help function needed for stratification of set.
  1740.    *
  1741.    * @param numFolds the number of folds for the stratification
  1742.    */
  1743.   private void stratStep (int numFolds){
  1744.     
  1745.     FastVector newVec = new FastVector(m_Instances.capacity());
  1746.     int start = 0, j;
  1747.     // create stratified batch
  1748.     while (newVec.size() < numInstances()) {
  1749.       j = start;
  1750.       while (j < numInstances()) {
  1751. newVec.addElement(instance(j));
  1752. j = j + numFolds;
  1753.       }
  1754.       start++;
  1755.     }
  1756.     m_Instances = newVec;
  1757.   }
  1758.   
  1759.   /**
  1760.    * Swaps two instances in the set.
  1761.    *
  1762.    * @param i the first instance's index
  1763.    * @param j the second instance's index
  1764.    */
  1765.   private void swap(int i, int j){
  1766.     
  1767.     m_Instances.swap(i, j);
  1768.   }
  1769.   /**
  1770.    * Merges two sets of Instances together. The resulting set will have
  1771.    * all the attributes of the first set plus all the attributes of the 
  1772.    * second set. The number of instances in both sets must be the same.
  1773.    *
  1774.    * @param first the first set of Instances
  1775.    * @param second the second set of Instances
  1776.    * @return the merged set of Instances
  1777.    * @exception IllegalArgumentException if the datasets are not the same size
  1778.    */
  1779.   public static Instances mergeInstances(Instances first, Instances second) {
  1780.     if (first.numInstances() != second.numInstances()) {
  1781.       throw new IllegalArgumentException("Instance sets must be of the same size");
  1782.     }
  1783.     // Create the vector of merged attributes
  1784.     FastVector newAttributes = new FastVector();
  1785.     for (int i = 0; i < first.numAttributes(); i++) {
  1786.       newAttributes.addElement(first.attribute(i));
  1787.     }
  1788.     for (int i = 0; i < second.numAttributes(); i++) {
  1789.       newAttributes.addElement(second.attribute(i));
  1790.     }
  1791.     
  1792.     // Create the set of Instances
  1793.     Instances merged = new Instances(first.relationName() + '_'
  1794.      + second.relationName(), 
  1795.      newAttributes, 
  1796.      first.numInstances());
  1797.     // Merge each instance
  1798.     for (int i = 0; i < first.numInstances(); i++) {
  1799.       merged.add(first.instance(i).mergeInstance(second.instance(i)));
  1800.     }
  1801.     return merged;
  1802.   }
  1803.   /**
  1804.    * Method for testing this class.
  1805.    *
  1806.    * @param argv should contain one element: the name of an ARFF file
  1807.    */
  1808.   public static void test(String [] argv) {
  1809.     Instances instances, secondInstances, train, test, transformed, empty;
  1810.     Instance instance;
  1811.     Random random = new Random(2);
  1812.     Reader reader;
  1813.     int start, num;
  1814.     double newWeight;
  1815.     FastVector testAtts, testVals;
  1816.     int i,j;
  1817.     
  1818.     try{
  1819.       if (argv.length > 1) {
  1820. throw (new Exception("Usage: Instances [<filename>]"));
  1821.       }
  1822.       
  1823.       // Creating set of instances from scratch
  1824.       testVals = new FastVector(2);
  1825.       testVals.addElement("first_value");
  1826.       testVals.addElement("second_value");
  1827.       testAtts = new FastVector(2);
  1828.       testAtts.addElement(new Attribute("nominal_attribute", testVals));
  1829.       testAtts.addElement(new Attribute("numeric_attribute"));
  1830.       instances = new Instances("test_set", testAtts, 10);
  1831.       instances.add(new Instance(instances.numAttributes()));
  1832.       instances.add(new Instance(instances.numAttributes()));
  1833.       instances.add(new Instance(instances.numAttributes()));
  1834.       instances.setClassIndex(0);
  1835.       System.out.println("nSet of instances created from scratch:n");
  1836.       System.out.println(instances);
  1837.       
  1838.       if (argv.length == 1) {
  1839. String filename = argv[0];
  1840. reader = new FileReader(filename);
  1841. // Read first five instances and print them
  1842. System.out.println("nFirst five instances from file:n");
  1843. instances = new Instances(reader, 1);
  1844. instances.setClassIndex(instances.numAttributes() - 1);
  1845. i = 0;
  1846. while ((i < 5) && (instances.readInstance(reader))) {
  1847.   i++;
  1848. }
  1849. System.out.println(instances);
  1850. // Read all the instances in the file
  1851. reader = new FileReader(filename);
  1852. instances = new Instances(reader);
  1853. // Make the last attribute be the class 
  1854. instances.setClassIndex(instances.numAttributes() - 1);
  1855. // Print header and instances.
  1856. System.out.println("nDataset:n");
  1857. System.out.println(instances);
  1858. System.out.println("nClass index: "+instances.classIndex());
  1859.       }
  1860.       
  1861.       // Test basic methods based on class index.
  1862.       System.out.println("nClass name: "+instances.classAttribute().name());
  1863.       System.out.println("nClass index: "+instances.classIndex());
  1864.       System.out.println("nClass is nominal: " +
  1865.  instances.classAttribute().isNominal());
  1866.       System.out.println("nClass is numeric: " +
  1867.  instances.classAttribute().isNumeric());
  1868.       System.out.println("nClasses:n");
  1869.       for (i = 0; i < instances.numClasses(); i++) {
  1870. System.out.println(instances.classAttribute().value(i));
  1871.       }
  1872.       System.out.println("nClass values and labels of instances:n");
  1873.       for (i = 0; i < instances.numInstances(); i++) {
  1874. Instance inst = instances.instance(i);
  1875. System.out.print(inst.classValue() + "t");
  1876. System.out.print(inst.toString(inst.classIndex()));
  1877. if (instances.instance(i).classIsMissing()) {
  1878.   System.out.println("tis missing");
  1879. } else {
  1880.   System.out.println();
  1881. }
  1882.       }
  1883.       
  1884.       // Create random weights.
  1885.       System.out.println("nCreating random weights for instances.");
  1886.       for (i = 0; i < instances.numInstances(); i++) {
  1887. instances.instance(i).setWeight(random.nextDouble()); 
  1888.       }
  1889.       
  1890.       // Print all instances and their weights (and the sum of weights).
  1891.       System.out.println("nInstances and their weights:n");
  1892.       System.out.println(instances.instancesAndWeights());
  1893.       System.out.print("nSum of weights: ");
  1894.       System.out.println(instances.sumOfWeights());
  1895.       
  1896.       // Insert an attribute
  1897.       secondInstances = new Instances(instances);
  1898.       Attribute testAtt = new Attribute("Inserted");
  1899.       secondInstances.insertAttributeAt(testAtt, 0);
  1900.       System.out.println("nSet with inserted attribute:n");
  1901.       System.out.println(secondInstances);
  1902.       System.out.println("nClass name: "
  1903.  + secondInstances.classAttribute().name());
  1904.       
  1905.       // Delete the attribute
  1906.       secondInstances.deleteAttributeAt(0);
  1907.       System.out.println("nSet with attribute deleted:n");
  1908.       System.out.println(secondInstances);
  1909.       System.out.println("nClass name: "
  1910.  + secondInstances.classAttribute().name());
  1911.       
  1912.       // Test if headers are equal
  1913.       System.out.println("nHeaders equal: "+
  1914.  instances.equalHeaders(secondInstances) + "n");
  1915.       
  1916.       // Print data in internal format.
  1917.       System.out.println("nData (internal values):n");
  1918.       for (i = 0; i < instances.numInstances(); i++) {
  1919. for (j = 0; j < instances.numAttributes(); j++) {
  1920.   if (instances.instance(i).isMissing(j)) {
  1921.     System.out.print("? ");
  1922.   } else {
  1923.     System.out.print(instances.instance(i).value(j) + " ");
  1924.   }
  1925. }
  1926. System.out.println();
  1927.       }
  1928.       
  1929.       // Just print header
  1930.       System.out.println("nEmpty dataset:n");
  1931.       empty = new Instances(instances, 0);
  1932.       System.out.println(empty);
  1933.       System.out.println("nClass name: "+empty.classAttribute().name());
  1934.       // Create copy and rename an attribute and a value (if possible)
  1935.       if (empty.classAttribute().isNominal()) {
  1936. Instances copy = new Instances(empty, 0);
  1937. copy.renameAttribute(copy.classAttribute(), "new_name");
  1938. copy.renameAttributeValue(copy.classAttribute(), 
  1939.   copy.classAttribute().value(0), 
  1940.   "new_val_name");
  1941. System.out.println("nDataset with names changed:n" + copy);
  1942. System.out.println("nOriginal dataset:n" + empty);
  1943.       }
  1944.       // Create and prints subset of instances.
  1945.       start = instances.numInstances() / 4;
  1946.       num = instances.numInstances() / 2;
  1947.       System.out.print("nSubset of dataset: ");
  1948.       System.out.println(num + " instances from " + (start + 1) 
  1949.  + ". instance");
  1950.       secondInstances = new Instances(instances, start, num);
  1951.       System.out.println("nClass name: "
  1952.  + secondInstances.classAttribute().name());
  1953.       // Print all instances and their weights (and the sum of weights).
  1954.       System.out.println("nInstances and their weights:n");
  1955.       System.out.println(secondInstances.instancesAndWeights());
  1956.       System.out.print("nSum of weights: ");
  1957.       System.out.println(secondInstances.sumOfWeights());
  1958.       
  1959.       // Create and print training and test sets for 3-fold
  1960.       // cross-validation.
  1961.       System.out.println("nTrain and test folds for 3-fold CV:");
  1962.       if (instances.classAttribute().isNominal()) {
  1963. instances.stratify(3);
  1964.       }
  1965.       for (j = 0; j < 3; j++) {
  1966.         train = instances.trainCV(3,j);
  1967. test = instances.testCV(3,j);
  1968.                       
  1969. // Print all instances and their weights (and the sum of weights).
  1970. System.out.println("nTrain: ");
  1971. System.out.println("nInstances and their weights:n");
  1972. System.out.println(train.instancesAndWeights());
  1973. System.out.print("nSum of weights: ");
  1974. System.out.println(train.sumOfWeights());
  1975. System.out.println("nClass name: "+train.classAttribute().name());
  1976. System.out.println("nTest: ");
  1977. System.out.println("nInstances and their weights:n");
  1978. System.out.println(test.instancesAndWeights());
  1979. System.out.print("nSum of weights: ");
  1980. System.out.println(test.sumOfWeights());
  1981. System.out.println("nClass name: "+test.classAttribute().name());
  1982.       }
  1983.       // Randomize instances and print them.
  1984.       System.out.println("nRandomized dataset:");
  1985.       instances.randomize(random);
  1986.       
  1987.       // Print all instances and their weights (and the sum of weights).
  1988.       System.out.println("nInstances and their weights:n");
  1989.       System.out.println(instances.instancesAndWeights());
  1990.       System.out.print("nSum of weights: ");
  1991.       System.out.println(instances.sumOfWeights());
  1992.       // Sort instances according to first attribute and
  1993.       // print them.
  1994.       System.out.print("nInstances sorted according to first attribute:n ");
  1995.       instances.sort(0);
  1996.         
  1997.       // Print all instances and their weights (and the sum of weights).
  1998.       System.out.println("nInstances and their weights:n");
  1999.       System.out.println(instances.instancesAndWeights());
  2000.       System.out.print("nSum of weights: ");
  2001.       System.out.println(instances.sumOfWeights());
  2002.     } catch (Exception e) {
  2003.       e.printStackTrace(); 
  2004.     }
  2005.   }
  2006.   /**
  2007.    * Main method for this class -- just prints a summary of a set
  2008.    * of instances.
  2009.    *
  2010.    * @param argv should contain one element: the name of an ARFF file
  2011.    */
  2012.   public static void main(String [] args) {
  2013.     try {
  2014.       Reader r = null;
  2015.       if (args.length > 1) {
  2016. throw (new Exception("Usage: Instances <filename>"));
  2017.       } else if (args.length == 0) {
  2018.         r = new BufferedReader(new InputStreamReader(System.in));
  2019.       } else {
  2020.         r = new BufferedReader(new FileReader(args[0]));
  2021.       }
  2022.       Instances i = new Instances(r);
  2023.       System.out.println(i.toSummaryString());
  2024.     } catch (Exception ex) {
  2025.       ex.printStackTrace();
  2026.       System.err.println(ex.getMessage());
  2027.     }
  2028.   }
  2029. }
  2030.