/** * An implementation of the recursive decision tree * pessimistic pruning algorithm. Given a parent * node, the method will prune all the branches * below the node. * * @param node The root node of the tree to prune. * * @param error A <code>double</code> array of size 1. The * array is used to store the current error value. * * @return <code>true</code> if an entire subtree was successfully * pruned, or <code>false</code> otherwise. */ public bool prunePessimisticDT(DecisionTreeNode node, double[] error) { // Post-order walk through the tree, marking // our path as we go along. if (node.isLeaf()) { if (node.getTrainingEgsAtNode() == 0) { error[0] = 0; return true; } else { // We do the error calculation in two steps - // Here we multiply the error value by the number // of examples that reach the node. When the method // is called recursively, this value will be divided // by the number of examples that reach the parent // node (thus weighting the error from each child). int errors1 = (int)node.getTrainingEgsAtNode() - node.getTrainingEgsCorrectClassUsingBestTrainingIndex(); double p1 = (double)(errors1 + 1.0) / (node.getTrainingEgsAtNode() + 2.0); error[0] = node.getTrainingEgsAtNode() * errorBar(p1, node.getTrainingEgsAtNode()) + errors1; return true; } } // We're at an internal node, so compute the error // of the children and use the result to determine // if we prune or not. double errorSum = 0; for (int i = 0; i < node.getArcLabelCount(); i++) { // Mark our current path. Tree.flagNode(node, i); if (!prunePessimisticDT(node.getChild(i), error)) { Tree.flagNode(node, -2); return false; } errorSum += error[0]; } // Mark the node as our current target. Tree.flagNode(node, -1); // Get the worst-case performance of this node. double errorWorst; if (node.getTrainingEgsAtNode() == 0) { error[0] = 0; return true; } int errors = (int)node.getTrainingEgsAtNode() - node.getTrainingEgsCorrectClassUsingBestTrainingIndex(); double p = (double)(errors + 1.0) / (node.getTrainingEgsAtNode() + 2.0); errorWorst = (double)node.getTrainingEgsAtNode() * errorBar(p, node.getTrainingEgsAtNode()) + errors; DecisionTreeNode newNode = node; if (errorWorst < errorSum) { // We need to "prune" this node to a leaf. DecisionTreeNode parent = node.getParent(); int arcNum = -1; if (parent != null) { arcNum = parent.getChildPosition(node); } Tree.pruneSubtree(node); // Figure out the label for the new leaf. String label = null; try { label = DatasetUse.getTargetAttribute().getAttributeValueByNum(node.getTrainingBestTarget()); } catch (Exception e) { // Should never happen. //e.printStackTrace(); } node.getMask().mask(0, node.getTrainingBestTarget()); newNode = Tree.addLeafNode(parent, arcNum, label, node.getMask(), node.getTrainingEgsAtNode(), node.getTrainingBestTarget(), node.getTrainingEgsCorrectClassUsingBestTrainingIndex(), node.getTestingEgsCorrectClassUsingBestTrainingIndex(), node.getTestingEgsAtNode(), node.getTestingBestTarget(), node.getTestingEgsCorrectClassUsingBestTestingIndex(), node.getTrainingEgsCorrectClassUsingBestTestingIndex()); } // Update the count. if (newNode.isLeaf()) { error[0] = errorWorst; } else { error[0] = errorSum; } // All finished, unmark the node if it still exists. Tree.flagNode(node, -2); return true; }