milk.core.Exemplars.java Source code

Java tutorial

Introduction

Here is the source code for milk.core.Exemplars.java

Source

/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *    Exemplars.java
 *    Copyright (C) 2002 University of Waikato
 *
 */
package milk.core;

import java.io.*;
import java.util.*;
import weka.core.*;

/**
 * The class of a set of exemplars
 *
 * @author Eibe Frank (eibe@cs.waikato.ac.nz)
 * @author Xin XU (xx5@cs.waikato.ac.nz)
 * @version $Revision: 1.1 $ 
 */
public class Exemplars implements Serializable {

    /** The dataset's name. */
    private String m_RelationName;

    /** The attribute information. */
    private Attribute[] m_Attributes;

    /** The exemplars. */
    private Vector m_Exemplars;

    /** The exemplars' ID attribute */
    private int m_IdIndex;

    /** The class index of this exemplar */
    private int m_ClassIndex;

    /** 
     * Constructor to form an Exemplars by deep copying from 
     * another Exemplars
     *
     * @param exemplars the copied Exemplars
     */
    public Exemplars(Exemplars exemplars) {
        m_IdIndex = exemplars.m_IdIndex;
        m_ClassIndex = exemplars.m_ClassIndex;
        m_RelationName = exemplars.m_RelationName;

        int attLen = exemplars.m_Attributes.length;
        m_Attributes = new Attribute[attLen];
        for (int i = 0; i < attLen; i++)
            m_Attributes[i] = (Attribute) exemplars.m_Attributes[i].copy();

        int exLen = exemplars.numExemplars();
        m_Exemplars = new Vector(exLen);
        for (int i = 0; i < exLen; i++)
            m_Exemplars.addElement(new Exemplar(exemplars.exemplar(i)));
    }

    /** 
     * Constructor creating an empty Exemplars with the same structure
     * of the given Exemplars and the given size (i.e. the number of
     * exemplars in the set)
     *
     * @param exemplars the given Exemplars
     * @param size the given size
     */
    public Exemplars(Exemplars exemplars, int size) {
        m_IdIndex = exemplars.m_IdIndex;
        m_ClassIndex = exemplars.m_ClassIndex;
        m_RelationName = exemplars.m_RelationName;

        int attLen = exemplars.m_Attributes.length;
        m_Attributes = new Attribute[attLen];
        for (int i = 0; i < attLen; i++)
            m_Attributes[i] = (Attribute) exemplars.m_Attributes[i].copy();

        m_Exemplars = new Vector(size);
    }

    /**
     * Constructor using the given dataset and set ID index to 0
     * 
     * @param dataset the set to be copied
     * @exception Exception if the class index of the dataset 
     * is not set(i.e. -1)
     */
    public Exemplars(Instances dataset) throws Exception {

        this(dataset, 0);
    }

    /**
     * Creates a new set of instances by copying a 
     * subset of another set.
     *
     * @param source the set of instances from which a subset 
     * is to be created
     * @param first the index of the first instance to be copied
     * @param toCopy the number of instances to be copied
     * @exception IllegalArgumentException if first and toCopy are out of range
     */
    public Exemplars(Exemplars source, int first, int toCopy) {

        this(source, toCopy);

        if ((first < 0) || ((first + toCopy) > source.numExemplars())) {
            throw new IllegalArgumentException("Parameters first and/or toCopy out " + "of range");
        }
        source.copyExemplars(first, this, toCopy);
    }

    /**
     * Constructor using the given dataset and set ID index to 
     * the given ID index.  Any instances with class value or ID
     * value missing will be dropped.
     *
     * @param dataset the instances from which the header 
     * information is to be taken
     * @param idIndex the ID attribute's index 
     * @exception Exception if the class index of the dataset 
     * is not set(i.e. -1) or the data is not a multi-instance data
     */
    public Exemplars(Instances dataset, int idIndex) throws Exception {
        if (dataset.classIndex() == -1)
            throw new Exception(" Class Index negative (class not set yet)!");

        m_ClassIndex = dataset.classIndex();
        m_RelationName = dataset.relationName();
        int numAttr = dataset.numAttributes();
        m_Attributes = new Attribute[numAttr];
        for (int i = 0; i < numAttr; i++)
            m_Attributes[i] = dataset.attribute(i);

        m_IdIndex = idIndex;
        Attribute id = m_Attributes[m_IdIndex];
        if ((m_IdIndex > numAttr) || (m_IdIndex < 0) || (!id.isNominal()))
            throw new Exception("ID index is wrong!");

        m_Exemplars = new Vector(id.numValues());

        for (int j = 0; j < dataset.numInstances(); j++) {
            Instance ins = dataset.instance(j);
            add(ins);
        }
    }

    /**
     * Adds one instance to one of the exemplars 
     *
     * @param instance the instance to be added
     * @exception Exception if the instance cannot be added properly
     */
    public final void add(Instance instance) {
        Instance ins = (Instance) instance.copy();

        int idv = (int) ins.value(m_IdIndex);
        int x = 0;
        for (; x < m_Exemplars.size(); x++) {
            Exemplar ex = (Exemplar) m_Exemplars.elementAt(x);
            if (ex != null) {
                if ((int) (ex.idValue()) == idv) {
                    if (!ex.checkInstance(instance))
                        throw new IllegalArgumentException("Instance not compatible " + "with the data");
                    ex.add(ins);
                    break;
                }
            }
        }
        if (x == m_Exemplars.size()) {
            Exemplar ex = new Exemplar(ins, m_IdIndex);
            ex.setWeight(1.0);
            m_Exemplars.addElement(ex);
        }
    }

    /**
     * Adds one exemplar to the exemplars 
     *
     * @param exemplar the exemplar to be added
     * @exception Exception if the exemplar already exists
     */
    public final void add(Exemplar exemplar) {
        int idv = (int) exemplar.idValue();
        for (int x = 0; x < m_Exemplars.size(); x++) {
            Exemplar ex = (Exemplar) m_Exemplars.elementAt(x);
            if ((int) (ex.idValue()) == idv)
                throw new IllegalArgumentException("Exemplar already exists in the Exemplars");
        }
        m_Exemplars.addElement(new Exemplar(exemplar));
    }

    /**
     * Returns an attribute.
     *
     * @param index the attribute's index
     * @return the attribute at the given position
     */
    public final Attribute attribute(int index) {

        return m_Attributes[index];
    }

    /**
     * Returns an attribute given its name. If there is more than
     * one attribute with the same name, it returns the first one.
     * Returns null if the attribute can't be found.
     *
     * @param name the attribute's name
     * @return the attribute with the given name, null if the
     * attribute can't be found
     */
    public final Attribute attribute(String name) {

        for (int i = 0; i < numAttributes(); i++) {
            if (attribute(i).name().equals(name)) {
                return attribute(i);
            }
        }
        return null;
    }

    /**
     * Checks for string attributes in the Exemplars
     *
     * @return true if string attributes are present, false otherwise
     */
    public boolean checkForStringAttributes() {

        int i = 0;

        while (i < m_Attributes.length) {
            if (attribute(i++).isString()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the class attribute.
     *
     * @return the class attribute
     * @exception UnassignedClassException if the class is not set
     */
    public final Attribute classAttribute() {

        if (m_ClassIndex < 0) {
            throw new UnassignedClassException("Class index is negative (not set)!");
        }
        return attribute(m_ClassIndex);
    }

    /**
     * Returns the class attribute's index. Returns negative number
     * if it's undefined.
     *
     * @return the class index as an integer
     */
    public final int classIndex() {

        return m_ClassIndex;
    }

    /**
     * Compactifies each exemplar in this Exemplars
     */
    public final void compactify() {

        for (int i = 0; i < m_Exemplars.size(); i++)
            ((Exemplar) m_Exemplars.elementAt(i)).compactify();
        m_Exemplars.trimToSize();
    }

    /**
     * Copies instances from one set to the end of another 
     * one.
     *
     * @param source the source of the instances
     * @param from the position of the first instance to be copied
     * @param dest the destination for the instances
     * @param num the number of instances to be copied
     */
    private void copyExemplars(int from, Exemplars dest, int num) {

        for (int i = 0; i < num; i++) {
            dest.add(exemplar(from + i));
        }
    }

    /**
     * Removes all Exemplars from the set.
     */
    public final void delete() {
        m_Exemplars.removeAllElements();
    }

    /**
     * Removes an exemplar at the given position from the set.
     *
     * @param index the instance's position
     */
    public final void delete(int index) {
        m_Exemplars.removeElementAt(index);

    }

    /**
     * Deletes an attribute at the given position 
     * (0 to numAttributes() - 1). 
     *
     * @param pos the attribute's position
     * @exception Exception if the given index is out of range or the
     * class attribute is being deleted
     */
    public void deleteAttributeAt(int position) throws Exception {
        for (int i = 0; i < m_Exemplars.size(); i++)
            ((Exemplar) m_Exemplars.elementAt(i)).deleteAttributeAt(position);

        Exemplar eg = (Exemplar) m_Exemplars.firstElement();
        int len = m_Attributes.length - 1;
        for (int j = position; j < len; j++)
            m_Attributes[j] = eg.getInstances().attribute(j);
        m_Attributes[len] = null;

        m_ClassIndex = eg.classIndex();
        m_IdIndex = eg.idIndex();
    }

    /**
     * Deletes all string attributes in the dataset. A deep copy of the attribute
     * information is performed before an attribute is deleted.
     *
     * @exception IllegalArgumentException if string attribute couldn't be 
     * successfully deleted (probably because it is the class attribute).
     */
    public void deleteStringAttributes() throws Exception {

        int i = 0;
        while (i < m_Attributes.length) {
            if (attribute(i).isString()) {
                deleteAttributeAt(i);
            } else {
                i++;
            }
        }
    }

    /**
     * Removes all instances with missing values for a particular
     * attribute from the dataset.
     *
     * @param attIndex the attribute's index
     */
    public final void deleteWithMissing(int attIndex) {

        for (int i = 0; i < m_Exemplars.size(); i++)
            ((Exemplar) m_Exemplars.elementAt(i)).getInstances().deleteWithMissing(attIndex);
    }

    /**
     * Removes all instances with missing values for a particular
     * attribute from the dataset.
     *
     * @param att the attribute
     */
    public final void deleteWithMissing(Attribute att) {

        deleteWithMissing(att.index());
    }

    /**
     * Returns an enumeration of all the attributes.
     *
     * @return enumeration of all the attributes.
     */
    public Enumeration enumerateAttributes() {

        return ((Exemplar) m_Exemplars.firstElement()).enumerateAttributes();
    }

    /**
     * Returns a vector of exemplars in this Exemplars.
     *
     * @return a vector of all exemplars
     */
    public final Vector getExemplars() {

        return m_Exemplars;
    }

    /**
     * Returns the first exemplar in the set.
     *
     * @return the first exemplar in the set
     */
    public final Exemplar firstExemplar() {

        return (Exemplar) m_Exemplars.firstElement();
    }

    /**
     * Returns the ID attribute.
     *
     * @return the ID attribute
     */
    public final Attribute idAttribute() {

        return attribute(m_IdIndex);
    }

    /**
     * Returns the ID attribute's index. 
     *
     * @return the ID index as an integer
     */
    public final int idIndex() {

        return m_IdIndex;
    }

    /**
     * Inserts an attribute at the given position (0 to 
     * numAttributes()) and sets all values to be missing.
     *
     * @param att the attribute to be inserted
     * @param pos the attribute's position
     * @exception IllegalArgumentException if the given index is out of range
     */
    public void insertAttributeAt(Attribute att, int position) {
        for (int i = 0; i < m_Exemplars.size(); i++)
            ((Exemplar) m_Exemplars.elementAt(i)).insertAttributeAt(att, position);

        Exemplar eg = (Exemplar) m_Exemplars.firstElement();
        int len = m_Attributes.length + 1;
        m_Attributes = new Attribute[len];
        for (int j = position; j < len; j++)
            m_Attributes[j] = eg.getInstances().attribute(j);

        m_ClassIndex = eg.classIndex();
        m_IdIndex = eg.idIndex();
    }

    /**
    * Returns the exemplar at the given position.
    *
    * @param index the exemplar's index
    * @return the exemplar at the given position
    */
    public final Exemplar exemplar(int index) {

        return (Exemplar) m_Exemplars.elementAt(index);
    }

    /**
     * Returns the last exemplar in the set.
     *
     * @return the last exemplar in the set
     */
    public final Exemplar lastExemplar() {
        return (Exemplar) m_Exemplars.lastElement();
    }

    /**
     * Returns the number of attributes.
     *
     * @return the number of attributes as an integer
     */
    public final int numAttributes() {

        return m_Attributes.length;
    }

    /**
     * Returns the number of class labels.
     *
     * @return the number of class labels as an integer if the class 
     * attribute is nominal, 1 otherwise.
     * @exception UnassignedClassException if the class is not set
     */
    public final int numClasses() {

        if (m_ClassIndex < 0) {
            throw new UnassignedClassException("Class index is negative (not set)!");
        }
        if (!classAttribute().isNominal()) {
            return 1;
        } else {
            return classAttribute().numValues();
        }
    }

    /**
     * Returns the number of exemplars in the set.
     *
     * @param att the attribute
     * @return the number of distinct values of exemplar
     */
    public final int numExemplars() {

        return m_Exemplars.size();
    }

    /**
     * Returns the number of instances in the dataset.
     *
     * @return the number of instances in the dataset as an integer array
     */
    public final int[] numsInstances() {

        int[] size = new int[numExemplars()];
        for (int i = 0; i < m_Exemplars.size(); i++)
            size[i] = ((Exemplar) m_Exemplars.elementAt(i)).getInstances().numInstances();

        return size;
    }

    /**
     * Shuffles the exemplars in the set so that they are ordered 
     * randomly.
     *
     * @param random a random number generator
     */
    public final void randomize(Random random) {

        for (int j = numExemplars() - 1; j > 0; j--)
            swap(j, random.nextInt(j + 1));
    }

    /**
     * Returns the relation's name.
     *
     * @return the relation's name as a string
     */
    public final String relationName() {

        return m_RelationName;
    }

    /**
     * Renames an attribute.
     *
     * @param att the attribute's index
     * @param name the new name
     */
    public final void renameAttribute(int att, String name) {

        for (int i = 0; i < m_Exemplars.size(); i++)
            ((Exemplar) m_Exemplars.elementAt(i)).getInstances().renameAttribute(att, name);
    }

    /**
     * Renames an attribute.
     *
     * @param att the attribute
     * @param name the new name
     */
    public final void renameAttribute(Attribute att, String name) {

        renameAttribute(att.index(), name);
    }

    /**
     * Renames the value of a nominal (or string) attribute value.
     *
     * @param att the attribute's index
     * @param val the value's index
     * @param name the new name 
     */
    public final void renameAttributeValue(int att, int val, String name) {

        for (int i = 0; i < m_Exemplars.size(); i++)
            ((Exemplar) m_Exemplars.elementAt(i)).getInstances().renameAttributeValue(att, val, name);
    }

    /**
     * Renames the value of a nominal (or string) attribute value.
     *
     * @param att the attribute
     * @param val the value
     * @param name the new name
     */
    public final void renameAttributeValue(Attribute att, String val, String name) {

        int v = att.indexOfValue(val);
        if (v == -1)
            throw new IllegalArgumentException(val + " not found");
        renameAttributeValue(att.index(), v, name);
    }

    /**
     * Creates a new Exemplars of the same size using random sampling
     * with replacement.
     *
     * @param random a random number generator
     * @return the new Exemplars
     */
    public final Exemplars resample(Random random) {

        Exemplars newData = new Exemplars(this, numExemplars());
        while (newData.m_Exemplars.size() < numExemplars()) {
            int j = (int) (random.nextDouble() * (double) numExemplars());
            newData.m_Exemplars.addElement(new Exemplar(exemplar(j)));
        }
        return newData;
    }

    /**
     * Creates a new Exemplars of the same size using random sampling
     * with replacement according to the current exemplar weights. The
     * weights of the exemplars in the new set are set to one.
     *
     * @param random a random number generator
     * @return the new dataset
     * @exception Exception if the weights array is of the wrong
     * length or contains negative weights or 
     * any other errors related to exemplars.
     */
    public final Exemplars resampleWithWeights(Random random) throws Exception {

        double[] weights = new double[numExemplars()];
        boolean foundOne = false;
        for (int i = 0; i < weights.length; i++) {
            weights[i] = exemplar(i).weight();
            if (!Utils.eq(weights[i], weights[0])) {
                foundOne = true;
            }
        }
        if (foundOne) {
            return resampleWithWeights(random, weights);
        } else {
            return new Exemplars(this);
        }
    }

    /**
     * Creates a new dataset of the same size using random sampling
     * with replacement according to the given weight vector. The
     * weights of the exemplars in the new dataset are set to one.
     * The length of the weight vector has to be the same as the
     * number of exemplars in the dataset, and all weights have to
     * be positive.
     *
     * @param random a random number generator
     * @param weights the weight vector
     * @return the new dataset
     * @exception Exception if the weights array is of the wrong
     * length or contains negative weights or 
     * any other errors related to exemplars.
     */
    public final Exemplars resampleWithWeights(Random random, double[] weights) throws Exception {
        int len = weights.length;
        if (len != numExemplars()) {
            throw new IllegalArgumentException("weights.length != numExemplars.");
        }
        Exemplars newData = new Exemplars(this, len);
        double[] probabilities = new double[len];
        double sumProbs = 0, sumOfWeights = Utils.sum(weights);
        for (int i = 0; i < len; i++) {
            sumProbs += random.nextDouble();
            probabilities[i] = sumProbs;
        }
        Utils.normalize(probabilities, sumProbs / sumOfWeights);

        // Make sure that rounding errors don't mess things up
        probabilities[len - 1] = sumOfWeights;
        int k = 0;
        int l = 0;
        sumProbs = 0;
        while ((k < len && (l < len))) {
            if (weights[l] < 0) {
                throw new IllegalArgumentException("Weights have to be positive.");
            }
            sumProbs += weights[l];
            while ((k < len) && (probabilities[k] <= sumProbs)) {
                newData.m_Exemplars.addElement(new Exemplar(exemplar(l)));
                newData.exemplar(k).setWeight(1.0);
                k++;
            }
            l++;
        }
        return newData;
    }

    /**
     * Sets the relation's name.
     *
     * @param newName the new relation name.
     */
    public final void setRelationName(String newName) {

        m_RelationName = newName;
    }

    /**
     * Sorts the instances based on the ID attribute. For numeric attributes, 
     * instances are sorted in ascending order. For nominal attributes, 
     * instances are sorted based on the attribute label ordering 
     * specified in the header.  The instances inside an exemplar are not sorted.
     *
     */
    public final void sort() {

        int i, j;

        // move all instances with missing values to end
        j = numExemplars() - 1;
        i = 0;

        quickSort(0, j);
    }

    /**
     * Implements quicksort.
     *
     * @param lo0 the first index of the subset to be sorted
     * @param hi0 the last index of the subset to be sorted
     */
    private void quickSort(int lo0, int hi0) {

        int lo = lo0, hi = hi0;
        double mid, midPlus, midMinus;

        if (hi0 > lo0) {

            // Arbitrarily establishing partition element as the 
            // midpoint of the array.
            mid = exemplar((lo0 + hi0) / 2).idValue();
            midPlus = mid + 1e-6;
            midMinus = mid - 1e-6;

            // loop through the array until indices cross
            while (lo <= hi) {

                // find the first element that is greater than or equal to 
                // the partition element starting from the left Index.
                while ((exemplar(lo).idValue() < midMinus) && (lo < hi0)) {
                    ++lo;
                }

                // find an element that is smaller than or equal to
                // the partition element starting from the right Index.
                while ((exemplar(hi).idValue() > midPlus) && (hi > lo0)) {
                    --hi;
                }

                // if the indexes have not crossed, swap
                if (lo <= hi) {
                    swap(lo, hi);
                    ++lo;
                    --hi;
                }
            }

            // If the right index has not reached the left side of array
            // must now sort the left partition.
            if (lo0 < hi) {
                quickSort(lo0, hi);
            }

            // If the left index has not reached the right side of array
            // must now sort the right partition.
            if (lo < hi0) {
                quickSort(lo, hi0);
            }
        }
    }

    /**
     * Swaps two instances in the set.
     *
     * @param i the first instance's index
     * @param j the second instance's index
     */
    private void swap(int i, int j) {
        Exemplar tmp = new Exemplar((Exemplar) (m_Exemplars.elementAt(i)));
        m_Exemplars.setElementAt(new Exemplar((Exemplar) (m_Exemplars.elementAt(j))), i);
        m_Exemplars.setElementAt(tmp, j);
    }

    /**
     * Stratifies a set of exemplars according to its class values 
     * if the class attribute is nominal (so that afterwards a 
     * stratified cross-validation can be performed).
     *
     * @param numFolds the number of folds in the cross-validation
     * @exception UnassignedClassException if the class is not set
     */
    public final void stratify(int numFolds) {

        if (numFolds <= 0) {
            throw new IllegalArgumentException("Number of folds must be greater than 1");
        }
        if (m_ClassIndex < 0) {
            throw new UnassignedClassException("Class index is negative (not set)!");
        }
        if (classAttribute().isNominal()) {

            // sort by class
            int index = 1;
            while (index < numExemplars()) {
                Exemplar eg1 = exemplar(index - 1);
                for (int j = index; j < numExemplars(); j++) {
                    Exemplar eg2 = exemplar(j);
                    if (eg1.classValue() == eg2.classValue()) {
                        swap(index, j);
                        index++;
                    }
                }
                index++;
            }
            stratStep(numFolds);
        }
    }

    /**
     * Help function needed for stratification of set.
     *
     * @param numFolds the number of folds for the stratification
     */
    private void stratStep(int numFolds) {

        Vector newExm = new Vector(m_Exemplars.size());
        int start = 0, j;

        // create stratified batch
        while (newExm.size() < numExemplars()) {
            j = start;
            while (j < numExemplars()) {
                newExm.addElement(new Exemplar(exemplar(j)));
                j = j + numFolds;
            }
            start++;
        }

        m_Exemplars = newExm;
    }

    /**
     * Computes the sum of all the exemplars' weights.
     *
     * @return the sum of all the exemplars' weights as a double
     */
    public final double[] sumsOfWeights() {

        double[] sum = new double[numExemplars()];

        for (int i = 0; i < numExemplars(); i++) {
            sum[i] = exemplar(i).getInstances().sumOfWeights();
        }
        return sum;
    }

    /**
     * Creates the test set for one fold of a cross-validation on 
     * the dataset.
     *
     * @param numFolds the number of folds in the cross-validation. Must
     * be greater than 1.
     * @param numFold 0 for the first fold, 1 for the second, ...
     * @return the test set as a set of weighted instances
     * @exception Exception if the number of folds is less than 2
     * or greater than the number of exemplars
     * or any other errors related to exemplar occur
     */
    public Exemplars testCV(int numFolds, int numFold) throws Exception {

        int numExamForFold, first, offset;
        Exemplars test;

        if (numFolds < 2) {
            throw new IllegalArgumentException("Number of folds must be at least 2!");
        }
        if (numFolds > numExemplars()) {
            throw new IllegalArgumentException("Can't have more folds than Exemplars!");
        }
        numExamForFold = numExemplars() / numFolds;
        if (numFold < numExemplars() % numFolds) {
            numExamForFold++;
            offset = numFold;
        } else
            offset = numExemplars() % numFolds;

        first = numFold * (numExemplars() / numFolds) + offset;

        test = new Exemplars(this, numExamForFold);
        for (int i = 0; i < numExamForFold; i++)
            test.m_Exemplars.addElement(new Exemplar((Exemplar) m_Exemplars.elementAt(first + i)));

        return test;
    }

    /**
     * Returns the exemplars as a string. 
     * It only shows each exemplar's ID value, class value and weight
     * as well as the ARFF header of the dataset 
     *
     * @return the set of exemplars as a string
     */
    public final String toString() {

        StringBuffer text = new StringBuffer();
        text.append("@relation " + Utils.quote(m_RelationName) + "\n\n");
        for (int i = 0; i < m_Attributes.length; i++) {
            text.append(m_Attributes[i]);
            if (i == m_IdIndex)
                text.append(" (ID Attribute)");
            else if (i == m_ClassIndex)
                text.append(" (Class Attribute)");

            //text.append("\n");
        }

        Attribute id = idAttribute();
        Attribute cl = classAttribute();
        text.append("\n@Exemplars: \nID(" + id.name() + "); Class(" + cl.name()
                + "); Weight; sumOfInstances'Weights\n");

        double[] weights = sumsOfWeights();
        for (int j = 0; j < m_Exemplars.size(); j++) {
            Exemplar eg = (Exemplar) m_Exemplars.elementAt(j);
            text.append(id.value((int) eg.idValue()) + "; " + cl.value((int) eg.classValue()) + "; " + eg.weight()
                    + "; " + weights[j] + "\n");
        }
        text.append("There are totally " + numExemplars() + " exemplars");
        return text.toString();
    }

    /**
     * Creates the training set skipping for one fold of a cross-validation 
     * on the exemplar set.
     *
     * @param numFolds the number of folds in the cross-validation. Must
     * be greater than 1.
     * @param numFold 0 for the first fold, 1 for the second, ...
     * @return the training set as a set of weighted 
     * instances
     * @exception Exception if the number of folds is less than 2
     * or greater than the number of exemplars or
     * or any other errors related to exemplar occur.
     */
    public Exemplars trainCV(int numFolds, int numFold) throws Exception {

        int numExamForFold, first, offset;
        Exemplars train;

        if (numFolds < 2)
            throw new IllegalArgumentException("Number of folds must be at least 2!");

        if (numFolds > numExemplars())
            throw new IllegalArgumentException("Can't have more folds than exemplars!");

        numExamForFold = numExemplars() / numFolds;
        if (numFold < numExemplars() % numFolds) {
            numExamForFold++;
            offset = numFold;
        } else
            offset = numExemplars() % numFolds;

        first = numFold * (numExemplars() / numFolds) + offset;
        train = new Exemplars(this, numExemplars() - numExamForFold);

        for (int i = 0; i < first; i++)
            train.m_Exemplars.addElement(new Exemplar((Exemplar) m_Exemplars.elementAt(i)));

        for (int i = first; i < numExemplars() - numExamForFold; i++)
            train.m_Exemplars.addElement(new Exemplar((Exemplar) m_Exemplars.elementAt(numExamForFold + i)));

        return train;
    }

    /**
     * Creates the training set for one fold of a cross-validation 
     * on the dataset. The data is subsequently randomized based
     * on the given random number generator.
     *
     * @param numFolds the number of folds in the cross-validation. Must
     * be greater than 1.
     * @param numFold 0 for the first fold, 1 for the second, ...
     * @param random the random number generator
     * @return the training set 
     * @exception IllegalArgumentException if the number of folds is less than 2
     * or greater than the number of instances.
     */
    public Exemplars trainCV(int numFolds, int numFold, Random random) throws Exception {

        Exemplars train = trainCV(numFolds, numFold);
        train.randomize(random);
        return train;
    }

    /**
     * Main method for this class -- just performone run of 10-fold CV
     * and prints out the set.  Assume ID is the first attribute and class
     * is the last one.
     *
     * @param argv should contain one element: the name of an ARFF file
     */
    public static void main(String[] args) {

        try {
            Reader r = null;
            if (args.length > 1) {
                throw (new Exception("Usage: Exemplers <filename>"));
            } else if (args.length == 0) {
                r = new BufferedReader(new InputStreamReader(System.in));
            } else {
                r = new BufferedReader(new FileReader(args[0]));
            }

            Instances data = new Instances(r);
            data.setClassIndex(data.numAttributes() - 1);
            Exemplars e = new Exemplars(data, 0);
            System.out.println("\nOriginal whole data:\n" + e.toString());
            Exemplars ex = new Exemplars(e);
            e = new Exemplars(ex, ex.numExemplars());
            for (int i = 0; i < ex.numExemplars(); i++)
                e.add(ex.exemplar(i));
            e.stratify(3);
            System.out.println("\nWhole data after stratification:\n" + e.toString());

            e.sort();
            System.out.println("\nWhole data after sorting by Exemplar #:\n" + e.toString());

            Random ran = new Random(System.currentTimeMillis());
            e.randomize(ran);
            System.out.println("\nWhole data after randomization:\n" + e.toString());

            Exemplars egs = e.resample(ran);
            System.out.println("\nResampled data:\n" + egs.toString());

            Exemplars test = e.testCV(10, 1);
            System.out.println("\nTesting data\n" + test.toString());

            Exemplars train = e.trainCV(10, 1);
            System.out.println("\nTraining data:\n" + train.toString());

        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }
    }
}