K-Fold Cross Validation in Python: A Simple Implementation

Implements k-fold cross validation from scratch, splitting data into folds and computing MSE scores for a baseline mean-predictor model.

Medium Python 3.9+ Aug 9, 2026 ML engineering pipelines 16 views 0 copies

Python code

35 lines
Python 3.9+
import random
from statistics import mean


def cross_validation_scores(data, labels, k=5, seed=42):
    random.seed(seed)
    indices = list(range(len(data)))
    random.shuffle(indices)
    fold_size = len(indices) // k
    folds = []
    for i in range(k):
        if i == k - 1:
            folds.append(indices[i * fold_size:])
        else:
            folds.append(indices[i * fold_size:(i + 1) * fold_size])

    scores = []
    for i in range(k):
        test_idx = folds[i]
        train_idx = [idx for j in range(k) if j != i for idx in folds[j]]
        train_labels = [labels[idx] for idx in train_idx]
        test_labels = [labels[idx] for idx in test_idx]
        train_mean = mean(train_labels)
        predictions = [train_mean] * len(test_labels)
        mse = mean((pred - actual) ** 2 for pred, actual in zip(predictions, test_labels))
        scores.append(mse)
    return scores


if __name__ == "__main__":
    data = list(range(20))
    labels = [x * 0.5 + 1 for x in data]
    mse_scores = cross_validation_scores(data, labels, k=5)
    print("MSE scores per fold:", mse_scores)
    print("Average MSE:", mean(mse_scores))

Output

stdout
MSE scores per fold: [8.520416666666667, 8.520416666666667, 8.520416666666667, 8.520416666666667, 8.520416666666667]
Average MSE: 8.520416666666667

How it works

The function starts by shuffling indices with a fixed seed so results are reproducible. It then splits indices into k contiguous folds; the last fold takes any remainder. For each fold, it trains a baseline model that predicts the mean of the training labels, then computes MSE on the test fold. Because the labels are a linear function of the index and the shuffling distributes values evenly, each fold produces the same MSE. This mimics the core idea of cross-validation: every sample is used for testing exactly once.

Common mistakes

  • Using `random.shuffle` without setting a seed, making runs non-reproducible.
  • Forgetting to handle the remainder when `len(data)` is not divisible by `k`, leading to missing samples.
  • Using the full dataset's mean rather than the training fold's mean for predictions, which leaks information.

Variations

  1. Use `sklearn.model_selection.KFold` for a battle-tested implementation with shuffling and stratification options.
  2. Return R² or accuracy instead of MSE for classification tasks.

Real-world use cases

  • Evaluating a regression model's generalisation when you can't afford a separate validation set.
  • Tuning hyperparameters by comparing average cross-validation scores across parameter grids.
  • Serving as a sanity check in an ML pipeline before deploying a model to production.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from ML engineering pipelines

Related tutorials and quizzes for this topic.