Decision Tree Basics
Predictive Analytics
Predictive analytics problems can be largely categorized into either class prediction (classification) or numeric prediction (regression) problems.
In classification, we use the information in the sample data to sort the data into distinct classes — this is what a classification tree does:
- predict whether a customer is a loyal customer or a churn customer
- predict whether a loan is going to default or not
- predict whether a passenger of the Titanic is going to survive or not
In numeric prediction, we predict the numeric value of a target/label attribute — this is what a regression tree does:
- predict the housing price
- predict how much a customer is going to spend in the next quarter
Decision trees can be used for both kinds of prediction, which is why they are often referred to as CART: Classification and Regression Trees.
Classification Trees (Decision Trees)
Decision trees (DTs), or classification trees, are one of the most popular classification techniques:
- easy to set up
- easy to interpret (especially for business users)
- computationally cheap
Almost all data mining packages include DTs.
Here is a small tree that recommends a major based on the grades a student earned in a few courses. The vocabulary you need is root, node, and leaf:
The root is the first question the tree asks, an internal node is any later question, and a leaf is a final answer — there is nothing left to ask.
Homogeneity/Purity of Data
The basic idea of a decision tree is to split the dataset based on the homogeneity (of the same kind) of the data, i.e., to reduce impurity.
Impurity (uncertainty) is at its maximum when all possible classes are equally represented — for example, the same number of "default" and "not default" customers. Suppose we have 30 loan customers, 16 of whom defaulted, and we split them twice, first on balance and then on age:
The top leaf contains 10 customers who all belong to the same class: it is 100% pure, or has 0% impurity. The tree has done its job on that branch. The two middle leaves are still mixed, so they are still impure.
Entropy
Entropy is one of the most common measures for calculating impurity. It was proposed by Claude Shannon, known as "the father of information theory":
where is the probability of class in the data, and is the total number of classes.
For two classes, entropy is 0 when the data is completely pure (every case belongs to one class) and 1 when the two classes are split 50/50 — the curve peaks in the middle:
import numpy as np
import matplotlib.pyplot as plt
p = np.linspace(0.001, 0.999, 500)
entropy = -p * np.log2(p) - (1 - p) * np.log2(1 - p)
plt.plot(p, entropy)
plt.xlabel('% of one class')
plt.ylabel('entropy')
plt.title('entropy for two classes')
plt.show()For the loan dataset above, there are two classes — "default" (16 cases) and "not default" (14 cases) — so the entropy of the entire dataset is:
Almost 1 — this dataset is about as impure as a two-class dataset can be.
ID3 Decision Tree Algorithm
ID3 (Iterative Dichotomiser 3) was developed in 1986 by Ross Quinlan. We will walk through it using the classic golf dataset: decide whether to play golf or not based on four attributes — outlook, temperature, humidity, and windy.
| Outlook | Temp | Humidity | Windy | Play Golf |
|---|---|---|---|---|
| rainy | hot | high | FALSE | no |
| rainy | hot | high | TRUE | no |
| overcast | hot | high | FALSE | yes |
| sunny | mild | high | FALSE | yes |
| sunny | cool | normal | FALSE | yes |
| sunny | cool | normal | TRUE | no |
| overcast | cool | normal | TRUE | yes |
| rainy | mild | high | FALSE | no |
| rainy | cool | normal | FALSE | yes |
| sunny | mild | normal | FALSE | yes |
| rainy | mild | normal | TRUE | yes |
| overcast | mild | high | TRUE | yes |
| overcast | hot | normal | FALSE | yes |
| sunny | mild | high | TRUE | no |
Building the tree comes down to answering two questions over and over:
- Where to split the data?
- When to stop splitting?
Sources: ID3 algorithm and saedsayad.com/decision_tree.htm.
Entropy Calculation Using Frequency Tables
There are two entropy calculations we need, and both can be done from a frequency table.
Entropy of the target variable
The golf dataset has 9 "yes" and 5 "no":
| Play Golf: Yes | Play Golf: No |
|---|---|
| 9 | 5 |
Entropy of the target given one feature
where is the target and is the feature. Cross-tabulate the feature against the target:
| Outlook | Yes | No | Total |
|---|---|---|---|
| sunny | 3 | 2 | 5 |
| overcast | 4 | 0 | 4 |
| rainy | 2 | 3 | 5 |
| all | 9 | 5 | 14 |
Each branch contributes its own entropy, weighted by how much of the data lands in it:
Note that the overcast branch contributes nothing: all four of its cases are "yes", so its entropy is 0.
Information Gain/Entropy Reduction
Information gain is the decrease in entropy after the dataset is split on an attribute — information gain is entropy reduction:
For outlook:
Constructing a decision tree is all about finding the attribute that returns the highest information gain at each split.
Choosing the First Split
Repeat the same frequency-table calculation for all four attributes:
| Attribute | Branches (yes/no) | Weighted entropy | Gain |
|---|---|---|---|
| Outlook | sunny 3/2, overcast 4/0, rainy 2/3 | 0.693 | 0.247 |
| Humidity | high 3/4, normal 6/1 | 0.788 | 0.152 |
| Windy | FALSE 6/2, TRUE 3/3 | 0.892 | 0.048 |
| Temp | hot 2/2, mild 4/2, cool 3/1 | 0.911 | 0.029 |
Outlook has the largest gain, so it becomes the decision node. Divide the dataset by its branches and repeat the same process on every branch.
Outlook = sunny — still mixed, needs further splitting:
| Outlook | Temp | Humidity | Windy | Play Golf |
|---|---|---|---|---|
| sunny | mild | high | FALSE | yes |
| sunny | cool | normal | FALSE | yes |
| sunny | cool | normal | TRUE | no |
| sunny | mild | normal | FALSE | yes |
| sunny | mild | high | TRUE | no |
Outlook = overcast — already pure (entropy is 0), no more splitting:
| Outlook | Temp | Humidity | Windy | Play Golf |
|---|---|---|---|---|
| overcast | hot | high | FALSE | yes |
| overcast | cool | normal | TRUE | yes |
| overcast | mild | high | TRUE | yes |
| overcast | hot | normal | FALSE | yes |
Outlook = rainy — still mixed, needs further splitting:
| Outlook | Temp | Humidity | Windy | Play Golf |
|---|---|---|---|---|
| rainy | hot | high | FALSE | no |
| rainy | hot | high | TRUE | no |
| rainy | mild | high | FALSE | no |
| rainy | cool | normal | FALSE | yes |
| rainy | mild | normal | TRUE | yes |
Splitting Recursively
The rule for each branch:
- a branch with entropy of 0 is a leaf node
- a branch with entropy greater than 0 needs further splitting
- repeat recursively on the non-leaf branches until all data is classified
Take the sunny branch (5 rows, entropy 0.971) and compute the gain of the three remaining attributes on those rows only:
| Attribute | Branches (yes/no) | Gain |
|---|---|---|
| Windy | FALSE 3/0, TRUE 0/2 | 0.971 |
| Humidity | high 1/1, normal 2/1 | 0.020 |
| Temp | mild 2/1, cool 1/1 | 0.020 |
Windy splits the sunny rows perfectly, so it is chosen as the next decision node — and both of its branches are pure, so both are leaves:
The rainy branch is still impure, so run the same calculation once more on its 5 rows:
| Attribute | Branches (yes/no) | Gain |
|---|---|---|
| Humidity | high 0/3, normal 2/0 | 0.971 |
| Temp | hot 0/2, mild 1/1, cool 1/0 | 0.571 |
| Windy | FALSE 1/2, TRUE 1/1 | 0.020 |
Humidity splits it perfectly. Every branch is now pure, so the tree is complete:
Read a prediction off the tree by walking from the root down to a leaf. A cool, humid, windy, overcast day? Outlook is overcast, so play golf — the tree never even looks at the other three attributes.
Split Points for Numeric Variables
The golf dataset above used categorical attributes ("hot", "high"). Real temperature and humidity readings are numbers, and a decision tree needs a split point to turn a number into a yes/no question.
| Temperature | Humidity |
|---|---|
| 64 | 65 |
| 65 | 70 |
| 68 | 80 |
| 69 | 70 |
| 70 | 96 |
| 71 | 80 |
| 72 | 95 |
| 72 | 90 |
| 75 | 80 |
| 75 | 70 |
| 80 | 90 |
| 81 | 75 |
| 83 | 78 |
| 85 | 85 |
Method 1: use averages of available values. Sort the values, then try the midpoint between each adjacent pair as a candidate split. For temperature: first split at Avg(64, 65) = 64.5, then Avg(65, 68) = 66.5, and so on — computing the information gain of each candidate and keeping the best.
Method 2: discretize the values via "binning". Turn the numbers back into categories once, up front. For temperature: >= 80 is "hot", between 70 and 79 is "mild", and less than 70 is "cool".
When to Stop?
The ID3 algorithm as described keeps splitting until every leaf is pure, which may lead to a very complex tree with hundreds of nodes. Such a tree may work well on the training data but predict poorly on unseen data — this is called overfitting, and we will discuss it more in a later lecture.
The general fix is regularization: limiting how complex a model may become, so it can't fit the training data perfectly. You give up a little training accuracy to gain accuracy on new data.
For a decision tree, regularization means pruning, which comes in two kinds:
- Pre-pruning — stop the tree from growing, such as by setting a maximum depth or a minimum information gain required to make a split:
max_depth,min_samples_leaf,min_samples_split,max_leaf_nodes,min_impurity_decrease. - Post-pruning — fully grow the tree, then cut back the branches that aren't worth keeping, with minimal cost-complexity pruning:
ccp_alpha.
Every stopping rule below is an argument to DecisionTreeClassifier, and each one answers "when to stop" differently:
| Option | Refuses the split when | Default |
|---|---|---|
max_depth | the node is already this many levels below the root | None — no limit |
min_samples_split | the node holds fewer training rows than this | 2 |
min_samples_leaf | either child would hold fewer rows than this | 1 |
max_leaf_nodes | the tree already has this many leaves | None — no limit |
min_impurity_decrease | the best available split improves impurity by less than this | 0.0 |
ccp_alpha | post-pruning: a branch costs more complexity than the impurity reduction it buys — see below | 0.0 — no pruning |
Read the default column again: scikit-learn prunes nothing out of the box. A node splits as long as it is impure and holds at least two rows, so the tree grows until every leaf is pure — the overfitting-prone tree this section opened with. A tree left at its defaults can score a perfect 1.0 on the training data and still lose to a three-condition rule you wrote by hand. Note also that criterion defaults to 'gini' rather than the 'entropy' we computed by hand above.
So pre-pruning is what you reach for first, and two of those options carry most of the weight:
max_depthlimits how many questions the tree may ask before it must decide. Depth 3 means at most 3 questions, so at most 8 leaves. This is the interpretability knob: a depth-3 tree fits on a slide and you can read it aloud to the revenue director.min_samples_leaflimits how much evidence each answer must rest on. Set it to 20 and the tree may not create a leaf covering fewer than 20 training rows — so it can't propose a rule backed by two customers. This is the noise knob.
from sklearn.tree import DecisionTreeClassifier
tree_clf = DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_leaf=20)The two are not the same constraint: depth caps the questions, leaf size caps the confidence. A shallow tree can still isolate a single customer, if one split happens to cut that customer off at depth 2.
Post-Pruning with ccp_alpha (optional)
ccp_alpha stands for cost-complexity pruning alpha. It scores a tree like this:
- Impurity of its leaves — how badly the tree fits the training data: the Gini or entropy of each leaf, weighted by its share of the rows. A fully grown tree scores 0 here.
- α × number of leaves — a price per leaf. Every extra leaf makes the tree more complex, and α says how much that complexity costs.
The pruning then works like this:
-
Grow the full tree, with every leaf pure.
-
For each branch, ask how much impurity it removes per extra leaf it adds. That is the branch's effective α:
-
If a branch's effective α is below
ccp_alpha, it does not pay for its leaves, so it is cut back to a single leaf. The weakest branch goes first, and this repeats until every remaining branch pays for itself.
So ccp_alpha=0 (the default) prunes nothing, and a larger ccp_alpha makes every leaf more expensive, so the tree gets smaller.
The business framing: every leaf has to earn its keep. A branch stays only if the fit it adds is worth more than the complexity it costs.
Which numbers to use cannot be read off the data. In Model Evaluation we score several candidate values on rows the tree never saw during training, and keep whichever wins — until then, any particular depth is a guess.
Decision Tree
A decision tree splits the data into increasingly pure groups. We first work out how those splits are chosen by hand on the golf dataset, then fit a tree…
Decision Tree Prediction
We create a basic Decision Tree model with all default settings to show the key steps for building a baseline model-based prediction (no model selection, no…