/**
 *
 */

package dt;

import java.util.*;
import org.apache.commons.math3.util.FastMath;

public class ID3Algorithm implements Algorithm {
  // final Logger logger = LoggerFactory.getLogger(ID3Algorithm.class);
  private Examples examples;

  public ID3Algorithm(Examples examples) {
    this.examples = examples;
  }

  /**
   * Returns the next attribute to be chosen.
   *
   * chosenAttributes represents the decision path from the root attribute
   * to the node under consideration. usedAttributes is the set of all
   * attributes that have been incorporated into the tree prior to this
   * call to nextAttribute(), even if the attributes were not used in the path
   * to the node under consideration.
   *
   * Results are undefined if examples.count() == 0.
   */
  public Attribute nextAttribute(Map chosenAttributes, Set usedAttributes) {
    double currentGain = 0.0, bestGain = 0.0;
    String bestAttribute = "";

    /*
     * If there are no positive examples for the already chosen attributes,
     * then return a false classifier leaf. If no negative examples,
     * then return a true classifier leaf.
     */
    if ( examples.countPositive(chosenAttributes) == 0 )
      return new Attribute(false);
    else if ( examples.countNegative(chosenAttributes) == 0 )
      return new Attribute(true);

    //logger.debug("Choosing attribute out of {} remaining attributes.",
    //             remainingAttributes(usedAttributes).size());
    //logger.debug("Already chosen attributes/decisions are {}.", chosenAttributes);

    for ( String attribute : remainingAttributes(usedAttributes) ) {
      // for each remaining attribute, determine the information gain of using it
      // to choose among the examples selected by the chosenAttributes
      // if none give any information gain, return a leaf attribute,
      // otherwise return the found attribute as a non-leaf attribute
      currentGain = informationGain(attribute, chosenAttributes);
      //logger.debug("Evaluating attribute {}, information gain is {}",
      //            attribute, currentGain);
      if ( currentGain > bestGain ) {
        bestAttribute = attribute;
        bestGain = currentGain;
      }
    }

    // If no attribute gives information gain, generate leaf attribute.
    // Leaf is true if there are any true classifiers.
    // If there is at least one negative example, then the information gain
    // would be greater than 0.
    if ( bestGain == 0.0 ) {
      boolean classifier = examples.countPositive(chosenAttributes) > 0;
      //logger.debug("Creating new leaf attribute with classifier {}.", classifier);
      return new Attribute(classifier);
    } else {
      //logger.debug("Creating new non-leaf attribute {}.", bestAttribute);
      return new Attribute(bestAttribute);
    }
  }

  private Set remainingAttributes(Set usedAttributes) {
    Set result = examples.extractAttributes();
    result.removeAll(usedAttributes);
    return result;
  }

  private double entropy(Map specifiedAttributes) {
    double totalExamples = examples.count();
    double positiveExamples = examples.countPositive(specifiedAttributes);
    double negativeExamples = examples.countNegative(specifiedAttributes);

    return -nlog2(positiveExamples / totalExamples) - 
            nlog2(negativeExamples / totalExamples);
  }

  private double entropy(String attribute, String decision, Map specifiedAttributes) {
    double totalExamples = examples.count(attribute, decision, specifiedAttributes);
    double positiveExamples = examples.countPositive(attribute, decision, specifiedAttributes);
    double negativeExamples = examples.countNegative(attribute, decision, specifiedAttributes);

    return -nlog2(positiveExamples / totalExamples) - 
            nlog2(negativeExamples / totalExamples);
  }

  private double informationGain(String attribute, Map specifiedAttributes) {
    double sum = entropy(specifiedAttributes);
    double examplesCount = examples.count(specifiedAttributes);

    if ( examplesCount == 0 )
      return sum;

    Map > decisions = examples.extractDecisions();

    for ( String decision : decisions.get(attribute) ) {
      double entropyPart = entropy(attribute, decision, specifiedAttributes);
      double decisionCount = examples.countDecisions(attribute, decision);

      sum += -(decisionCount / examplesCount) * entropyPart;
    }

    return sum;
  }

  private double nlog2(double value) {
    if ( value == 0 )
      return 0;

    return value * FastMath.log(value) / FastMath.log(2);
  }
}

 

Ads help maintain this website.