Decision trees
After this page you can take a table of labelled examples, build the decision tree by hand the way the algorithm would, compute its training error, and say exactly why the error-rate splitting criterion has to be thrown away.
How to build a decision tree from labelled examples by hand, and why the error-rate splitting criterion has to be thrown away.
- Decision tree — internal nodes split on an attribute $x_j$, leaves carry labels; inference walks root to leaf, reading one attribute per node. Benefit: human interpretable. Cost: accuracy on hard, non-axis-aligned problems.
- Decision stump — a tree with exactly one split. Two free choices: which attribute, and which label in each leaf. With $d$ binary attributes there are only $4d$ stumps, so brute force works.
- Majority vote — a leaf predicts $\hat y = \argmax_{k \in \Y} \sum_i \1\!\left(y^{(i)} = k\right)$. A constant predictor $c$ makes $n - n_c$ mistakes, so majority vote provably minimises leaf mistakes and the leaf labels are forced once the structure is fixed. State a tie-break rule (this guide: predict $1$).
- Construction —
DT(S, A): if $A = \emptyset$ or all of $S$ shares a label, return a leaf with the majority vote; else take $b = $BestAttribute$(S,A)$ and recurse on $\{x_b = 1\}$ and $\{x_b = 0\}$ with $A \setminus \{b\}$. $A$ shrinks every level, so depth $\le d$ and the recursion terminates. - BestAttribute v1 — minimise the stump's training error: split, majority-vote each child, add the mistakes. On the poll dataset $x_2$ scores $1/8$ against $3/8$ for $x_1$ and $x_3$; one further split on $x_1$ reaches zero training error.
- Classification error rate — $\text{err} = \frac{1}{N}\sum_i \1\!\left(y^{(i)} \ne \hat y^{(i)}\right)$: the empirical risk $\hat R(h)$ under zero-one loss. A fine way to evaluate a finished tree, a bad way to choose a split.
- Greedy is suboptimal — a one-step-lookahead approximation to ERM, used because the smallest consistent tree is NP-hard while greedy costs $O(Nd)$ per node. XOR defeats every one-step criterion (error rate and mutual information both score 0), yet the recursion still reaches a perfect depth-2 tree — unless a zero-gain early stop quits at the root.
- Boundaries are axis-aligned staircases — each node tests one coordinate against a constant, so each leaf owns an intersection of axis-aligned half-spaces (a box). A diagonal rule can only be approximated, at roughly $O(1/\varepsilon)$ splits, each thinning the data.
- Stopping criteria — $A = \emptyset$; pure node; gain threshold (dangerous — blind to XOR); depth limit; minimum leaf size. They exist because a tree grown to purity memorises the training set.
- Why error rate is broken as a criterion — it sees only which class wins, not by how much. On the 8-row $(y,a,b)$ dataset no split, splitting on $a$ and splitting on $b$ all score $2/8$, while $I(Y;a) = 0$ and $I(Y;b) = 0.311$ bits correctly prefer $b$.
Everything below is the explanation and the practice. If you are short on time, read this box, then go to the problems.
The apartment problem
Four hundred apartments are listed and you cannot tour four hundred. You need a cheap rule that says go see this one or skip it, explainable to your roommate. So you write one down — not as a formula, but as a sequence of questions:
Read it out loud and it is a policy: if the rent is low and the deposit is low, go; if the rent is low, the deposit high, but you are within 2 miles, go; if the rent is high, you only bother within a mile. That is a decision tree.
Anatomy, and what interpretability actually buys you
A decision tree is a hypothesis function $\hat y = h(x)$ built from two kinds of node:
- Internal nodes split on some attribute (also called a feature) $x_j$ of the input. The value of $x_j$ decides which branch you follow. Two branches is the usual case, but a node can split into more.
- Leaves carry output labels — here $\checkmark$ or $\times$. Reaching a leaf ends the computation and the leaf's label is the prediction $\hat y$.
Major benefit: human interpretable. You can print a decision tree on one page, hand it to someone with no mathematical training, and they can both execute it and argue with it.
Concretely, you can audit it ("rejected because the deposit was high and it's 2.3 miles out"), debug it, deploy it legally where decisions must be justified, and edit it by hand. The cost is accuracy on hard problems: trees carve feature space into axis-aligned boxes (see below), so a diagonal rule costs a staircase of splits.
Rule-based vs. learned: the actual ML question
Where did Figure 1 come from? You made it up: you already knew rent matters more than bus access, and you guessed the thresholds. That is a rule-based approach, legitimate when you have the rules. The notes' objection is two-part:
- You often don't have the rules. Nobody can write down, from first principles, which combination of 40 blood-panel numbers indicates sepsis.
- The setting is too complex. Even where rules exist, with $d = 30$ binary attributes there are $2^{30}$ possible inputs and astronomically many trees. Hand-authoring is hopeless.
The ML question for this chapter: given a set of labelled examples, how do you construct a decision tree?
Everything that follows — stumps, majority vote, the recursion, BestAttribute, entropy in Chapter 7 — is an answer to that one question.
What the examples comprise
First say precisely what one "example" is. In the apartment problem:
- Each example is one prospective apartment.
- $x$ is the vector of answers to the five questions: "Rent low?", "Deposit low?", "Within 1 mi?", "Within 2 mi?", "Near bus stop?"
- $y$ is the answer to "should I go check this apartment out or not?"
Written formally, exactly as the notes have it:
$$N = \#\text{ prospective apartments},\qquad \X = \{\text{Yes},\text{No}\}^5 = \{0,1\}^5, \qquad \Y = \{Y, N\} = \{0,1\}$$$N$ counts examples (rows), not attributes. $\X$ is the input space: all possible $x$, not the ones you have — $\{0,1\}^5$ has $2^5 = 32$ elements while your dataset might hold 12 apartments, some repeated. And the superscript in $x^{(i)}$ is an index, not a power: $x^{(3)}_2$ is the third apartment's answer to question 2, subscript for attributes, parenthesised superscript for examples.
Now generalise. For the rest of the chapter:
$$\X = \{0,1\}^d \ \ (d \text{ binary attributes}), \qquad \Y = \{0,1\}$$Inference: running a new point down the tree
Prediction is trivial, and that is the point. Given a new apartment $x^{(\mathrm{new})} \in \X$: start at the root, answer the question, follow the matching branch, repeat, and output the label of the leaf you land in.
Take $x^{(\mathrm{new})} = (\text{rent low}=\text{Yes},\ \text{deposit low}=\text{No},\ \text{1mi}=\text{No},\ \text{2mi}=\text{No},\ \text{bus}=\text{Yes})$.
Root "Rent low?" → Yes → "Deposit low?" → No → "Within 2mi of campus?" → No → "Near bus stop?" → Yes → leaf $\checkmark$.
Go see it. Cost: four comparisons. Note that attribute "Within 1mi" was never even read — a tree only looks at the features it needs, which is another quiet advantage over models that must evaluate every coordinate.
The key building block: the decision stump
Trees are recursive, so start with the smallest tree that still does something — the notes' key building block:
A decision stump is a decision tree with exactly one split: a single internal node "Is a certain attribute $= 1$?" branching straight into two leaves, each carrying a chosen output label.
Constructing a stump, in three steps
The pre-reading gives the recipe:
- Select the attribute and split criterion. Which feature does the node test, and what test? For binary features the test is just "$x_b = 1$?"; for real-valued features you also pick a threshold.
- Split the training dataset into subsets by passing each training point through the decision node. Each leaf then holds a subset of the training data.
- Each leaf predicts by majority vote among the output values $y^{(i)}$ of the points that landed there. A tie-breaking scheme must be specified.
Majority vote. Given a leaf's subset of training points, each with a discrete label $y^{(i)} \in \Y$:
$$\hat y \;=\; \argmax_{k \in \Y} \; \sum_i \1\!\left(y^{(i)} = k\right)$$Symbol by symbol: $\1(\cdot)$ is the indicator function, $1$ when the statement inside is true and $0$ otherwise, so $\sum_i \1(y^{(i)} = k)$ counts the points in this leaf with label $k$ and $\argmax_k$ returns the label with the biggest count — the class that appears most often in the leaf's training subset.
$\argmax$ returns the argument (which label), not the value (how many). If a leaf holds labels $\{0,0,1\}$, then $\sum_i \1(y^{(i)}=0) = 2$ and $\sum_i \1(y^{(i)}=1)=1$, so the $\max$ is $2$ but the $\argmax$ — and therefore $\hat y$ — is $\mathbf{0}$.
And notice why a tie-break rule is genuinely needed: with labels $\{0,0,1,1\}$ the $\argmax$ is not unique, so the formula alone does not define $\hat y$. Convention used throughout this guide: on a tie, predict $1$. Any fixed rule works; what is unacceptable is leaving it unstated, because then two people compute different training errors for the same tree.
Why majority vote is the right leaf label
Claim: among all constant predictors on a leaf, majority vote minimises the number of training mistakes on that leaf.
Let the leaf hold $n_k$ points of label $k$, with $n = \sum_k n_k$ total. A constant predictor $\hat y = c$ is wrong on every point whose label is not $c$, so it makes
$$\text{mistakes}(c) \;=\; n - n_c$$mistakes. Since $n$ is fixed, minimising $n - n_c$ over $c$ is the same as maximising $n_c$ over $c$, and $\argmax_c n_c$ is exactly the majority vote. $\blacksquare$
This is why leaves are never a design decision: given the tree's structure, the optimal leaf labels are forced. All the intelligence in tree learning lives in choosing the splits.
The recursive construction algorithm
From a stump to a tree is one idea: do it again on each leaf. Each leaf holds a subset; run the stump procedure on that subset; repeat until a stopping criterion fires. The algorithm exactly as the notes state it:
Algorithm 1 Decision Tree Construction
1: function DT(dataset S, set of attributes A ⊆ {1, 2, ..., d})
2: if A = ∅ or all examples in S have the same label then
3: return a leaf with value = majority vote of labels in S
4: end if
5: let b = BestAttribute(S, A) ∈ A
6: let LeftNode = DT( {(x,y) ∈ S | x_b = 1}, A \ {b} )
7: let RightNode = DT( {(x,y) ∈ S | x_b = 0}, A \ {b} )
8: return the tree "Is the b-th attribute equal to 1?"
/ Yes No \
LeftNode RightNode
9: end function
It is called initially with the whole training set and the whole attribute list:
$$S = \{(x^{(i)}, y^{(i)})\}_{i=1}^{N}, \qquad A = \{1, 2, \dots, d\}$$The recursion in words, line by line
- Line 2 — the two base cases. $A = \emptyset$: every attribute on this path is used up, so no split is possible. Same label throughout $S$: the node is pure and splitting cannot beat the zero error it already has.
- Line 3 — the leaf. Majority vote, as proved above; needed even in the first base case, where $S$ may be mixed.
- Line 5 — the only interesting line.
BestAttributeis a black box for now, and swapping implementations is what distinguishes the algorithms in the literature (see below and Chapter 7). - Lines 6–7 — the partition. $x_b$ is $1$ or $0$, so every point goes to exactly one child and $|S_{\text{left}}| + |S_{\text{right}}| = |S|$. Removing $b$ from $A$ shrinks both arguments, so the recursion terminates at depth $\le d$.
- Line 8 — assembling. The recursive calls return sub-trees, not labels.
A wrinkle worth naming: "Is $x_b = 1$?" vs "Is $x_b = 0$?"
The pseudocode always asks "Is $x_b = 1$?", while the notes' worked example asks "Is $x_2 = 0$?". Not different algorithms: "is it 0?" is the same split with the branches swapped. The phrasing is cosmetic — keep the partition straight and the labels take care of themselves.
BestAttribute, version 1: minimise training error
We need to fill in BestAttribute. The notes' first proposal is the most obvious one:
Pretend you are allowed only one split — i.e. you must produce a decision stump. Then ask: which choice of attribute to split on (and which labels for the two resulting leaves) gives the lowest training error? Pick that attribute.
A finite search you can do by hand: for each $b \in A$, split $S$ into the $x_b = 1$ and $x_b = 0$ groups, majority-vote each, add the mistakes; smallest total wins. Leaf labels need no separate search — majority vote is already optimal given the split — so "choose attribute and labels" collapses to "choose attribute".
The poll example, solved completely
The notes pose this question and answer only "In poll." Here is the full answer, every split, every count.
Training data. $N = 8$, $d = 3$, all attributes and labels binary. I have added a row index $i$; the notes give only the four columns.
| $i$ | $y$ | $x_1$ | $x_2$ | $x_3$ |
|---|---|---|---|---|
| 1 | 0 | 1 | 0 | 0 |
| 2 | 0 | 1 | 0 | 1 |
| 3 | 0 | 1 | 0 | 0 |
| 4 | 1 | 0 | 0 | 1 |
| 5 | 1 | 1 | 1 | 0 |
| 6 | 1 | 1 | 1 | 1 |
| 7 | 1 | 1 | 1 | 0 |
| 8 | 1 | 1 | 1 | 1 |
Question. Which attribute does BestAttribute pick at the root under the training-error criterion? And what is the final tree?
Step 0 — the root before any split
Rows 1, 2, 3 have $y=0$ and rows 4–8 have $y=1$: 3 zeros, 5 ones. Majority-voting the whole dataset without splitting predicts $\hat y = 1$ and is wrong on the 3 zeros — a baseline training error of $3/8$, which any split has to beat.
Step 1 — try every attribute as a stump
| Split on | Branch | Rows that land there | Labels | Counts (0 / 1) | Majority vote $\hat y$ | Mistakes |
|---|---|---|---|---|---|---|
| $x_1$ | $x_1 = 1$ | 1, 2, 3, 5, 6, 7, 8 | 0,0,0,1,1,1,1 | 3 / 4 | 1 | 3 |
| $x_1 = 0$ | 4 | 1 | 0 / 1 | 1 | 0 | |
| $x_2$ | $x_2 = 1$ | 5, 6, 7, 8 | 1,1,1,1 | 0 / 4 | 1 | 0 |
| $x_2 = 0$ | 1, 2, 3, 4 | 0,0,0,1 | 3 / 1 | 0 | 1 | |
| $x_3$ | $x_3 = 1$ | 2, 4, 6, 8 | 0,1,1,1 | 1 / 3 | 1 | 1 |
| $x_3 = 0$ | 1, 3, 5, 7 | 0,0,1,1 | 2 / 2 | 1 (tie → 1) | 2 |
Adding the two branches of each split gives the totals:
| Attribute | Mistakes on $x_b=1$ side | Mistakes on $x_b=0$ side | Total training error | Beats the $3/8$ baseline? |
|---|---|---|---|---|
| $x_1$ | 3 | 0 | $3/8 = 0.375$ | no — ties it |
| $x_2$ | 0 | 1 | $\mathbf{1/8 = 0.125}$ | yes |
| $x_3$ | 1 | 2 | $3/8 = 0.375$ | no — ties it |
$x_2$ is the best first split, $1/8$ against $3/8$ for the other two, because the $x_2 = 1$ side is perfectly pure — all four rows there have $y=1$ — so that branch is finished in one step.
Step 2 — recurse on the impure branch
The calls are DT({rows 5,6,7,8}, A = {1,3}) and DT({rows 1,2,3,4}, A = {1,3}).
The $x_2 = 1$ branch holds rows 5–8, all with $y = 1$: line 2 fires, return a leaf with value $1$. The $x_2 = 0$ branch holds rows 1–4 with labels $0,0,0,1$ — not pure, and $A = \{1,3\}$ is not empty, so recurse on:
| $i$ | $y$ | $x_1$ | $x_3$ |
|---|---|---|---|
| 1 | 0 | 1 | 0 |
| 2 | 0 | 1 | 1 |
| 3 | 0 | 1 | 0 |
| 4 | 1 | 0 | 1 |
| Split on | Branch | Rows | Labels | Majority $\hat y$ | Mistakes | Total |
|---|---|---|---|---|---|---|
| $x_1$ | $x_1=1$ | 1, 2, 3 | 0,0,0 | 0 | 0 | 0 / 4 |
| $x_1=0$ | 4 | 1 | 1 | 0 | ||
| $x_3$ | $x_3=1$ | 2, 4 | 0,1 | 1 (tie) | 1 | 1 / 4 |
| $x_3=0$ | 1, 3 | 0,0 | 0 | 0 |
$x_1$ resolves this node completely. Both of its children are pure, so both are immediately leaves, and the recursion stops.
Step 3 — the finished tree
Assembling the pieces gives exactly the tree in the notes (written in the notes' "Is $x_2 = 0$?" phrasing):
Step 4 — verify zero training error by running all 8 rows through it
| $i$ | $x_1$ | $x_2$ | $x_3$ | Path through the tree | $\hat y$ | $y$ | $\1(y \ne \hat y)$ |
|---|---|---|---|---|---|---|---|
| 1 | 1 | 0 | 0 | $x_2{=}0$ Yes → $x_1{=}0$ No | 0 | 0 | 0 |
| 2 | 1 | 0 | 1 | $x_2{=}0$ Yes → $x_1{=}0$ No | 0 | 0 | 0 |
| 3 | 1 | 0 | 0 | $x_2{=}0$ Yes → $x_1{=}0$ No | 0 | 0 | 0 |
| 4 | 0 | 0 | 1 | $x_2{=}0$ Yes → $x_1{=}0$ Yes | 1 | 1 | 0 |
| 5 | 1 | 1 | 0 | $x_2{=}0$ No | 1 | 1 | 0 |
| 6 | 1 | 1 | 1 | $x_2{=}0$ No | 1 | 1 | 0 |
| 7 | 1 | 1 | 0 | $x_2{=}0$ No | 1 | 1 | 0 |
| 8 | 1 | 1 | 1 | $x_2{=}0$ No | 1 | 1 | 0 |
The last column sums to $0$, so the error rate is $\tfrac{1}{8}\cdot 0 = 0$: a perfect fit to this training set. (Whether that is good is another question — see stopping criteria.)
Suppose a stubborn colleague insists the root must test $x_3$. Build the rest of the tree greedily from there (splitting the children on the best remaining attribute), and report the final training error. Is a perfect fit still reachable?
Step 1 — Perform the root split on $x_3$
$x_3 = 1$: rows 2, 4, 6, 8 with labels $0,1,1,1$ (attributes $x_1,x_2$ left).
$x_3 = 0$: rows 1, 3, 5, 7 with labels $0,0,1,1$.
Neither child is pure, so both recurse with $A = \{1,2\}$.
Step 2 — Recurse on the $x_3 = 1$ child (rows 2, 4, 6, 8)
Their $(y, x_1, x_2)$: row 2 = $(0,1,0)$, row 4 = $(1,0,0)$, row 6 = $(1,1,1)$, row 8 = $(1,1,1)$.
Split on $x_1$: $x_1{=}1$ → rows 2,6,8 with labels $0,1,1$ → vote 1, 1 mistake. $x_1{=}0$ → row 4, label 1 → 0 mistakes. Total 1.
Split on $x_2$: $x_2{=}1$ → rows 6,8, labels $1,1$ → 0 mistakes. $x_2{=}0$ → rows 2,4, labels $0,1$ → tie → vote 1, 1 mistake. Total 1.
A tie. Take $x_2$ (lowest index among the winners is a fine deterministic rule; the notes do not specify one, so state yours). Then the $x_2{=}1$ leaf is pure, and the $x_2{=}0$ node still holds rows 2 and 4 with labels $0,1$ and $A = \{1\}$ remaining. Split on $x_1$: row 2 has $x_1{=}1$ (label 0), row 4 has $x_1{=}0$ (label 1). Both children pure! So this whole branch reaches 0 mistakes at depth 3.
Step 3 — Recurse on the $x_3 = 0$ child (rows 1, 3, 5, 7)
Their $(y, x_1, x_2)$: row 1 = $(0,1,0)$, row 3 = $(0,1,0)$, row 5 = $(1,1,1)$, row 7 = $(1,1,1)$.
Split on $x_1$: every row has $x_1 = 1$. The $x_1{=}0$ child is empty and the $x_1{=}1$ child is the whole set — a useless split, 2 mistakes.
Split on $x_2$: $x_2{=}1$ → rows 5,7 (labels $1,1$, pure); $x_2{=}0$ → rows 1,3 (labels $0,0$, pure). 0 mistakes.
So $x_2$ wins here and finishes the branch at depth 2.
Answer & the takeaway
Final training error: 0. The forced-$x_3$ tree still fits perfectly — but it needs 4 internal nodes and depth 3, where the greedy tree needed 2 internal nodes and depth 2.
Takeaway. A bad first split usually does not destroy your ability to fit the training data; with enough attributes you can almost always drive training error to zero eventually. What it destroys is compactness. And compactness is not cosmetic: a bigger tree means fewer examples per leaf, which means the leaf labels are estimated from less data, which means worse generalisation. BestAttribute is fighting for a small tree, not merely a correct one.
Build the tree yourself
Reading a split table is not the same as feeling it. The 8 rows below are live: click any leaf to select it, split it on an attribute, and watch the counts flow into the children and the training error drop.
| $i$ | $y$ | $x_1$ | $x_2$ | $x_3$ | lands in leaf |
|---|
Greediness: why this algorithm is not optimal, and why we do it anyway
The notes are careful to flag this, and you should be too:
The construction algorithm is a greedy approach towards ERM (empirical risk minimisation, see Chapter 5). Greediness is suboptimal but helps computational efficiency.
Why it is suboptimal. True ERM would search over all trees of a given size; greedy commits to the root split on a one-step lookahead, judging each attribute by what it does alone and never reconsidering — and a split that looks useless alone can be indispensable in combination. Why we do it anyway: finding the smallest consistent tree is NP-hard, while greedy costs $O(N \cdot d)$ per node.
The canonical failure: XOR
Let $d = 2$ and $y = x_1 \oplus x_2$ (exclusive-or). The complete dataset is four rows:
| $i$ | $x_1$ | $x_2$ | $y$ |
|---|---|---|---|
| 1 | 0 | 0 | 0 |
| 2 | 0 | 1 | 1 |
| 3 | 1 | 0 | 1 |
| 4 | 1 | 1 | 0 |
Baseline: 2 zeros, 2 ones → majority vote is a tie → predict 1 → 2 mistakes out of 4.
Split on $x_1$: $x_1{=}1$ gives rows 3, 4 with labels $1, 0$ — a 1–1 tie, 1 mistake. $x_1{=}0$ gives rows 1, 2 with labels $0,1$ — another tie, 1 mistake. Total $2/4$. The split bought nothing.
Split on $x_2$: by symmetry, also $2/4$. Also nothing.
So the one-step criterion reports that both attributes are worthless, even though the pair of them determines $y$ exactly. (The same is true of mutual information here: $I(Y;x_1) = I(Y;x_2) = 0$. XOR defeats every one-step-lookahead criterion, not just error rate — this is worth knowing, because students often assume entropy rescues XOR. It does not.)
How the algorithm still recovers
The part people forget — run the algorithm literally on XOR:
- At the root, all attributes tie.
BestAttributebreaks the tie somehow — say it returns $x_1$. - Left child ($x_1 = 1$) holds rows 3, 4 with labels $1, 0$. Not pure, and $A = \{2\}$ is not empty, so recurse. The only choice is $x_2$: row 3 has $x_2{=}0$ (label 1), row 4 has $x_2{=}1$ (label 0). Both children pure.
- Right child ($x_1=0$) holds rows 1, 2. Split on $x_2$: both children pure.
Final tree: depth 2, zero training error, perfect. The greedy criterion was blind, but the recursion kept going anyway because the stopping rule was "pure or out of attributes", not "gain is zero".
This is exactly why the stopping criterion "stop if the split doesn't reduce training error by much" — one of the options the notes list — is dangerous. On XOR it stops at the root with error $2/4$ and never discovers the perfect depth-2 tree. The cheap fix used in practice is to grow the tree out fully and then prune it back, rather than stopping early, because pruning judges a subtree by what it achieved, not by what its first split promised. beyond syllabus
BestAttribute uses, recurses, and at depth 2 the remaining attribute separates each child perfectly. The lesson: greedy scoring can be blind at one level and still be rescued by the recursion below it — provided you did not add an early-stopping rule that quits on zero gain.Evaluating decision trees
The pre-reading asks: here are two decision trees for the Iris task — which is better?
You cannot tell. Comparing two models by staring at them is not a thing. You need (a) data and (b) a performance measure.
The measure for classification is the classification error rate:
$$\text{err} \;=\; \frac{1}{N} \sum_i \1\!\left(y^{(i)} \ne \hat y^{(i)}\right)$$Naming the symbols: $N$ is how many examples you are evaluating on, $y^{(i)}$ the true label, $\hat y^{(i)} = h(x^{(i)})$ the tree's prediction, and $\1(\cdot)$ the indicator, contributing $1$ for a mismatch. The sum counts mistakes; dividing by $N$ makes it a rate, comparable across dataset sizes.
This is the empirical risk $\hat R(h)$ from Chapter 5 with the zero-one loss $\ell(y,\hat y) = \1(y \ne \hat y)$ plugged in — the general machinery specialised to classification.
Error rate treats all mistakes as equally bad, which is often wrong. The pre-reading's medical table charges $10$ for predicting "healthy" for an ill patient and $2$ for the reverse:
| $\ell(y, \hat y)$ | $\hat y = 0$ | $\hat y = 1$ |
|---|---|---|
| $y = 0$ | 0 | 2 |
| $y = 1$ | 10 | 0 |
Everything here works with a weighted loss: replace "count the mistakes" with "add up the losses" in the majority vote and in BestAttribute, and the leaf predicts the cost-minimising class rather than the most common one.
The Iris comparison, worked in full
reconstruction The pre-reading's data table and its two trees are figures that did not survive text extraction from the PDF, so I have reconstructed a plausible 7-row Iris table and two candidate trees that reproduce the stated result — Model A errs on $1/7$, Model B on $2/7$. The rows below are genuine measurements from Fisher's Iris dataset; the trees are mine.
| $i$ | sepal len | sepal wid | petal len | petal wid | species $y$ |
|---|---|---|---|---|---|
| 1 | 5.1 | 3.5 | 1.4 | 0.2 | setosa |
| 2 | 4.9 | 3.0 | 1.5 | 0.2 | setosa |
| 3 | 6.4 | 3.2 | 4.5 | 1.5 | versicolor |
| 4 | 5.7 | 2.8 | 4.1 | 1.3 | versicolor |
| 5 | 5.9 | 3.2 | 4.8 | 1.8 | versicolor |
| 6 | 6.3 | 3.3 | 6.0 | 2.5 | virginica |
| 7 | 6.5 | 3.2 | 5.1 | 2.0 | virginica |
| $i$ | true $y$ | A: path | A: $\hat y$ | A wrong? | B: path | B: $\hat y$ | B wrong? |
|---|---|---|---|---|---|---|---|
| 1 | setosa | PL 1.4 < 2.5 → Yes | setosa | · | SL 5.1 < 5.5 → Yes | setosa | · |
| 2 | setosa | PL 1.5 < 2.5 → Yes | setosa | · | SL 4.9 < 5.5 → Yes | setosa | · |
| 3 | versicolor | PL 4.5; PW 1.5 < 1.75 | versicolor | · | SL 6.4; SW 3.2 ≱ 3.1 | virginica | ✗ |
| 4 | versicolor | PL 4.1; PW 1.3 < 1.75 | versicolor | · | SL 5.7; SW 2.8 < 3.1 | versicolor | · |
| 5 | versicolor | PL 4.8; PW 1.8 ≱ 1.75 | virginica | ✗ | SL 5.9; SW 3.2 ≱ 3.1 | virginica | ✗ |
| 6 | virginica | PL 6.0; PW 2.5 ≱ 1.75 | virginica | · | SL 6.3; SW 3.3 ≱ 3.1 | virginica | · |
| 7 | virginica | PL 5.1; PW 2.0 ≱ 1.75 | virginica | · | SL 6.5; SW 3.2 ≱ 3.1 | virginica | · |
Counting the crosses and dividing by $N = 7$:
$$\text{err}(A) = \frac{1}{7}\sum_i \1(y^{(i)} \ne \hat y^{(i)}) = \frac{1}{7} \approx 0.143, \qquad \text{err}(B) = \frac{2}{7} \approx 0.286$$Model A performs better on this dataset — the pre-reading's conclusion. Note the qualifier: seven flowers is far too few to conclude that petal measurements beat sepal measurements in general, since one flower is 14 percentage points of error rate.
Two more reconstructed examples
reconstruction The pre-reading illustrates "using decision trees" with two figures that did not survive PDF extraction — a cesarean-section tree and the Iris species tree — redrawn here. The structures are mine, not the professor's, though the attributes are standard for each problem.
Extension: beyond binary attributes, and decision boundaries
The notes list several extensions. Start with the one that changes the picture most.
Attributes with more than $\{0,1\}$ values
Two cases, handled differently.
- Categorical with $m$ values (colour $\in$ {red, green, blue}): either split into $m$ branches at once, or binarise — test "colour = red?" and let the No branch test the rest. Multi-way splits consume the attribute in one node; binary tests let you return to it.
- Real-valued (e.g. petal length $\in \R$). You cannot enumerate the values, so the node tests a threshold: "Is $x_j \ge t$?" Now
BestAttributemust choose both $j$ and $t$.
Here is the notes' example with $\X = \R^2$, $\Y = \{0,1\}$, splitting first at $x_2 = 4.5$ and then at $x_1 = 3.5$:
The decision boundary is the set of lines (in general, surfaces) that separate the region where the model outputs $0$ from the region where it outputs $1$.
In Figure 9 it is the horizontal segment at $x_2 = 4.5$ over $x_1 \le 3.5$, plus the vertical segment at $x_1 = 3.5$ below $x_2 = 4.5$. Notice it is not the whole of either line: where label-1 sits on both sides of a line, that stretch of line is not a boundary at all.
Why tree boundaries are always axis-aligned staircases
Every internal node tests a single coordinate against a constant: $x_j \ge t$. The set $\{x : x_j \ge t\}$ is a half-space whose bounding hyperplane is perpendicular to axis $j$ — i.e. axis-aligned.
A leaf is reached exactly when all the tests on its root-to-leaf path hold, so the region a leaf owns is an intersection of axis-aligned half-spaces, which is an axis-aligned box (possibly unbounded).
The model's $0$-region is a union of such boxes, and so is its $1$-region. A boundary between two unions of axis-aligned boxes can only consist of axis-aligned pieces. $\blacksquare$
Consequence for a diagonal boundary. If the truth is $y = \1(x_1 + x_2 \ge 5)$ — a clean 45° line — a tree can never draw it. It can only approximate it with a staircase of alternating horizontal and vertical splits, each shaving off one rectangle: roughly $O(1/\varepsilon)$ splits to get within $\varepsilon$, and every extra split thins the data, so leaves get sparse and the fit gets noisy. A tree cuts only with a guillotine held parallel to the table edges.
The fix is not a better tree algorithm but a better feature: add the column $z = x_1 + x_2$ and one split on $z$ does the job. That is precisely the subject of feature engineering.
How threshold candidates are chosen beyond syllabus
You do not search all of $\R$: sort the $N$ values of $x_j$ and take the midpoints between consecutive distinct values. The partition changes only when $t$ crosses a data value, so there are at most $N-1$ candidates. Petal lengths $1.4, 1.5, 4.1, 4.5, 4.8, 5.1, 6.0$ give $1.45, 2.8, 4.3, 4.65, 4.95, 5.55$ — six, not infinitely many.
Extensions: attribute reuse and stopping criteria
You need not remove the chosen attribute
Lines 6–7 pass $A \setminus \{b\}$ down, but the notes say this is optional. For binary attributes, keeping $b$ is wasteful rather than wrong: every point in the left child already has $x_b = 1$, so a second test on $b$ sends them all one way and leaves the other child empty.
For real-valued attributes you must not remove the attribute. Look at Figure 9 and imagine the truth were a horizontal band $2 \le x_2 \le 4.5$. You need two thresholds on $x_2$ — one at $4.5$, another at $2$ further down the tree. Delete $x_2$ from $A$ after the first split and that band becomes unlearnable.
Real implementations (CART, scikit-learn) therefore never remove attributes. The consequence is that the depth bound "at most $d$" evaporates, and you need a real stopping criterion instead.
Different stopping criteria
Options, from the notes and from practice:
| Criterion | Stop when… | Risk |
|---|---|---|
| $A = \emptyset$ | every attribute has been used on this path | none — but only reachable if you do remove attributes |
| Pure node | all labels in $S$ are the same | none; the node is already perfect on training data |
| Gain threshold "the split doesn't reduce training error by much" | the best available split improves the criterion by less than $\tau$ | blind to XOR and other interaction effects — stops too early |
| Depth limit | depth reaches $D_{\max}$ | crude but effective and very common |
| Minimum leaf size | a child would hold fewer than $m$ points | the most reliable single knob in practice |
Stopping criteria exist because of overfitting. Grown to purity, a tree memorises the training set: it will happily build a leaf holding a single noisy example and predict that example's label for the whole region around it. Training error $\to 0$; test error climbs.
Every criterion above is a way of saying "stop trusting the data before the leaves get too small to trust." This is the same bias–variance tension you meet again with regularisation in feature engineering and overfitting.
Depth limit and minimum leaf size both enforce one rule: never make a claim about a region of feature space from two data points. A leaf holding 40 examples that are 90% positive is a defensible claim; a leaf holding 1 example is an anecdote.
Extension: the menu of BestAttribute criteria
The notes give three options, with the formula for each.
1. Minimise training error
Done above in full: split on each candidate attribute, majority-vote both children, add the mistakes, take the minimum. Simple, intuitive — and, as the next section shows, badly behaved.
2. Minimise Gini impurity beyond syllabus
The notes say "won't discuss here", so one line: for class proportions $p_k$,
$$G \;=\; 1 - \sum_{k \in \Y} p_k^2,$$the probability that two points drawn at random from the node have different labels; choose the split minimising the weighted average of $G$ over the children, exactly as with conditional entropy. It is the CART and scikit-learn default, behaves almost identically to entropy, and needs no logarithm.
3. Maximise mutual information
Instead of "how many mistakes would a stump make?", ask "how much of the uncertainty in the labels does this attribute remove?" Quantifying that needs entropy, which is all of Chapter 7. The criterion, stated now:
$$b \;=\; \argmax_{a \in A} \; I(Y; a) \;=\; \argmax_{a \in A} \big[\,H(Y) - H(Y \mid a)\,\big]$$This is also called the information gain method.
Why error rate is a broken splitting criterion
The pre-reading drops a warning and moves on: "error rate as a splitting criterion is problematic and will [be replaced] with other splitting criteria such as mutual information." That sentence is the hinge between this chapter and the next, so here is the demonstration rather than the promise.
The defect: error rate only counts which class wins in each child. It is completely blind to by how much. A child that goes from 50/50 to 75/25 has become dramatically more certain, but the majority label did not change, so the error count did not change either. Error rate cannot see progress that does not immediately flip a prediction.
A dataset where every split has the same training error
$N = 8$, two binary attributes $a$ and $b$, labels binary.
| $i$ | $y$ | $a$ | $b$ |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 1 |
| 3 | 1 | 0 | 1 |
| 4 | 1 | 0 | 1 |
| 5 | 1 | 1 | 0 |
| 6 | 1 | 0 | 0 |
| 7 | 0 | 1 | 0 |
| 8 | 0 | 0 | 0 |
Labels overall: 6 ones, 2 zeros.
No split at all. Majority vote $= 1$, wrong on rows 7 and 8: 2/8.
Split on $a$.
- $a=1$: rows 1, 2, 5, 7 → labels $1,1,1,0$ → 3 ones, 1 zero → vote 1 → 1 mistake.
- $a=0$: rows 3, 4, 6, 8 → labels $1,1,1,0$ → 3 ones, 1 zero → vote 1 → 1 mistake.
- Total: 2/8.
Split on $b$.
- $b=1$: rows 1, 2, 3, 4 → labels $1,1,1,1$ → pure → vote 1 → 0 mistakes.
- $b=0$: rows 5, 6, 7, 8 → labels $1,1,0,0$ → 2–2 tie → vote 1 (tie-break) → 2 mistakes.
- Total: 2/8.
All three numbers are 2/8. Under the error-rate criterion, splitting on $a$, splitting on $b$, and not splitting at all are indistinguishable. The criterion has no opinion.
But the splits are obviously not equally good. Split $b$ finishes half the dataset permanently — the $b=1$ child is pure and will never need another node. Split $a$ leaves both children in exactly the same 3-to-1 state the parent was in; it achieved literally nothing.
Mutual information does have an opinion
The next chapter derives all of this; here is the punchline. With $H$ the entropy in bits:
| Split | Child 1 (size, labels) | Child 0 (size, labels) | Training error | $H(Y \mid \cdot)$ | $I(Y;\cdot)$ |
|---|---|---|---|---|---|
| none | $H(Y) = H(\tfrac{6}{8}) = 0.8113$ | 2/8 | — | — | |
| $a$ | 4 pts, 3–1 → $H = 0.8113$ | 4 pts, 3–1 → $H = 0.8113$ | 2/8 | $0.8113$ | $0.0000$ |
| $b$ | 4 pts, 4–0 → $H = 0$ | 4 pts, 2–2 → $H = 1$ | 2/8 | $0.5000$ | $0.3113$ |
The arithmetic, since you should be able to do it by hand:
$$H(Y) = -\tfrac{6}{8}\log_2\tfrac{6}{8} - \tfrac{2}{8}\log_2\tfrac{2}{8} = 0.75(0.41504) + 0.25(2) = 0.31128 + 0.5 = 0.81128$$ $$H(Y \mid a) = \tfrac{4}{8}(0.81128) + \tfrac{4}{8}(0.81128) = 0.81128 \;\Longrightarrow\; I(Y;a) = 0.81128 - 0.81128 = \mathbf{0}$$ $$H(Y \mid b) = \tfrac{4}{8}(0) + \tfrac{4}{8}(1) = 0.5 \;\Longrightarrow\; I(Y;b) = 0.81128 - 0.5 = \mathbf{0.31128}$$Mutual information says, unambiguously: $a$ is worthless ($I = 0$ — the children are statistically identical to the parent) and $b$ is valuable ($I = 0.311$ bits — it purified half the data). Error rate could not tell them apart.
Hence the switch of criteria. Error rate is a fine way to evaluate a finished tree; it is a bad way to choose a split, because a split is progress toward a future prediction, and progress is measured in certainty, not in flipped labels.
Using the 8-row $(y, a, b)$ dataset above, build the full tree twice: once with a BestAttribute that breaks ties by preferring $a$, and once preferring $b$. Compare final trees. Assume attributes are removed from $A$ after use, and tie-break majority votes to $1$.
Step 1 — The "prefer $a$" tree
Root splits on $a$. Children: $a{=}1$ holds rows 1,2,5,7 (labels $1,1,1,0$); $a{=}0$ holds rows 3,4,6,8 (labels $1,1,1,0$). Neither pure; $A = \{b\}$ remains.
$a{=}1$ child, split on $b$: $b{=}1$ → rows 1,2 (labels $1,1$, pure); $b{=}0$ → rows 5,7 (labels $1,0$, tie → 1 mistake). $A$ is now empty, so both become leaves.
$a{=}0$ child, split on $b$: $b{=}1$ → rows 3,4 (pure, label 1); $b{=}0$ → rows 6,8 (labels $1,0$, tie → 1 mistake).
Result: 3 internal nodes, 4 leaves, depth 2, training error $2/8$.
Step 2 — The "prefer $b$" tree
Root splits on $b$. $b{=}1$ → rows 1,2,3,4, all label 1 → pure, immediate leaf. $b{=}0$ → rows 5,6,7,8 with labels $1,1,0,0$; $A = \{a\}$.
Split that child on $a$: $a{=}1$ → rows 5,7 (labels $1,0$, tie, 1 mistake); $a{=}0$ → rows 6,8 (labels $1,0$, tie, 1 mistake). $A$ empty, both leaves.
Result: 2 internal nodes, 3 leaves, depth 2, training error $2/8$.
Step 3 — Why is the training error the same?
Because with only two attributes there are four possible $(a,b)$ cells, and the data forces it. Look at the cells: $(1,1)$ holds rows 1,2 — both label 1. $(0,1)$ holds rows 3,4 — both label 1. $(1,0)$ holds rows 5,7 — labels $1,0$. $(0,0)$ holds rows 6,8 — labels $1,0$.
The two mixed cells each contain one point of each label, so no function of $(a,b)$ can classify all eight rows: the data is not separable by these features. Two mistakes is the floor. Both trees reach the floor.
Answer & the takeaway
Both trees hit the irreducible error of $2/8$, but the "prefer $b$" tree does it with one fewer internal node and one fewer leaf, because $b$ cleared half the dataset in a single move.
The takeaway, and the reason for Chapter 7: the criterion that could have told you to prefer $b$ is mutual information ($0.311$ vs $0.000$), not error rate (a dead tie). When two models have equal training error, the simpler one is preferable — fewer leaves means more data per leaf means a more trustworthy prediction. Error rate is structurally unable to express that preference; entropy is.
One real-valued attribute $x \in \R$, binary label. Six training points, sorted by $x$:
| $x$ | 1.0 | 2.0 | 3.0 | 6.0 | 7.0 | 9.0 |
|---|---|---|---|---|---|---|
| $y$ | 0 | 0 | 1 | 0 | 1 | 1 |
Find the best threshold split "Is $x \ge t$?" under the training-error criterion. Tie-break majority votes to $1$.
Step 1 — Enumerate the candidate thresholds
Midpoints of consecutive distinct values: $1.5,\ 2.5,\ 4.5,\ 6.5,\ 8.0$. Five candidates. (There is no candidate below 1.0 or above 9.0, because those give an empty child and the same error as no split.)
Step 2 — Score every candidate
Labels in order: $0,0,1,0,1,1$. For each $t$, the "$x \ge t$" group is a suffix and the "$x < t$" group is a prefix.
| $t$ | $x < t$ labels | vote | err | $x \ge t$ labels | vote | err | total |
|---|---|---|---|---|---|---|---|
| 1.5 | 0 | 0 | 0 | 0,1,0,1,1 | 1 | 2 | 2 |
| 2.5 | 0,0 | 0 | 0 | 1,0,1,1 | 1 | 1 | 1 |
| 4.5 | 0,0,1 | 0 | 1 | 0,1,1 | 1 | 1 | 2 |
| 6.5 | 0,0,1,0 | 0 | 1 | 1,1 | 1 | 0 | 1 |
| 8.0 | 0,0,1,0,1 | 0 | 2 | 1 | 1 | 0 | 2 |
Step 3 — Break the tie between $t = 2.5$ and $t = 6.5$
Both score $1/6$ under error rate. Error rate has, once again, no further opinion. Look at the children:
$t = 2.5$: left child $\{0,0\}$ is pure (2 points); right child is $\{1,0,1,1\}$, 3–1.
$t = 6.5$: left child $\{0,0,1,0\}$ is 3–1; right child $\{1,1\}$ is pure (2 points).
They are mirror images, so entropy ties them too: $H(Y\mid\cdot) = \tfrac{2}{6}(0) + \tfrac{4}{6}(0.8113) = 0.5409$ for both. A genuine, honest tie — which happens, and which is why implementations need a documented deterministic tie-break (e.g. smallest threshold).
Answer & the takeaway
Best stump: "Is $x \ge 2.5$?" → Yes: label 1, No: label 0. Training error $1/6$ (the point at $x=6.0$ with $y=0$ is misclassified). $t = 6.5$ is equally good and would be chosen by an implementation that prefers larger thresholds.
Takeaway. Real-valued attributes add a second search (over $t$) inside the first (over $j$), but the search is finite — at most $N-1$ thresholds per attribute — so the total work at a node is $O(N d)$ after sorting. Nothing conceptual changes; the node still asks one yes/no question and still partitions the data in two.
Recap
Every statement worth memorising is in the Essentials box at the top. The one line to carry forward: error rate is a fine way to evaluate a finished tree and a bad way to choose a split, because it is blind to purity changes that do not flip a majority. Mutual information is not — that is Chapter 7, where $H$ and $I$ are built from scratch via a game of 20 questions.