DataLens

Features, labels, training vs test data

Organise data into a feature matrix X and a label vector y, encode categories, split into training and test sets, and avoid leaking the answer into the features.

Level: Foundations · 8 min read

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

The idea

Every supervised ML problem is set up the same way:

The model learns a function from X to y. Then comes the crucial question: does it work on examples it has never seen? To answer honestly, you hide some examples during training and test on them afterwards.

X and y

This track's main example is customer churn: predicting which customers are about to stop buying, so the shop can offer them something before they go. Each row of churn.csv is one customer. A few columns describe their behaviour, and one column, churned, records whether they actually left. In machine learning terms, the descriptive columns are the features (X) and the answer you want to predict is the label (y):

import pandas as pd

churn = pd.read_csv("churn.csv")
features = ["tenure_months", "orders_90d", "avg_order_value", "days_since_last_order", "support_tickets", "app_user"]
X = churn[features]
y = churn["churned"]
print(X.shape, y.shape)
print(X.head(3))
(400, 6) (400,)
   tenure_months  orders_90d  avg_order_value  days_since_last_order  support_tickets  app_user
0             12           7           163000                     47                1         0
1             33           4           472000                     73                1         0
2              5           0           124000                     14                0         0

Categorical features

Models do maths, so text categories like segment ("Consumer", "Business") must become numbers. The standard way is one-hot encoding: one 0/1 column per category.

encoded = pd.get_dummies(churn[["segment"]], dtype=int)
print(encoded.head(3))
   segment_Business  segment_Consumer
0                 0                 1
1                 1                 0
2                 1                 0

Why not just Consumer = 0, Business = 1? With two categories that's fine. With three (Jakarta = 0, Bandung = 1, Surabaya = 2), numbers would imply an order and a distance ("Surabaya is twice Bandung"), which is nonsense. One-hot avoids inventing relationships that don't exist. The preprocessing module covers this with scikit-learn tools.

Why a test set?

Imagine a teacher who gives students the exact exam questions to practise. Every student scores 100%, and the score says nothing about whether they learned. A model scored on the data it trained on has the same problem: it might have memorised the examples.

So you split the data:

Here's a split by hand with NumPy, so you see there's no magic:

import numpy as np

rng = np.random.default_rng(42)
order = rng.permutation(len(churn))
cut = int(len(churn) * 0.75)
train_idx, test_idx = order[:cut], order[cut:]

X_train, X_test = X.iloc[train_idx], X.iloc[test_idx]
y_train, y_test = y.iloc[train_idx], y.iloc[test_idx]
print(len(X_train), len(X_test))
print(round(y_train.mean(), 3), round(y_test.mean(), 3))
300 100
0.327 0.36

Shuffling first matters: if the file were sorted (all churners at the end), an unshuffled split would put them all in the test set.

train_test_split

Shuffling and cutting the data by hand works, but it's easy to get wrong, and you'll do it in almost every project. scikit-learn does the whole thing in one call:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)
print(X_train.shape, X_test.shape)
print(round(y_train.mean(), 3), round(y_test.mean(), 3))
(300, 6) (100, 6)
0.337 0.33

The order of the four results is always X_train, X_test, y_train, y_test.

Leakage: when the answer sneaks into the features

Data leakage happens when a feature contains information you wouldn't have at prediction time, often a disguised version of the answer. The model looks brilliant in testing and fails in real use.

Examples at a shop:

Key idea

For every feature, ask: "at the moment I'd make this prediction, would I already know this value?" If not, it leaks. And keep the test set untouched until the very end.

A suspiciously perfect score (99% where experts expected 75%) is the classic symptom.

Why this matters for clean code

A clear X/y setup, with an explicit list of feature names, documents exactly what the model sees. Keep it in one place (FEATURES = [...]) so training and prediction use the same columns in the same order. Seeds make splits reproducible, and an untouched test set makes the final score honest, which is what stakeholders will make decisions on.

Recap

Practise this module

The questions for this lesson run in your browser and check your answer straight away.

Next: The idea of a model and a loss function →