Loading...
Loading...
Loading Curriculum...
Loading Subject...
Loading Topic...
Loading Lesson...
Loading Lab...
Decision trees build a rule list by repeatedly choosing the next split that reduces impurity. In the lab, each step expands one node—until the leaves become confident.
A flowchart for predictions.
A decision tree asks a sequence of questions like a “choose-your-path” story. Each question is a split on one feature (e.g., ). At the end, each leaf outputs a class.
Think of triage in a clinic: “Is temperature high?” → “Is cough severe?” → then decide the next action.
Every split carves a rectangle.
For every node, we try candidate thresholds and compute how “mixed” the labels are (impurity). We pick the split that makes left/right nodes more pure.
At inference time, you follow splits until you reach a leaf. The leaf’s class is a majority vote based on its training subset.
Uncertainty → less uncertainty.
A node is “mixed” when it contains both classes. A good split creates two smaller sets where each set is closer to pure. The result is a tree that turns complex shapes into axis-aligned rectangles.
In every step, the lab chooses a split with the highest impurity gain under your constraints.
Computing tree…
Expand one node per step.
Loading Lab...
A tree chooses the split that reduces impurity the most.
If all labels are class 1 or class 0, then and impurity becomes 0. Mixed nodes have higher impurity.
Entropy measures uncertainty. Trees can use entropy or Gini—your slider switches the scoring rule.
The algorithm keeps expanding the node with the biggest impurity gain that also satisfies your split constraints.
Explainability + fast rules.
How the model’s shape evolves.
Use Step Forward to watch how regions change from a coarse guess (root leaf) into a more refined partition (deeper splits).
Click the plot after a few steps: the same point will jump between regions if the tree has not expanded enough yet.
Clear and beginner-friendly.
# Decision Tree (from scratch, tiny CART-style demo)
import math
def gini(labels):
n = len(labels)
if n == 0: return 0.0
p1 = sum(labels) / n
return 2 * p1 * (1 - p1)
def split(points, axis, thr):
left = [p for p in points if (p["x1"] if axis=="x1" else p["x2"]) <= thr]
right = [p for p in points if (p["x1"] if axis=="x1" else p["x2"]) > thr]
return left, right
def best_split(points, min_samples=8):
impurity_before = gini([p["y"] for p in points])
best = None
for axis in ["x1","x2"]:
values = sorted({p[axis] for p in points})
for i in range(len(values)-1):
thr = (values[i] + values[i+1]) / 2
left, right = split(points, axis, thr)
if len(left) < min_samples or len(right) < min_samples:
continue
labelsL = [p["y"] for p in left]
labelsR = [p["y"] for p in right]
impurity_after = (len(left)/len(points))*gini(labelsL) + (len(right)/len(points))*gini(labelsR)
gain = impurity_before - impurity_after
if best is None or gain > best["gain"]:
best = {"axis": axis, "thr": thr, "gain": gain}
return bestIn production you’ll typically use libraries like scikit-learn. The lab code exists to make the “split choice” logic visible, not to replace battle-tested implementations.
Overfitting happens easily when trees get too deep.
Memorize these patterns.