Start learning

Train and Test Split

Lesson 4. About 8 minutes, with a video walkthrough.

A train and test split divides your data into two parts: one the model learns from, and one kept hidden so you can honestly measure how well the model works on data it has never seen.

Your Path Through This Lesson

0 of 5 done

By the End of This Lesson You Can
  • Explain why a model must be tested on data it has never seen.
  • Split data with train_test_split and choose its arguments.
  • Spot data leakage and avoid it.

Why You Can't Test on the Training Data

Imagine a teacher who gives students the exact exam questions, with answers, a week before the exam. Everyone scores 100%. Does that prove they understand the subject? Of course not. They might just have memorised the answers.

Models do the same thing. A flexible model can memorise its training data and score perfectly on it, then fail badly on new data. So we hold back some data the model never sees during training, and grade it only on that. That hidden part is the test set.

How the Split Works

  1. Shuffle the rows, so both parts get a fair mix. If your file is sorted by date or by class, an unshuffled split would put all the old rows in training and all the new ones in testing.
  2. Cut it into two parts. A common choice is 80% training, 20% testing. With a big dataset, 90/10 or even 99/1 is fine, because 1% of a million rows is still 10,000 test rows.
  3. Train only on the training part.
  4. Evaluate only on the test part.
The dataset is shuffled and split. The model learns from the training set and is scored on the test set.
The dataset is shuffled and split. The model learns from the training set and is scored on the test set.

In scikit-learn

train_test_split does the shuffle and the cut in one line.

python
import numpy as np
from sklearn.model_selection import train_test_split

X = np.arange(20).reshape(-1, 1)   # 20 rows, 1 feature
y = np.arange(20) * 2              # 20 answers

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

print(X_train.shape, X_test.shape)
print(y_train.shape, y_test.shape)
Output
(16, 1) (4, 1)
(16,) (4,)

16 rows for training and 4 for testing: exactly 80/20. Three arguments matter:

Argument What it does
test_size=0.2 Fraction of rows that go into the test set
random_state=42 Fixes the shuffle, so you get the same split every time you run the code
stratify=y For classification: keeps the same class proportions in both parts
Note

The order of the four returned values is always X_train, X_test, y_train, y_test. Swap two of them by mistake and your model trains on the wrong thing without any error message. It's worth reading that line twice.

Why random_state?

Without it, every run gives a different shuffle, so every run gives a slightly different score. That makes it impossible to tell whether a change you made actually helped. The number itself doesn't matter: 42, 0 or 7 all work. Just keep it fixed.

Why stratify?

Suppose 95 of your 100 emails are normal and 5 are spam. A random 20-row test set might, by bad luck, contain no spam at all. Then you can't measure spam detection. With stratify=y, both parts keep the 95/5 ratio.

python
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

Data Leakage: The Silent Bug

Data leakage means information from the test set sneaks into training. The model then looks better than it really is, and that only shows up when it fails in the real world.

The most common leak happens when you scale or fill in values before splitting:

python
# Wrong: the scaler sees the test rows, so test info leaks into training
X_scaled = scaler.fit_transform(X)
X_train, X_test = train_test_split(X_scaled, test_size=0.2)

# Right: split first, learn the scaling from training rows only
X_train, X_test = train_test_split(X, test_size=0.2, random_state=42)
X_train = scaler.fit_transform(X_train)   # fit on train
X_test = scaler.transform(X_test)         # only transform test
Watch Out

Rule of thumb: split first, then do everything else. Any step that learns something from data (scaling, filling missing values, choosing features) should learn it from the training set only.

What About a Validation Set?

When you try many models or settings, you choose the one with the best score. If you choose using the test set, the test set slowly stops being "unseen". So bigger projects split three ways:

  • Training set: fit the model.
  • Validation set: compare models and settings.
  • Test set: touched once, at the very end, for the final honest score.

Later, cross-validation gives you a smarter way to do the validation step without wasting data.

Try It Yourself

Run the code with test_size=0.25 and check the shapes. Then remove random_state, run it three times and print X_test each time. Do you get the same test rows?

The Key Idea

Split first, then do everything else. The test set stays locked away until the very end.

Summary

  • Never judge a model on the data it learned from.
  • Shuffle, then split, usually 80/20, with random_state so the split is repeatable.
  • Use stratify=y for classification with uneven classes.
  • Split first to avoid data leakage.
  • Next: your first real algorithm, simple linear regression.