Machine Learning Slides 📂 Introduction · 8 of 17 55 min read

Decision Trees Explained — Splits, Gini, Entropy, Pruning & Feature Importance

A visual, beginner-friendly guide to Decision Trees built around a loan-officer analogy and the classic Play-Tennis dataset. Learn tree anatomy, how Gini and Entropy score splits, how Information Gain picks the winner, why unpruned trees always overfit, pre- and post-pruning with max_depth and ccp_alpha, feature importance via MDI, sklearn implementation, six common pitfalls and six golden rules.

🌳

Decision Trees

The if-else engine of interpretable ML — how splits are chosen with Gini and Entropy, why unpruned trees always overfit, and how they became the building block of Random Forest and XGBoost.
Tree Anatomy Gini & Entropy Pruning Feature Importance

Press Next → or use ← → arrow keys

Section 01

What is a Decision Tree?

Ramesh, a loan officer, thinks in if-else long before ML does
Ramesh checks the applicant's credit score first. If it's above 700, he then asks about income. If below, he asks about employment history. Every decision opens two branches, and every path eventually lands on approve or reject.

A Decision Tree is the same idea, formalised: a flowchart of yes/no questions the machine learns from your training data — no scaling, no engineering, no black box. Every prediction comes with a readable trace of the questions that led to it.
💡
The Working Definition

A Decision Tree is a supervised algorithm that models predictions as a hierarchy of if-else questions, learned automatically by scoring which split separates the classes best at every step.

🌱→🌳Root grows into leaves
O(log n)Prediction speed
100%Interpretable
RF · XGBFoundation of ensembles
Section 02

Tree Anatomy — Root, Nodes, Leaves

≥ 700 < 700 high low > 5 yr < 5 yr Credit Score? Annual Income? Employment Years? ✓ APPROVED ✗ REJECTED ✓ APPROVED ✗ REJECTED ROOT → DECISION → LEAF → Ramesh's loan-approval tree · depth 2
🌱
Root Node
the best first split
Top of the tree. Holds every training sample. Split on the feature that separates classes best.
🔀
Decision Node
tests a feature
Internal node that asks a question and routes samples down a YES or NO branch.
🍃
Leaf Node
the prediction
Terminal node — outputs the final class label or (for regression) the mean value of samples that land there.
Section 03

Impurity — How A Split Is Scored

0 0.5 1 1.0 0.8 0.4 p · probability of Class 1 → impurity → Entropy · peaks at 1.0 Gini · peaks at 0.5 p = 0.5 · max impurity
Gini Impurity · sklearn default
Gini(S) = 1 − Σ pᵢ²
Fast to compute (no log). Range 0–0.5 for binary. Peaks when classes are 50/50.
Shannon Entropy · ID3 / C4.5
H(S) = − Σ pᵢ · log₂(pᵢ)
More sensitive to skewed distributions. Range 0–log₂(n). Slightly slower.
🎯
Same Tree, Different Numbers

Both curves have the same shape — zero at the ends (pure), maximum in the middle (50/50 chaos). They almost always pick the same split in practice. Use Gini for speed; use Entropy when you want the extra sensitivity on skewed multi-class data.

Section 04

Information Gain — The Split That Wins

For every candidate split, compute how much impurity drops. The one with the biggest drop becomes the split. On the classic Play-Tennis dataset [9 Yes, 5 No]:

Parent Entropy
H(S) = −(9/14)log₂(9/14) − (5/14)log₂(5/14) = 0.940
Starting entropy of the full 14-sample dataset before any split.
Information Gain
IG = H(parent) − Σ (|Sᵥ|/|S|) · H(Sᵥ)
Weighted sum of children's entropy, subtracted from parent. Higher IG = better split.
FeatureWeighted Child EntropyInformation GainVerdict
Outlook0.6940.246 🏆Winner — becomes root
Humidity0.7890.151Runner-up
Wind0.8920.048Weak splitter
Temperature0.9110.029Never selected
🏆
Outlook Wins Because It Creates The Purest Children

Splitting on Outlook produces one perfectly pure child (Overcast: 4 Yes, 0 No → entropy 0). That single clean split alone recovers almost 25% of the parent entropy. Temperature's tiny 0.029 gain means it barely helps distinguish classes — the tree will ignore it.

Section 05

Building The Tree — Play Tennis Result

Sunny Overcast Rain High Normal Strong Weak Outlook? [9+, 5−] · IG 0.246 Humidity? [2+, 3−] · IG 0.971 ✓ PLAY [4+, 0−] · pure Wind? [3+, 2−] · IG 0.971 ✗ NO ✓ PLAY ✗ NO ✓ PLAY
🌳
Depth 2 · 5 Leaves · 100% Training Accuracy

All five leaves are pure (entropy = 0). Temperature was scored, deemed useless (IG = 0.029) and never included. The tree captured the pattern in just two splits — human-readable, testable, deployable.

Section 06

The CART Algorithm — 4 Steps, Repeat

🔍
1. Try All Splits
every feature × every threshold
For each numeric feature try all midpoints between sorted values. For each categorical feature try each value. Compute impurity gain for each.
🏆
2. Pick The Best
greedy · local optimum
The (feature, threshold) with maximum information gain becomes this node's split. Locally optimal — not globally.
🔁
3. Recurse On Children
independent per subset
Apply steps 1–2 on each child node independently. Different features often win on different branches.
🛑
4. Stop
purity or budget hit
Halt when leaf is pure, samples fall below min_samples_split, max_depth reached, or no split improves impurity.
⚠️
Greedy — Not Guaranteed Optimal

CART picks the best split now, not the split that would enable the best future splits. A globally optimal tree is NP-hard. In practice, the greedy choice is good enough — especially inside ensembles that average many trees.

Section 07

Decision Boundary — Axis-Aligned Rectangles

Feature x₁ (e.g. income) → Feature x₂ (credit score) → split 1: x₁ ≥ 260 split 2 split 3
📐
Every Split Is Axis-Aligned

A tree can only split parallel to an axis (horizontal or vertical cuts in 2D). If your classes are truly separated by a diagonal, a single tree will need many stair-step splits to approximate it. Rotate features or switch to SVM / linear models when diagonal patterns dominate.

Section 08

Depth vs Overfitting — The Tree's Achilles Heel

An unconstrained tree keeps splitting until every leaf is pure — memorising every training point, including the noise. That looks brilliant on train and collapses on test.

max_depthTrain AccuracyTest AccuracyGapVerdict
287%86%1%Slight underfit
493%90%3%Sweet spot ✅
898%87%11%Starting to overfit
None (unlimited)100%82%18%Severe overfit
🚨
100% Training Accuracy Is A Red Flag, Not A Trophy

Real data always has noise. A tree hitting 100% on training has memorised, not learned. Always constrain depth — start at 3, increase until validation accuracy plateaus.

Section 09

Pruning — Trading Some Bias For A Lot Less Variance

❌ UNPRUNED · memorises noise depth 4 · 15 nodes · train 100% test 82% ✅ PRUNED · captures the pattern ✂️ ✂️ depth 2 · 7 nodes · train 93% test 90%
✂️
Two Ways To Prune

Pre-pruning stops growth early using hyperparameters: max_depth, min_samples_split, min_samples_leaf. Post-pruning (Cost Complexity, ccp_alpha) grows the full tree first, then collapses branches whose complexity penalty exceeds their gain. Both close the train-test gap.

Section 10

Hyperparameters — The Six Dials That Matter

ParameterDefaultEffectToo LowToo High
max_depthNoneMax levels from root to leafUnderfitOverfit
min_samples_split2Min samples to attempt a splitSplits noiseStops early
min_samples_leaf1Min samples in any final leafSingle-sample leavesForces large leaves
max_featuresNoneFeatures considered per splitRandomNo benefit
criterion'gini'Split scoring function
ccp_alpha0.0Post-pruning penalty strengthNo pruningPrunes to stump
🎛️
A Safe Starting Point For Production

max_depth = 4, min_samples_split = 10, min_samples_leaf = 5, criterion = 'gini'. Then let GridSearchCV refine around that on a log grid. Tune on validation folds — never on the test set.

Section 11

Feature Importance — What The Tree Actually Uses

Mean Decrease in Impurity → credit_score 0.582 debt_ratio 0.234 annual_income 0.151 employment_years 0.022 num_accounts 0.011 ← noise
📊
Read Feature Importance Like A Report Card

Every split reduces impurity by some amount. Sum those reductions per feature → normalise → you get each feature's share of the tree's decisions. In Ramesh's model, credit score alone drives 58% of the predictions. Features with near-zero importance — like num_accounts — can be dropped without hurting accuracy.

Section 12

Strengths & Weaknesses

📖
Fully Interpretable
Every prediction comes with a readable if-else path. Regulators, doctors and lawyers love this.
📏
No Feature Scaling
Splits are on thresholds, not distances. Age 25 and income ₹5,00,000 coexist without issue.
🔀
Mixed Data Types
Numeric, categorical, ordinal — all handled natively. Missing values with surrogate splits.
🌀
High Variance
Small data change → completely different tree. Unstable in isolation. Cured by ensembling.
📐
Axis-Aligned Only
Cannot draw a diagonal boundary in one split. Diagonal patterns need many stair-step splits.
🎯
Greedy Local Optimum
Picks the best split now, not globally. The perfect tree is NP-hard — greedy is good enough.
🌲
Why Trees Rule Tabular ML — Via Ensembles

A single Decision Tree is rarely production-worthy. Its true power is as the atom of ensembles: Random Forest (bagging → crushes variance), Gradient Boosting / XGBoost / LightGBM (sequential correction → crushes bias). Both still win Kaggle tabular competitions in 2026.

Section 13

Implementation — Scikit-learn In A Dozen Lines

from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.model_selection import GridSearchCV, train_test_split

# ── Fit with sane production defaults ────────────
dt = DecisionTreeClassifier(
    criterion='gini',
    max_depth=4,
    min_samples_split=10,
    min_samples_leaf=5,
    random_state=42,
)
dt.fit(X_train, y_train)
proba = dt.predict_proba(X_new)                    # calibrated probs

# ── Inspect & visualise ─────────────────────────
plot_tree(dt, feature_names=cols, class_names=['reject', 'approve'], filled=True)
print(dict(zip(cols, dt.feature_importances_)))

# ── Tune with GridSearchCV ──────────────────────
grid = GridSearchCV(
    DecisionTreeClassifier(random_state=42),
    param_grid={
        'max_depth':         [2, 3, 4, 5, 6],
        'min_samples_split': [5, 10, 15, 20],
        'min_samples_leaf':  [2, 5, 10],
        'criterion':         ['gini', 'entropy'],
    },
    cv=5, scoring='roc_auc', n_jobs=-1,
)
grid.fit(X_train, y_train)
print(grid.best_params_, grid.best_score_)
🧪
Two Non-Negotiables

Always set random_state for reproducibility. Always cap max_depth in production — the default (None) invites overfitting. Then let CV pick the exact value.

Section 14

Common Pitfalls — What Bites Practitioners

🌲
Unlimited Depth
the default trap
sklearn's default max_depth=None lets trees grow until every leaf is pure → 100% train, disaster on test.
🎲
Unstable Across Seeds
high variance
Same data, different random state → different tree. Report CV mean ± std, and consider ensembling.
🔢
High-Cardinality Bias
many unique values win
Raw Information Gain favours features with many unique values (IDs, timestamps). Gini and gain-ratio help.
📐
Diagonal Patterns
axis-aligned only
Trees stair-step across diagonals — inefficient. Rotate features (PCA) or switch to SVM / logistic.
📦
Ignoring Class Imbalance
90/10 → always majority
Set class_weight='balanced' or resample. Otherwise the majority class dominates every split.
🚫
Trusting A Single Tree
use ensembles
Rarely deploy one tree in production. Random Forest for stability; XGBoost for accuracy.
Section 15 · Part 1

Golden Rules — 1 to 3

🌳 DECISION TREE DISCIPLINE · RULES 1–3
1
Always constrain max_depth. Start at 3, increase by 1 until validation accuracy plateaus. Never leave the default at None in production.
2
Skip feature scaling — you don't need it. Trees split on thresholds, not distances. Save the StandardScaler for logistic / SVM / KNN.
3
Prefer Gini for speed; Entropy for skewed multi-class. They produce nearly identical trees. Gini avoids the log and runs faster on wide data.
Section 15 · Part 2

Golden Rules — 4 to 6

🌳 DECISION TREE DISCIPLINE · RULES 4–6
4
Zero feature importance = redundancy. If a feature is never split on, the tree tells you it's noise. Prune it from your dataset and retrain leaner.
5
Report train AND test accuracy — always with the gap. Train 100% and Test 82% is a red flag, not a headline. A stable tree keeps that gap under 5%.
6
Use trees for interpretability; ensembles for accuracy. Ship a single tree when you must explain every decision. Ship XGBoost / Random Forest when accuracy wins.
The Deployment Checklist

✅ max_depth capped · ✅ random_state set · ✅ CV-tuned hyperparameters · ✅ Feature importance sanity-checked · ✅ Train-test gap under 5% · ✅ Ensemble considered.

FINAL

Decision Trees — Simple, Readable, Foundational

🌱→🍃Root to leaves
Ginior Entropy · same idea
IGInformation Gain picks the split
✂️Prune to control overfitting
6Hyperparameters that matter
RF · XGBThe ensembles built on trees
🎯
The Foundation Is Set

You now understand tree anatomy, how impurity drives splits, why unpruned trees fail, and how feature importance falls out for free. Every tabular ML model you'll build from here — Random Forest, XGBoost, LightGBM, CatBoost — is a Decision Tree grown up.

📚
Where To Go Next

Study Random Forest next (bagging + feature subsampling), then Gradient Boosting (XGBoost, LightGBM, CatBoost). Practise on Kaggle's Titanic or House Prices — watch a single tree lose to a properly-tuned forest by 3–5 points every time.

🌳 End of tutorial · Press to review, or click Restart