Package weka.classifiers.meta

Source Code of weka.classifiers.meta.ClassificationViaClustering

/*
*    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.
*/

/*
* ClassificationViaClustering.java
* Copyright (C) 2007 University of Waikato, Hamilton, New Zealand
*/

package weka.classifiers.meta;

import weka.classifiers.Classifier;
import weka.classifiers.rules.ZeroR;
import weka.clusterers.ClusterEvaluation;
import weka.clusterers.Clusterer;
import weka.clusterers.AbstractClusterer;
import weka.clusterers.SimpleKMeans;
import weka.core.Capabilities;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.Option;
import weka.core.OptionHandler;
import weka.core.RevisionUtils;
import weka.core.Utils;
import weka.core.Capabilities.Capability;

import java.util.Enumeration;
import java.util.Vector;

/**
<!-- globalinfo-start -->
* A simple meta-classifier that uses a clusterer for classification. For cluster algorithms that use a fixed number of clusterers, like SimpleKMeans, the user has to make sure that the number of clusters to generate are the same as the number of class labels in the dataset in order to obtain a useful model.<br/>
* <br/>
* Note: at prediction time, a missing value is returned if no cluster is found for the instance.<br/>
* <br/>
* The code is based on the 'clusters to classes' functionality of the weka.clusterers.ClusterEvaluation class by Mark Hall.
* <p/>
<!-- globalinfo-end -->
*
<!-- options-start -->
* Valid options are: <p/>
*
* <pre> -D
*  If set, classifier is run in debug mode and
*  may output additional info to the console</pre>
*
* <pre> -W
*  Full name of clusterer.
*  (default: weka.clusterers.SimpleKMeans)</pre>
*
* <pre>
* Options specific to clusterer weka.clusterers.SimpleKMeans:
* </pre>
*
* <pre> -N &lt;num&gt;
*  number of clusters.
*  (default 2).</pre>
*
* <pre> -V
*  Display std. deviations for centroids.
* </pre>
*
* <pre> -M
*  Replace missing values with mean/mode.
* </pre>
*
* <pre> -S &lt;num&gt;
*  Random number seed.
*  (default 10)</pre>
*
<!-- options-end -->
*
* @author  fracpete (fracpete at waikato dot ac dot nz)
* @version $Revision: 1.5 $
*/
public class ClassificationViaClustering
  extends Classifier {
 
  /** for serialization */
  private static final long serialVersionUID = -5687069451420259135L;

  /** the cluster algorithm used (template) */
  protected Clusterer m_Clusterer;

  /** the actual cluster algorithm being used */
  protected Clusterer m_ActualClusterer;
 
  /** the original training data header */
  protected Instances m_OriginalHeader;
 
  /** the modified training data header */
  protected Instances m_ClusteringHeader;
 
  /** the mapping between clusters and classes */
  protected double[] m_ClustersToClasses;
 
  /** the default model */
  protected Classifier m_ZeroR;
 
  /**
   * default constructor
   */
  public ClassificationViaClustering() {
    super();
   
    m_Clusterer = new SimpleKMeans();
  }
 
  /**
   * Returns a string describing classifier
   *
   * @return     a description suitable for displaying in the
   *             explorer/experimenter gui
   */
  public String globalInfo() {
    return
        "A simple meta-classifier that uses a clusterer for classification. "
      + "For cluster algorithms that use a fixed number of clusterers, like "
      + "SimpleKMeans, the user has to make sure that the number of clusters "
      + "to generate are the same as the number of class labels in the dataset "
      + "in order to obtain a useful model.\n"
      + "\n"
      + "Note: at prediction time, a missing value is returned if no cluster "
      + "is found for the instance.\n"
      + "\n"
      + "The code is based on the 'clusters to classes' functionality of the "
      + "weka.clusterers.ClusterEvaluation class by Mark Hall.";
  }

  /**
   * Gets an enumeration describing the available options.
   *
   * @return     an enumeration of all the available options.
   */
  public Enumeration listOptions(){
    Vector     result;
    Enumeration    enm;

    result = new Vector();

    enm = super.listOptions();
    while (enm.hasMoreElements())
      result.addElement(enm.nextElement());

    result.addElement(new Option(
  "\tFull name of clusterer.\n"
  + "\t(default: " + defaultClustererString() +")",
  "W", 1, "-W"));

    result.addElement(new Option(
  "",
  "", 0, "\nOptions specific to clusterer "
  + m_Clusterer.getClass().getName() + ":"));
    enm = ((OptionHandler) m_Clusterer).listOptions();
    while (enm.hasMoreElements())
      result.addElement(enm.nextElement());

    return result.elements();
  }
 
  /**
   * returns the options of the current setup
   *
   * @return    the current options
   */
  public String[] getOptions(){
    int           i;
    Vector<String>      result;
    String[]      options;

    result = new Vector<String>();

    result.add("-W");
    result.add("" + getClusterer().getClass().getName());
   
    options = super.getOptions();
    for (i = 0; i < options.length; i++)
      result.add(options[i]);

    if (getClusterer() instanceof OptionHandler) {
      result.add("--");
      options = ((OptionHandler) getClusterer()).getOptions();
      for (i = 0; i < options.length; i++)
        result.add(options[i]);
    }

    return result.toArray(new String[result.size()]);   
  }

  /**
   * Parses the options for this object. <p/>
   *
   <!-- options-start -->
   * Valid options are: <p/>
   *
   * <pre> -D
   *  If set, classifier is run in debug mode and
   *  may output additional info to the console</pre>
   *
   * <pre> -W
   *  Full name of clusterer.
   *  (default: weka.clusterers.SimpleKMeans)</pre>
   *
   * <pre>
   * Options specific to clusterer weka.clusterers.SimpleKMeans:
   * </pre>
   *
   * <pre> -N &lt;num&gt;
   *  number of clusters.
   *  (default 2).</pre>
   *
   * <pre> -V
   *  Display std. deviations for centroids.
   * </pre>
   *
   * <pre> -M
   *  Replace missing values with mean/mode.
   * </pre>
   *
   * <pre> -S &lt;num&gt;
   *  Random number seed.
   *  (default 10)</pre>
   *
   <!-- options-end -->
   *
   * @param options  the options to use
   * @throws Exception  if setting of options fails
   */
  public void setOptions(String[] options) throws Exception {
    String  tmpStr;

    super.setOptions(options);

    tmpStr = Utils.getOption('W', options);
    if (tmpStr.length() > 0) {
      // This is just to set the classifier in case the option
      // parsing fails.
      setClusterer(AbstractClusterer.forName(tmpStr, null));
      setClusterer(AbstractClusterer.forName(tmpStr, Utils.partitionOptions(options)));
    }
    else {
      // This is just to set the classifier in case the option
      // parsing fails.
      setClusterer(AbstractClusterer.forName(defaultClustererString(), null));
      setClusterer(AbstractClusterer.forName(defaultClustererString(), Utils.partitionOptions(options)));
    }
  }

  /**
   * String describing default clusterer.
   *
   * @return    the classname
   */
  protected String defaultClustererString() {
    return SimpleKMeans.class.getName();
  }
 
  /**
   * Returns the tip text for this property
   *
   * @return     tip text for this property suitable for
   *       displaying in the explorer/experimenter gui
   */
  public String clustererTipText() {
    return "The clusterer to be used.";
  }

  /**
   * Set the base clusterer.
   *
   * @param value   the clusterer to use.
   */
  public void setClusterer(Clusterer value) {
    m_Clusterer = value;
  }

  /**
   * Get the clusterer used as the base learner.
   *
   * @return     the current clusterer
   */
  public Clusterer getClusterer() {
    return m_Clusterer;
  }

  /**
   * Classifies the given test instance.
   *
   * @param instance   the instance to be classified
   * @return     the predicted most likely class for the instance or
   *       Instance.missingValue() if no prediction is made
   * @throws Exception   if an error occurred during the prediction
   */
  public double classifyInstance(Instance instance) throws Exception {
    double  result;
    double[]  values;
    Instance  newInst;
    int    i;
    int    n;
   
    if (m_ZeroR != null) {
      result = m_ZeroR.classifyInstance(instance);
    }
    else {
      if (m_ActualClusterer != null) {
  // build new instance
  values = new double[m_ClusteringHeader.numAttributes()];
  n = 0;
  for (i = 0; i < instance.numAttributes(); i++) {
    if (i == instance.classIndex())
      continue;
    values[n] = instance.value(i);
    n++;
  }
  newInst = new Instance(instance.weight(), values);
  newInst.setDataset(m_ClusteringHeader);

  // determine cluster/class
  result = m_ClustersToClasses[m_ActualClusterer.clusterInstance(newInst)];
  if (result == -1)
    result = Instance.missingValue();
      }
      else {
  result = Instance.missingValue();
      }
    }
   
    return result;
  }
 
  /**
   * Returns default capabilities of the classifier.
   *
   * @return    the capabilities of this classifier
   */
  public Capabilities getCapabilities() {
    Capabilities  result;
   
    result = m_Clusterer.getCapabilities();
   
    // class
    result.disableAllClasses();
    result.disable(Capability.NO_CLASS);
    result.enable(Capability.NOMINAL_CLASS);
   
    return result;
  }

  /**
   * builds the classifier
   *
   * @param data        the training instances
   * @throws Exception  if something goes wrong
   */
  public void buildClassifier(Instances data) throws Exception {
    Instances    clusterData;
    ClusterEvaluation  eval;
    int      i;
    Instance    instance;
    int[][]     counts;
    int[]     clusterTotals;
    double[]     best;
    double[]     current;
    double[]     clusterAssignments;
   
    // can classifier handle the data?
    getCapabilities().testWithFail(data);

    // remove instances with missing class
    data = new Instances(data);
    data.deleteWithMissingClass();

    // save original header (needed for clusters to classes output)
    m_OriginalHeader = new Instances(data, 0);
   
    // remove class attribute for clusterer
    clusterData = new Instances(data);
    clusterData.setClassIndex(-1);
    clusterData.deleteAttributeAt(m_OriginalHeader.classIndex());
    m_ClusteringHeader = new Instances(clusterData, 0);

    if (m_ClusteringHeader.numAttributes() == 0) {
      System.err.println("Data contains only class attribute, defaulting to ZeroR model.");
      m_ZeroR = new ZeroR();
      m_ZeroR.buildClassifier(data);
    }
    else {
      m_ZeroR = null;
     
      // build clusterer
      m_ActualClusterer = AbstractClusterer.makeCopy(m_Clusterer);
      m_ActualClusterer.buildClusterer(clusterData);

      // evaluate clusterer on training set
      eval = new ClusterEvaluation();
      eval.setClusterer(m_ActualClusterer);
      eval.evaluateClusterer(clusterData);
      clusterAssignments = eval.getClusterAssignments();

      // determine classes-to-clusters mapping
      counts        = new int [eval.getNumClusters()][m_OriginalHeader.numClasses()];
      clusterTotals = new int[eval.getNumClusters()];
      best          = new double[eval.getNumClusters()+1];
      current       = new double[eval.getNumClusters()+1];
      for (i = 0; i < data.numInstances(); i++) {
  instance = data.instance(i);
  counts[(int) clusterAssignments[i]][(int) instance.classValue()]++;
  clusterTotals[(int) clusterAssignments[i]]++;
  i++;
      }
      best[eval.getNumClusters()] = Double.MAX_VALUE;
      ClusterEvaluation.mapClasses(eval.getNumClusters(), 0, counts, clusterTotals, current, best, 0);
      m_ClustersToClasses = new double[best.length];
      System.arraycopy(best, 0, m_ClustersToClasses, 0, best.length);
    }
  }

  /**
   * Returns a string representation of the classifier.
   *
   * @return    a string representation of the classifier.
   */
  public String toString() {
    StringBuffer  result;
    int      i;
    int      n;
    boolean    found;
   
    result = new StringBuffer();
   
    // title
    result.append(this.getClass().getName().replaceAll(".*\\.", "") + "\n");
    result.append(this.getClass().getName().replaceAll(".*\\.", "").replaceAll(".", "=") + "\n");
   
    // model
    if (m_ActualClusterer != null) {
      // output clusterer
      result.append(m_ActualClusterer + "\n");
     
      // clusters to classes
      result.append("Clusters to classes mapping:\n");
      for (i = 0; i < m_ClustersToClasses.length - 1; i++) {
  result.append("  " + (i+1) + ". Cluster: ");
  if (m_ClustersToClasses[i] < 0)
    result.append("no class");
  else
    result.append(
        m_OriginalHeader.classAttribute().value((int) m_ClustersToClasses[i])
        + " (" + ((int) m_ClustersToClasses[i] + 1) + ")");
  result.append("\n");
      }
      result.append("\n");
     
      // classes to clusters
      result.append("Classes to clusters mapping:\n");
      for (i = 0; i < m_OriginalHeader.numClasses(); i++) {
  result.append(
      "  " + (i+1) + ". Class ("
      + m_OriginalHeader.classAttribute().value(i) + "): ");
 
  found = false;
  for (n = 0; n < m_ClustersToClasses.length - 1; n++) {
    if (((int) m_ClustersToClasses[n]) == i) {
      found = true;
      result.append((n+1) + ". Cluster");
      break;
    }
  }
 
  if (!found)
    result.append("no cluster");

  result.append("\n");
      }
     
      result.append("\n");
    }
    else {
      result.append("no model built yet\n");
    }
   
    return result.toString();
  }
 
  /**
   * Returns the revision string.
   *
   * @return    the revision
   */
  public String getRevision() {
    return RevisionUtils.extract("$Revision: 1.5 $");
  }
 
  /**
   * Runs the classifier with the given options
   *
   * @param args  the commandline options
   */
  public static void main(String[] args) {
    runClassifier(new ClassificationViaClustering(), args);
  }
}
TOP

Related Classes of weka.classifiers.meta.ClassificationViaClustering

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.