EduFlow

Section 1 · What Machine Learning Is

The Workflow and the Train/Test Split

18 min3 min readPython exercise

Lesson at a glance

Turn the churn table into a feature matrix X and a target y, then split it with train_test_split so you can measure a model on customers it has never seen.

  1. 1The workflow, start to finish
  2. 2Features and target
  3. 3Holding out a test set
  4. 4What the split buys you

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

A model that scores 99% is either very good or has seen the answers. You can’t tell which from the number alone. The single most useful habit in machine learning is setting aside data the model never sees during training, so that its score on that data means something.

The workflow, start to finish

Every supervised project you’ll work on follows roughly the same loop. The steps are boring on purpose; skipping one is how projects fail.

Seven steps in a loop: frame the question, collect labelled data, split, baseline, train, evaluate, then ship and monitor, with an arrow from evaluate back to train for iteration. Frame Collect Split Baseline Train Evaluate Ship iterate
The supervised learning workflow

Frame the question precisely: what is predicted, for whom, at what moment, and what action follows. For Cartwheel: “On 30 September, for every customer who has ordered before, predict whether they will order in the next 90 days, so marketing can target win-back offers.” Collect examples where the answer is known: that’s churn.csv. Split off a test set. Build a baseline (next lesson but one). Train models, evaluate them, iterate, and only then ship and keep watching.

Features and target

scikit-learn expects two things: a feature matrix X, one row per example and one column per feature, and a target y with one answer per row. A pandas DataFrame works as X and a Series works as y.

For now you’ll use the eight numeric columns. Text columns such as country need encoding first (section 4 handles that), and avg_satisfaction has gaps that some models can’t accept yet.

Python
import pandas as pd

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X = churn[features]
y = churn["churned"]
print(X.shape, y.shape)
print(y.value_counts())

customer_id stays out. An id is an arbitrary label, and a flexible model could still find splits on it that fit the training rows by coincidence. Anything that wouldn’t make sense to a person explaining churn shouldn’t be a feature.

Holding out a test set

train_test_split shuffles the rows and cuts them into two parts. You train on one and measure on the other.

Python
import pandas as pd
from sklearn.model_selection import train_test_split

churn = pd.read_csv("churn.csv")
features = ["tenure_days", "orders", "total_spent", "avg_order_value",
            "days_since_last_order", "used_coupon", "support_tickets", "mobile_share"]
X, y = churn[features], churn["churned"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42, stratify=y
)
print("train:", X_train.shape, "churn rate", round(y_train.mean(), 3))
print("test: ", X_test.shape, "churn rate", round(y_test.mean(), 3))

Three arguments do the work:

  • test_size=0.25 keeps a quarter of the rows back: 100 customers for testing, 298 for training. With a few hundred rows, 20–30% is a sensible test share; with millions, 1–5% is plenty.
  • random_state=42 fixes the shuffle so you get the same split every run. Without it, every rerun reshuffles and your score wobbles for reasons that have nothing to do with your model.
  • stratify=y keeps the churn rate the same in both parts (55%). With 100 test rows, an unlucky shuffle could otherwise give you a test set that’s 45% or 62% churned, and every number you compute on it would shift.

The order of the four returned values is fixed: X_train, X_test, y_train, y_test. Mixing them up is a common source of nonsense scores.

What the split buys you

Think of the split as a contract with your future self. Everything you do from here on (choosing features, choosing models, tuning settings) happens on the training part. The test part stays sealed until you’re ready to report a result. With X_train and y_train you can fit any model you like. With X_test and y_test you get one honest answer to “how well does this work on customers it hasn’t seen?”. That’s the number that predicts what happens when marketing uses your model in December. Next lesson you’ll fit the first model and see how big the gap between those two numbers can be.

Key takeaways

  • Every supervised project follows the same loop: frame, collect labelled data, split, baseline, train, evaluate, then ship and monitor.
  • X is a table of features with one row per example; y is the matching column of answers.
  • A model’s score on its own training data says how well it memorised, not how well it predicts.
  • Use stratify=y for classification so the train and test sets keep the same class balance, and set random_state so the split is repeatable.
  • The test set is for the final check; looking at it while you make decisions turns it into training data.

Further reading

Check your understanding

4 questions

Question 1 of 4

You fit a model on all 398 churn rows and it scores 0.99 on those same rows. What can you conclude?

Question 2 of 4

What does stratify=y change in train_test_split(X, y, test_size=0.25, stratify=y)?

Question 3 of 4

Why drop customer_id from the features?

Question 4 of 4

You try twenty model settings, keep the one with the best test score, and report that score. What is wrong?

Practice

Python exercise

Build X and y, then split

Prepare the churn data for modelling.

  1. X: the eight numeric feature columns: everything except customer_id, country, segment, avg_satisfaction and the target.
  2. y: the churned column.
  3. Split into X_train, X_test, y_train, y_test with a 25% test set, random_state=42, and the churn rate kept equal in both parts.

Data files: churn.csv

import pandas as pd
from sklearn.model_selection import train_test_split

churn = pd.read_csv("churn.csv")

# 1. Feature matrix: the eight numeric feature columns (no id, no target)
X = churn.drop(columns=["country", "segment", "avg_satisfaction"])
# 2. Target
y = churn["churned"]

# 3. Split: 25% test, random_state=42, keep the churn rate equal in both parts
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

(no output)

Checks

  • X holds exactly the eight numeric features
  • y is the churned column (398 values of 0 and 1)
  • 298 training rows and 100 test rows
  • Train and test keep the overall churn rate