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