TAI BUI
Back to curriculum
BuildPython

Decision Trees and Random Forests

A decision tree is a flowchart that learned which questions to ask. A forest of them is one of the strongest tools in ML.

TIME: ~90 minutesPREREQUISITES: Phase 1 (Lessons 09 Information Theory, 06 Probability)

Learning Objectives

  • Implement Gini impurity, entropy, and information gain calculations to find optimal decision tree splits
  • Build a decision tree classifier from scratch with pre-pruning controls (max depth, min samples)
  • Construct a random forest using bootstrap sampling and feature randomization, and explain why it reduces variance
  • Compare MDI feature importance with permutation importance and identify when MDI is biased

The Problem

You have tabular data: rows are samples, columns are features, and one column is the target you're trying to predict. It's tempting to reach for a neural network. Don't. For tabular data, tree-based models (decision trees, random forests, gradient boosted trees) consistently beat deep learning, and Kaggle's structured-data leaderboards prove it: XGBoost and LightGBM win, not transformers.

Why? Trees handle mixed feature types (numeric and categorical) with zero preprocessing. They pick up nonlinear relationships without any feature engineering. They're interpretable: read the tree and you know exactly why it made a given prediction. And a random forest, which averages many trees together, is highly resistant to overfitting on moderate-sized datasets.

This lesson builds a decision tree from scratch with recursive splitting, then stacks a random forest on top of it. You'll implement the math behind split criteria (Gini impurity, entropy, information gain) and see exactly why an ensemble of weak learners adds up to one strong learner.

Warm-up0/3
1. You're sorting red and blue marbles into two jars by color. Which jar would you call more 'pure': one with 9 red and 1 blue, or one with 5 red and 5 blue?Warm-up
Two jars of marbles. Jar 1 holds five red and five blue marbles, Jar 2 holds nine red and one blue marble.
Marble positions inside each jar are arbitrary.
2. A model scores 100% on the data it was trained on but only 60% on fresh data. What is the most likely problem?Warm-up
Two bars for the same model: 100 percent accuracy on its training data and 60 percent on fresh data.
Values from the question.
3. You draw a card from a deck, note it, put it back, shuffle, and repeat five times. Can the same card show up more than once?Warm-up
A deck of cards on the left and five empty slots labelled draw 1 to draw 5, each holding a question mark. A note says the card is noted, put back and the deck shuffled after each draw.
Schematic of the procedure in the question.
Check: The Problem0/2
1. Why do tree-based models often beat neural networks on flat tabular data?Explain
A table of four customer records with columns age, plan, country, spend and a churned column to predict.
Typed example rows, not real data.
2. You must predict churn from 5,000 rows of mixed numeric and categorical columns, and the business wants to read the reasons for each prediction. Which design fits best?Design
A table of three example rows with tenure, plan, country, calls and churned columns, and two lines saying there are 5,000 rows of mixed columns and the business wants readable reasons for each prediction.
Typed example rows, not real data.

The Concept

What a decision tree does

A decision tree partitions the feature space into rectangular regions by asking a sequence of yes/no questions.

Mermaid diagram

Each internal node tests one feature against a threshold; each leaf makes a prediction. Classify a new point by starting at the root and following branches down until you hit a leaf.

The tree gets built top-down: at each node, pick the feature and threshold that best separate the data. What counts as "best" is defined by a split criterion.

Split criteria: measuring impurity

At each node you're holding a set of samples, and you want to split them so the resulting child nodes are as "pure" as possible: each child dominated by a single class.

Gini impurity measures the probability that a randomly picked sample gets misclassified if you label it according to the node's class distribution.

Gini(S) = 1 - sum(p_k^2)

where p_k is the proportion of class k in set S.

For a pure node (all one class), Gini is 0. A 50/50 binary split lands at Gini 0.5. Lower is better.

Example: 6 cats, 4 dogs

Gini = 1 - (0.6^2 + 0.4^2) = 1 - (0.36 + 0.16) = 0.48

Entropy measures a node's information content, its disorder. Phase 1 Lesson 09 covers it in depth.

Entropy(S) = -sum(p_k * log2(p_k))

For a pure node, entropy is 0. A 50/50 binary split hits entropy 1.0. Lower is better.

Example: 6 cats, 4 dogs

Entropy = -(0.6 * log2(0.6) + 0.4 * log2(0.4))
        = -(0.6 * -0.737 + 0.4 * -1.322)
        = 0.442 + 0.529
        = 0.971 bits

Information gain is the drop in impurity, Gini or entropy, that a split buys you.

IG(S, feature, threshold) = Impurity(S) - weighted_avg(Impurity(S_left), Impurity(S_right))

where the weights are the proportions of samples in each child.

The greedy algorithm at each node is simple: try every feature and every possible threshold, then keep whichever (feature, threshold) pair maximizes information gain.

How splitting works

For a dataset with n features and m samples at the current node:

  1. For each feature j (j = 1 to n):
    • Sort the samples by feature j
    • Try every midpoint between consecutive distinct values as a threshold
    • Compute the information gain for each threshold
  2. Select the feature and threshold with the highest information gain
  3. Split the data into left (feature <= threshold) and right (feature > threshold)
  4. Recurse on each child

This greedy approach doesn't guarantee the globally optimal tree, and finding that optimal tree is NP-hard anyway. Greedy splitting still works well in practice.

Stopping conditions

Left alone, the tree keeps growing until every leaf is pure, one sample per leaf. That memorizes the training data perfectly, and generalizes terribly.

Pre-pruning stops the tree before it fully grows:

  • Maximum depth: stop splitting when the tree reaches a set depth
  • Minimum samples per leaf: stop if a node has fewer than k samples
  • Minimum information gain: stop if the best split improves impurity by less than a threshold
  • Maximum leaf nodes: limit the total number of leaves

Post-pruning grows the full tree, then trims it back:

  • Cost-complexity pruning (used by scikit-learn): adds a penalty proportional to the number of leaves. Increase the penalty to get smaller trees
  • Reduced error pruning: remove a subtree if the validation error does not increase

Pre-pruning is simpler and faster to run. Post-pruning tends to produce better trees, since it never prematurely kills a split that would have paid off a level or two down.

Decision trees for regression

For regression, each leaf predicts the mean of its target values, and the split criterion changes to match:

Variance reduction replaces information gain:

VR(S, feature, threshold) = Var(S) - weighted_avg(Var(S_left), Var(S_right))

Pick whichever split reduces variance the most. The tree still partitions the input space into regions; it just predicts a constant, the region's mean, in each one.

Random forests: the power of ensembles

A single decision tree runs high variance: a small change in the training data can produce a completely different tree. Random forests fix this by averaging many trees together.

Mermaid diagram

Two sources of randomness keep those trees from all looking alike:

Bagging (bootstrap aggregating): each tree trains on a bootstrap sample, a random sample with replacement drawn from the training data. Roughly 63% of the original samples show up in any given bootstrap; the rest are out-of-bag samples you can use for validation.

Feature randomization: at each split, the tree only gets to consider a random subset of features, sqrt(n_features) by default for classification, n_features/3 for regression. That stops every tree from splitting on whatever feature happens to dominate.

The key insight: average enough decorrelated trees and variance drops without bias going up. Any single tree in the forest might be mediocre. The forest isn't.

Feature importance

Random forests hand you feature importance scores for free. The most common method:

Mean Decrease in Impurity (MDI): for each feature, add up the total impurity reduction across every tree and every node where that feature gets used. A feature that drives bigger impurity drops, especially at early splits, ranks as more important.

importance(feature_j) = sum over all nodes where feature_j is used:
    (n_samples_at_node / n_total_samples) * impurity_decrease

MDI is fast, computed right there during training, but it's biased toward high-cardinality features and features with lots of possible split points.

Permutation importance is the fix: shuffle one feature's values and measure how far the model's accuracy falls. Slower, but far more reliable.

When trees beat neural networks

Trees and forests dominate neural networks on tabular data, for several reasons:

FactorTreesNeural networks
Mixed types (numeric + categorical)Native supportNeed encoding
Small datasets (< 10k rows)Work wellOverfit
Feature interactionsFound by splittingNeed architecture design
InterpretabilityFull transparencyBlack box
Training timeMinutesHours
Hyperparameter sensitivityLowHigh

Neural networks take over once the data has spatial or sequential structure: images, text, audio. For flat feature tables, trees are the default.

Check: The Concept0/30
1. What is a decision tree?Define
A small tree diagram. The top box asks whether x1 is at most 2.5. The yes branch ends in a leaf class A. The no branch leads to a box asking whether x2 is at most 1.0, with leaves class B for yes and class A for no.
Typed illustration with made-up feature names and thresholds.
2. What does Gini impurity measure at a node?Define
Three small nodes drawn as coloured dots: four cats and no dogs with Gini 0, three cats and one dog with Gini 0.375, and two cats and two dogs with Gini 0.5.
Gini computed with numpy from the counts.
3. What does entropy measure at a node, and what values does it take for a pure node and a 50/50 binary node?Define
Node P with eight samples of one class and Node Q with four samples of each of two classes, each labelled with entropy equal to a question mark.
Schematic with typed counts.
4. What does information gain measure when choosing a split?Define
A parent node with four cats and four dogs split by a feature threshold into a left child with three cats and one dog and a right child with one cat and three dogs. A label reads gain equals question mark.
Typed counts; each dot is one sample.
5. What is pre-pruning?Define
Four small tree icons showing one tree growing: the root alone, then after one, two and three levels of splits.
Schematic of tree growth.
6. What is post-pruning?Define
A complete binary tree icon with three levels of splits and eight leaves.
Schematic of a finished tree.
7. What is a bootstrap sample?Define
Eight original rows labelled A to H in a row, an arrow down to a dashed box labelled sample for one tree with a question mark.
Schematic with typed row names.
8. What is bagging?Define
Eight training rows labelled A to H with arrows to three boxes Model 1, Model 2 and Model 3, each arrow marked with a question mark, and an arrow from the models to a box labelled one answer.
Schematic with typed row names.
9. What is a random forest?Define
A table with four feature columns f1 to f4 and six rows r1 to r6, with arrows to three boxes tree 1, tree 2 and tree 3, each labelled trained on question mark.
Schematic with typed sizes.
10. What does Mean Decrease in Impurity report for a feature?Define
A tree on 20 samples. The root splits on feature A with 20 samples, its left child splits on feature B with 11 samples and its right child splits on feature A with 9 samples. The four leaves show class counts 9|0, 0|2, 0|6 and 1|2. Two labels ask for the importance of A and of B.
Counts typed; the tree is a toy, not trained.
11. How is permutation importance computed for a feature?Define
A table of six evaluation rows with income, age and churned columns, and a label asking for the importance of income as a question mark.
Typed toy rows, not a real dataset.
12. What role does variance reduction play in a regression tree?Define
A scatter of ten points of a numeric target against one feature, a dashed vertical candidate split between the fifth and sixth point, and a horizontal line at the mean of the points on each side.
Typed toy values.
13. In random forests, how many features are typically considered at each split, according to the lesson's defaults?Define
Sixteen feature chips f1 to f16 in two rows, with two labels asking how many features are examined at a split in a classification forest and in a regression forest.
Typed example with 16 features.
14. Why does the information gain formula weight each child's impurity by its share of the samples?Explain
A parent with six cats and four dogs split into a left child with five cats and three dogs and a right child with one cat and one dog, above a template gain = Gini(parent) - ( ? * Gini(left) + ? * Gini(right) ).
Typed counts.
15. Why does the greedy splitting algorithm not guarantee the globally optimal tree?Explain
A square scatter plot of forty points in four clusters at the corners of a unit square. Two diagonally opposite clusters are one class and the other two are the second class.
Synthetic points generated with numpy (seed 7).
16. Why does a tree left to grow until every leaf is pure generalize poorly?Explain
A scatter of 60 training points in two overlapping classes, drawn as blue and orange dots.
Synthetic points generated with numpy (seed 5); about one in ten labels is flipped.
17. In bagging, roughly what fraction of the original training samples end up in a single bootstrap sample, and what happens to the rest?Explain
A grid of twenty training rows with an arrow to a dashed box for one tree's sample, labelled with the fraction of rows seen as a question mark, and a second label asking about the rows not in the sample.
Typed example size.
18. Why does a random forest use both bootstrap sampling AND random feature subsets at each split?Explain
A four-step flow: training data, rows resampled for each tree, columns limited at each split, and many trees with results averaged.
Schematic of the lesson's forest recipe.
19. MDI (Mean Decrease in Impurity) feature importance is biased toward which type of feature?Explain
A table of eight rows with three feature columns, member as 0 or 1, age as a number and a zip code, and a line asking for the MDI importance of each column as question marks.
Typed toy rows.
20. A node contains 6 cats and 4 dogs. Using the lesson's Gini formula, what is its impurity?Apply
Ten dots in two rows, six blue cats and four orange dogs, with a label Gini equals question mark.
Typed counts from the question.
21. A node contains 6 cats and 4 dogs. Using the lesson's entropy formula, what is its approximate entropy?Apply
A bar split into six cats in blue and four dogs in orange, with a label Entropy equals question mark in bits.
Typed counts from the question.
22. A parent node holds 6 cats and 4 dogs (Gini 0.48). A split sends 5 cats left and 1 cat plus 4 dogs right. Using Gini, what is the information gain?Apply
A parent of six cats and four dogs with Gini 0.48 split into a left child of five cats and a right child of one cat and four dogs, with a label asking for the information gain.
Counts and parent Gini from the question.
23. A feature column holds the values 1, 2, 2, 4 at a node. Which thresholds does the lesson's split search try for that feature?Apply
A number line from 0 to 5 with four dots: one at 1, two stacked at 2 and one at 4, and a label asking which thresholds the split search tries.
Typed values from the question.
24. Your random forest ranks a sequential customer ID column as its single most important feature by MDI, even though IDs carry no real signal. What's the most likely explanation?Analyze
A horizontal bar chart of MDI importances, customer_id longest at about 0.50, then tenure, plan and region.
Real run of the numpy forest (40 trees, 280 rows) on synthetic data where customer_id is just 0 to 399.
25. A fully grown tree scores 100% on training data but much lower on validation data. Which change targets the cause?Analyze
Two bars for a fully grown tree: 100 percent accuracy on training data and 58 percent on validation data.
Real run of the numpy tree on synthetic noisy data (200 validation points).
26. A forest's trees all use bootstrap samples, yet they look almost identical and the forest barely beats one tree. One feature dominates the data. Which ingredient is missing?Analyze
Four small tree tops from one forest, each showing a root question and two child questions. The root question uses feature f1 in every tree, and the child questions test f2 or f3.
Four of 30 trees from a real run of the numpy forest on synthetic data (bootstrap only).
27. A pre-pruned tree stops at a node because the best split's gain is below the minimum-gain threshold, yet splits two levels deeper would have helped a lot. What does this show?Analyze
A scatter of forty points in four clusters at the corners of a square and a code box showing min_gain 0.05, the best split found of gain 0.036, and a note that the node stops splitting.
Synthetic points (numpy); min_gain = 0.05 is an illustrative setting.
28. MDI ranks one column first, but permutation importance ranks it near the bottom. The column has thousands of unique values. Which ranking do you trust?Analyze
Two ranked bar charts of the same forest. In the MDI chart user_code is first. In the permutation chart user_code sits at the bottom with a drop of 0.000.
Numpy forest on synthetic data; the real column here has 400 values, the question says thousands.
29. A teammate proposes a random forest to classify photos, since trees win on tabular data. What is the best response?Design
An eight by eight grid of grey pixels showing a bright ring, with notes that each pixel is a brightness value and the proposal is a random forest on these pixel values.
Illustrative 8 by 8 image, drawn in the script.
30. You must report feature importances from a forest to a stakeholder, and the data has a column of unique customer codes. Which plan fits?Design
Five column chips: customer_code highlighted, then age, plan, tenure and region. A note says customer_code is unique per customer, a box says the report is feature importances from the forest, and a line asks for the plan.
Typed column names.

Build It

Step 1: Gini impurity and entropy

Build both split criteria from scratch, then check that they agree on which splits look good.

python
import math
 
def gini_impurity(labels):
    n = len(labels)
    if n == 0:
        return 0.0
    counts = {}
    for label in labels:
        counts[label] = counts.get(label, 0) + 1
    return 1.0 - sum((c / n) ** 2 for c in counts.values())
 
def entropy(labels):
    n = len(labels)
    if n == 0:
        return 0.0
    counts = {}
    for label in labels:
        counts[label] = counts.get(label, 0) + 1
    return -sum(
        (c / n) * math.log2(c / n) for c in counts.values() if c > 0
    )

Step 2: Find the best split

Sweep every feature and every threshold, and return whichever pair maximizes information gain.

python
def information_gain(parent_labels, left_labels, right_labels, criterion="gini"):
    measure = gini_impurity if criterion == "gini" else entropy
    n = len(parent_labels)
    n_left = len(left_labels)
    n_right = len(right_labels)
    if n_left == 0 or n_right == 0:
        return 0.0
    parent_impurity = measure(parent_labels)
    child_impurity = (
        (n_left / n) * measure(left_labels) +
        (n_right / n) * measure(right_labels)
    )
    return parent_impurity - child_impurity

Step 3: Build the DecisionTree class

Recursive splitting, prediction, and feature-importance tracking, all in one class.

python
class DecisionTree:
    def __init__(self, max_depth=None, min_samples_split=2,
                 min_samples_leaf=1, criterion="gini",
                 max_features=None):
        self.max_depth = max_depth
        self.min_samples_split = min_samples_split
        self.min_samples_leaf = min_samples_leaf
        self.criterion = criterion
        self.max_features = max_features
        self.tree = None
        self.feature_importances_ = None
 
    def fit(self, X, y):
        self.n_features = len(X[0])
        self.feature_importances_ = [0.0] * self.n_features
        self.n_samples = len(X)
        self.tree = self._build(X, y, depth=0)
        total = sum(self.feature_importances_)
        if total > 0:
            self.feature_importances_ = [
                fi / total for fi in self.feature_importances_
            ]
 
    def predict(self, X):
        return [self._predict_one(x, self.tree) for x in X]

Step 4: Build the RandomForest class

Bootstrap sampling, feature randomization, and majority-vote prediction.

python
class RandomForest:
    def __init__(self, n_trees=100, max_depth=None,
                 min_samples_split=2, max_features="sqrt",
                 criterion="gini"):
        self.n_trees = n_trees
        self.max_depth = max_depth
        self.min_samples_split = min_samples_split
        self.max_features = max_features
        self.criterion = criterion
        self.trees = []
 
    def fit(self, X, y):
        n = len(X)
        for _ in range(self.n_trees):
            indices = [random.randint(0, n - 1) for _ in range(n)]
            X_boot = [X[i] for i in indices]
            y_boot = [y[i] for i in indices]
            tree = DecisionTree(
                max_depth=self.max_depth,
                min_samples_split=self.min_samples_split,
                max_features=self.max_features,
                criterion=self.criterion,
            )
            tree.fit(X_boot, y_boot)
            self.trees.append(tree)
 
    def predict(self, X):
        all_preds = [tree.predict(X) for tree in self.trees]
        predictions = []
        for i in range(len(X)):
            votes = {}
            for preds in all_preds:
                v = preds[i]
                votes[v] = votes.get(v, 0) + 1
            predictions.append(max(votes, key=votes.get))
        return predictions

See code/trees.py for the full implementation, with every helper method included.

Check: Build It0/4
1. In Step 2, information_gain is called with an empty left_labels list. What does it return?Apply
The first lines of the information_gain function from the lesson, with the rest of the body hidden, and below it a call with the left_labels list empty whose return value is a question mark.
Function lines copied from the lesson; the call arguments are typed.
2. RandomForest.fit runs on 100 training rows. What does each tree's X_boot contain?Apply
Lines of RandomForest.fit from the lesson with n equal to 100, the line that builds indices hidden, then X_boot and y_boot built from indices, and a label asking for the size of X_boot.
Code lines copied from the lesson; n = 100 is from the question.
3. Three trees in a RandomForest predict 'ham', 'spam', 'spam' for one sample. What does predict return for it?Apply
Three boxes tree 1, tree 2 and tree 3 predicting ham, spam and spam, with arrows into a box forest.predict(sample) whose result is a question mark.
Typed votes from the question.
4. After DecisionTree.fit, the raw per-feature importance totals are [2.0, 6.0, 0.0, 2.0]. What is feature_importances_?Apply
Four bars for features 0 to 3 with raw totals 2.0, 6.0, 0.0 and 2.0, and a label asking for feature_importances_.
Values from the question.

Use It

With scikit-learn, training a random forest takes three lines:

python
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
 
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
 
rf = RandomForestClassifier(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
print(f"Accuracy: {rf.score(X_test, y_test):.4f}")
print(f"Feature importances: {rf.feature_importances_}")

In practice, gradient boosted trees (XGBoost, LightGBM, CatBoost) usually beat random forests, since each new tree gets built to correct the previous ones' errors. Random forests still hold their own: they're harder to misconfigure, and they need almost no hyperparameter tuning.

Check: Use It0/1
1. According to the Use It section, what is one practical advantage of random forests over gradient boosted trees like XGBoost?Explain
Two boxes side by side, Random forest and Gradient boosted trees (XGBoost, LightGBM, CatBoost), above a line asking for a practical advantage of the random forest as a question mark.
Names taken from the lesson.

Ship It

This lesson produces outputs/prompt-tree-interpreter.md, a prompt that interprets decision tree splits for business stakeholders. Feed it a trained tree's structure (depth, features, split thresholds, accuracy), and it translates the model into plain-language rules, ranks feature importance, flags overfitting or leakage, and recommends next steps. Use it any time you need to explain a tree-based model to someone who doesn't read code.

Author's Note

The MDI bias isn't a footnote, it's the thing that actually burns people in production. I've watched an ID-like column, something with thousands of unique values and zero real signal, come out on top of an MDI importance ranking simply because it had endless places to split. If you take one habit from this lesson into a real project, make it this: never hand a stakeholder an MDI ranking without running permutation importance next to it. When the two agree, you're fine. When they don't, MDI is almost always the one that's wrong, and a high-cardinality column is almost always the culprit.

The other habit worth building early: reach for a random forest before you reach for a gradient boosted tree, even knowing GBTs usually win on raw accuracy. A forest with default settings rarely blows up on you. A GBT with default settings can, depending on the dataset and how much tuning patience you have. Get the boring model working first, then decide whether the accuracy gap is worth the extra tuning.

Exercises

  1. Train a single decision tree on a 2D dataset with 3 classes. Manually trace the splits and draw the rectangular decision boundaries. Compare the boundaries at max_depth=2 vs max_depth=10.

  2. Implement variance reduction splitting for regression trees. Generate y = sin(x) + noise for 200 points and fit your regression tree. Plot the tree's piecewise-constant predictions against the true curve.

  3. Build a random forest with 1, 5, 10, 50, and 200 trees. Plot training accuracy and test accuracy vs number of trees. Observe that test accuracy plateaus but does not decrease (forests resist overfitting).

  4. Compare Gini impurity vs entropy as split criteria on 5 different datasets. Measure accuracy and tree depth. In most cases, they produce nearly identical results. Explain why.

  5. Implement permutation importance. Compare it with MDI importance on a dataset where one feature is random noise but has high cardinality. MDI will rank the noise feature highly. Permutation importance will not.

Key Terms

TermWhat people sayWhat it actually means
Decision tree"A flowchart for predictions"A model that partitions feature space into rectangular regions by learning a sequence of if/else splits
Gini impurity"How mixed the node is"Probability of misclassifying a random sample at a node. 0 = pure, 0.5 = maximum impurity for binary
Entropy"The disorder in a node"Information content at a node. 0 = pure, 1.0 = maximum uncertainty for binary. From information theory
Information gain"How good a split is"Reduction in impurity after a split. The greedy criterion for choosing splits
Pre-pruning"Stop the tree early"Stopping tree growth early by setting max depth, min samples, or min gain thresholds
Post-pruning"Trim the tree after"Growing the full tree, then removing subtrees that do not improve validation performance
Bagging"Train on random subsets"Bootstrap aggregating. Train each model on a different random sample with replacement
Random forest"A bunch of trees"Ensemble of decision trees, each trained on a bootstrap sample with random feature subsets at each split
Feature importance (MDI)"Which features matter"Total impurity decrease contributed by each feature, summed across all trees and nodes
Permutation importance"Shuffle and check"Accuracy drop when a feature's values are randomly shuffled. More reliable than MDI for noisy features
Variance reduction"The regression version of info gain"The regression tree analogue of information gain. Picks the split that reduces target variance the most
Bootstrap sample"Random sample with repeats"A random sample drawn with replacement from the original dataset. Same size, but with duplicates

Further Reading

Code

Ships With