Python Decision Trees and K-Nearest Neighbors

Train decision trees and k-nearest neighbours, tune depth and neighbour count on validation data, scale features for KNN and compare the two models.

Lesson 95 of 107Advanced13 min read

Decision trees and k-nearest neighbours (KNN) are two intuitive classification and regression models. A decision tree asks a sequence of yes/no questions about the features, like a flowchart. KNN finds the training examples most similar to a new one and lets them vote. Both are available in scikit-learn:

Python
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
X = [[1], [2], [3], [8], [9], [10]]          # item weight (kg)
y = ["paper", "paper", "paper", "metal", "metal", "metal"]
tree = DecisionTreeClassifier(max_depth=2).fit(X, y)
knn = KNeighborsClassifier(n_neighbors=3).fit(X, y)

This lesson explains how each model makes predictions, the hyperparameters that control them, why KNN needs scaled features, and how they differ in interpretability and cost. The examples sort items at a recycling centre. It assumes you know training and testing and logistic regression.

Tree splits

A decision tree splits the data with conditions such as weight <= 5.5, choosing at each step the split that best separates the classes (by default using Gini impurity). Each branch is split again until a stopping rule applies, and each final leaf predicts the majority class of the training examples that reached it.

Python
from sklearn.tree import DecisionTreeClassifier, export_text
tree = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X, y)
print(export_text(tree, feature_names=["weight_kg"]))

To predict, a new item follows the conditions from the root down to a leaf. Trees handle features on any scale without preprocessing and capture interactions, such as "heavy and magnetic", automatically.

Left unrestricted, a tree keeps splitting until every leaf is pure, which often means memorising the training data, noise included. Limit growth with max_depth, min_samples_leaf or max_leaf_nodes. Shallow trees generalise better and are easier to read.

KNN neighborhoods

KNN stores the training data. To predict, it measures the distance from the new example to every stored one, takes the k nearest, and returns their majority class (or average value, for regression):

Python
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=3).fit(X, y)
print(knn.kneighbors([[7]]))     # distances and indexes of the 3 nearest items

The default distance is Euclidean, so feature scales matter. With weight in kilograms (0 to 10) and volume in cubic centimetres (0 to 5,000), volume differences swamp weight differences and the model effectively ignores weight. Standardise features first, inside a pipeline: make_pipeline(StandardScaler(), KNeighborsClassifier()).

The choice of k sets the trade-off: k = 1 follows every quirk of the training data, while a large k smooths over real boundaries. weights="distance" lets closer neighbours count more. KNN also struggles with many features, because in high dimensions distances become less informative.

Hyperparameters

Settings chosen before training, such as max_depth for a tree or n_neighbors for KNN, are hyperparameters. Choose them by comparing candidates on validation data or with cross-validation, never on the final test set:

Python
from sklearn.model_selection import GridSearchCV
search = GridSearchCV(KNeighborsClassifier(), {"n_neighbors": [1, 3, 5, 7]}, cv=5)
search.fit(X_train, y_train)
print(search.best_params_)

GridSearchCV runs cross-validation inside the training data for every candidate and refits the best one. The test set is used once, at the end, to report the chosen model's performance.

If you try ten depths and pick the one with the best test score, the test set has become part of model selection, and its score is optimistic: some of that "best" is luck. This is a subtle form of leakage that's easy to commit when experimenting in a notebook.

Interpretation and costs

Decision treeKNN
How it predictsfollows learned rulescompares with stored examples
Training costbuilds the tree (fast)just stores the data
Prediction costone path down the tree (fast)distances to all stored examples (slower as data grows)
Memorythe treethe whole training set
Needs scalingnoyes
Explanationreadable rules (export_text)"these k similar items"

A shallow tree can be printed and checked by a person, which is valuable when decisions must be justified. KNN's explanations are example-based: "classified as metal because these three similar items were metal".

Readable doesn't mean fair or correct. A tree trained on biased historical decisions reproduces that bias in clear rules, and KNN reproduces whatever patterns its stored examples contain. Both models are only as good as the data they learn from. Ensembles of many trees, random forests and gradient boosting, usually predict better than one tree, at the cost of simple readability.

Independent practice and a worked check

Task: compare a shallow tree and scaled KNN with the same held-out material records and training validation protocol.

Python
from sklearn.model_selection import GridSearchCV, train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier

X = [[0.2, 900], [0.3, 1500], [0.25, 3000], [2.0, 1000], [2.5, 1600], [2.2, 2900],
     [0.4, 1200], [1.8, 1300], [0.35, 2500], [2.4, 2200], [0.15, 700], [2.1, 800]]
y = ["paper"] * 3 + ["metal"] * 3 + ["paper", "metal", "paper", "metal", "paper", "metal"]
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.25, random_state=1, stratify=y)

candidates = {
    "tree": GridSearchCV(DecisionTreeClassifier(random_state=0), {"max_depth": [1, 2, 3]}, cv=3),
    "knn": GridSearchCV(make_pipeline(StandardScaler(), KNeighborsClassifier()),
                        {"kneighborsclassifier__n_neighbors": [1, 3]}, cv=3),
}
for name, search in candidates.items():
    search.fit(X_tr, y_tr)
    print(name, search.best_params_, "test accuracy", search.score(X_te, y_te))

Both models are tuned only on the training split and scored once on the same test rows. For the worked check, mixed KNN features are scaled because distance depends on their magnitudes.

Reference and next practice

scikit-learn's guides to decision trees and nearest neighbors explain the algorithms and their parameters. Continue with training and testing datasets and cross-validation. Practise with the Python exercises and Python quiz.

Decision Trees and K-Nearest Neighbors Summary

  • Tree splits: A decision tree partitions feature space through successive conditions.
  • KNN neighborhoods: K nearest neighbors predicts using nearby training observations according to a distance rule.
  • Hyperparameters: Tree depth and neighbor count are choices selected using training side validation.
  • Interpretation and costs: Trees expose decision conditions while KNN retains reference examples for prediction.

Decision Trees and K-Nearest Neighbors Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Decision Trees and K-Nearest Neighbors in a small program

  • Both agree here because the classes are cleanly separated by weight.
Python
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
X = [[1], [2], [3], [8], [9], [10]]
y = ["paper", "paper", "paper", "metal", "metal", "metal"]
for model in [DecisionTreeClassifier(max_depth=2, random_state=4), KNeighborsClassifier(n_neighbors=3)]:
    model.fit(X, y)
    print(model.predict([[2], [9]]).tolist())
Output
['paper', 'metal']
['paper', 'metal']
Line-by-line explanation
  1. Lines 3 to 4Light items are paper and heavy items are metal.
  2. Lines 5 to 7Both models are trained on the same data and asked about a 2 kg and a 9 kg item.
Example 2

Why KNN needs scaling

Python
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier

# features: weight (kg), volume (cm³); label: material
X = [[0.2, 1400], [0.3, 1500], [0.25, 1550], [2.0, 600], [2.5, 3000], [2.2, 4000]]
y = ["paper", "paper", "paper", "metal", "metal", "metal"]
new_item = [[2.3, 1480]]          # heavy, so very likely metal

raw_knn = KNeighborsClassifier(n_neighbors=3).fit(X, y)
scaled_knn = make_pipeline(StandardScaler(), KNeighborsClassifier(n_neighbors=3)).fit(X, y)
tree = DecisionTreeClassifier(max_depth=2, random_state=0).fit(X, y)

print("KNN on raw features:   ", raw_knn.predict(new_item)[0])
print("KNN on scaled features:", scaled_knn.predict(new_item)[0])
print("decision tree:         ", tree.predict(new_item)[0])
Output
KNN on raw features:    paper
KNN on scaled features: metal
decision tree:          metal
Line-by-line explanation
  1. Line 11Without scaling, volume (hundreds to thousands) dominates the distance, so the three paper items with similar volumes are chosen as neighbours and the heavy item is misclassified.
  2. Line 12Standardising puts weight and volume on comparable scales before distances are computed.
  3. Line 13The tree splits on single features, so their scales don't matter.

Common Decision Trees and K-Nearest Neighbors Mistakes

Choosing hyperparameters with the test set

Don't
for depth in range(1, 11):
    score = DecisionTreeClassifier(max_depth=depth).fit(X_train, y_train).score(X_test, y_test)
    # pick the depth with the best test score -> optimistic estimate
Do
search = GridSearchCV(DecisionTreeClassifier(), {"max_depth": range(1, 11)}, cv=5)
search.fit(X_train, y_train)
print(search.best_params_, search.score(X_test, y_test))   # test used once

Using KNN on unscaled mixed-unit features

Don't
KNeighborsClassifier().fit(X, y)                     # cm³ outweighs kg
Do
make_pipeline(StandardScaler(), KNeighborsClassifier()).fit(X, y)

Decision Trees and K-Nearest Neighbors in Real Projects

Trees and neighbours in practice

Decision trees appear in credit decisions, medical triage guidelines and customer-support routing, where a short, auditable rule set matters. Random forests and gradient-boosted trees (XGBoost, LightGBM, scikit-learn's HistGradientBoostingClassifier) are among the strongest models for tabular business data. KNN's idea powers recommendation ("customers similar to you bought...") and similarity search over embeddings, usually with specialised nearest-neighbour indexes for speed.

Both models are quick to try and make good baselines alongside logistic regression before investing in more complex approaches.

Decision Trees and K-Nearest Neighbors FAQ

What is the difference between a decision tree and KNN?

A decision tree learns explicit rules that split the feature space and predicts by following them. KNN stores the training examples and predicts from the k most similar ones. Trees are fast to predict and don't need scaling; KNN is simple to train but slower to predict and requires scaled features.

How do I choose k in k-nearest neighbors?

Try several values, often odd numbers such as 1, 3, 5, 7 and 9 for binary classification to avoid ties, and pick the best by cross-validation on the training data. Small k fits noise; large k blurs genuine boundaries.

Why is my decision tree overfitting?

An unrestricted tree grows until each leaf contains a single class, memorising noise. Limit max_depth, raise min_samples_leaf, or use a random forest, and compare training and validation scores to see the effect.

Do decision trees need feature scaling?

No. Each split compares one feature with a threshold, so rescaling a feature doesn't change which split is best. Scaling is needed for distance-based models such as KNN and for regularised linear models.

Can KNN be used for regression?

Yes. KNeighborsRegressor predicts the average target of the k nearest neighbours, optionally weighted by distance. The same scaling and k-selection advice applies.

Decision Trees and K-Nearest Neighbors Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

Why scale mixed KNN features?

Multiple choicemedium

Which review concern is valid for decision trees and k-nearest neighbors?

Decision Trees and K-Nearest Neighbors Quiz

2 questions to check what you learned.

  1. Why scale mixed KNN features?

    • To guarantee perfect accuracy
    • Because distance depends on their magnitudes
    • Python ignores the distinction
  2. Which statement correctly describes hyperparameters?

    • Tree depth and neighbor count are choices selected using training side validation.
    • Only the example output matters, not the rule
    • Every operation has the same behavior