HardData Processing

K-Fold Split (Indices Only)

Data Processing

Hard

Problem

Partition sample indices from zero through N-1 into k folds for cross-validation. When N is not divisible by k, the first N\bmod k validation folds receive one extra index.

\{0,1,\ldots,N-1\}=F_1\cup F_2\cup\cdots\cup F_k

F_i\cap F_j=\varnothing\quad\text{for }i\ne j

For fold i, validation indices are F_i and training indices are all remaining folds. If shuffle is true, shuffle indices with np.random.default_rng(seed).permutation before partitioning. Return a list of $$ dictionaries, each containing train_idx and val_idx as one-dimensional integer NumPy arrays.

Theory

K-Fold Cross-Validation is a resampling technique used to evaluate machine learning models on limited data. Instead of using a single train-test split, the data is divided into k equal parts (folds), and the model is trained and evaluated k times, each time using a different fold as the test set and the remaining folds as training data.


Why Use K-Fold Cross-Validation?

Better use of data: In a single train-test split, a portion of data is never used for training. K-Fold uses all data for both training and testing across different iterations.

More reliable estimates: A single split can be lucky or unlucky depending on which samples end up in test set. K-Fold averages over multiple splits for more stable performance estimates.

Detecting overfitting: If training scores are high but cross-validation scores are low, the model is overfitting.

Model selection: Compare different models or hyperparameters using cross-validation scores rather than a single test set.


The K-Fold Procedure

Given a dataset with N samples and chosen k value:

Step 1 - Partition data into k folds:

\text{fold\_size} = \lfloor N / k \rfloor

Each fold contains approximately N/k samples. If N is not divisible by k, some folds will have one extra sample.

Step 2 - Iterate k times:

Step 3 - Aggregate results:

\text{CV\_score} = \frac{1}{k} \sum_{i=1}^{k} \text{score}_i

The final cross-validation score is the average across all folds.


Choosing the Number of Folds

Common choices:

Trade-offs:

Small k (e.g., k=2 or k=3):

Large k (e.g., k=10 or k=20):


Worked Example

Dataset: 10 samples with indices [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

k=5 folds (fold_size = 10/5 = 2):

Fold 0: samples [0, 1] Fold 1: samples [2, 3] Fold 2: samples [4, 5] Fold 3: samples [6, 7] Fold 4: samples [8, 9]

Iteration 1:

Iteration 2:

Iteration 3:

Iteration 4:

Iteration 5:

Final CV Score:

\text{CV\_score} = \frac{0.85 + 0.82 + 0.88 + 0.79 + 0.86}{5} = 0.84

Standard deviation of fold scores provides uncertainty estimate: std = 0.034


Handling Uneven Splits

When N is not divisible by k, the remainder samples must be distributed:

Example: N=23 samples, k=5

\text{base\_size} = \lfloor 23/5 \rfloor = 4

\text{remainder} = 23 \mod 5 = 3

Distribution: First 3 folds get 5 samples, last 2 folds get 4 samples

Total: 5 + 5 + 5 + 4 + 4 = 23 samples


Shuffling Before Splitting

Why shuffle? If data is ordered (e.g., sorted by class label or time), consecutive samples may be similar. Without shuffling, folds might contain biased subsets.

Implementation consideration: Shuffle indices before assigning to folds, not the data itself. This preserves original data order while ensuring random fold assignment.

Reproducibility: Set a random seed before shuffling to ensure the same fold assignments across runs.


Important Considerations

Data leakage: Any preprocessing that uses information from the full dataset (e.g., scaling, feature selection) must be performed inside each fold to avoid leakage. The test fold should never influence training.

Computational cost: Training k models takes k times longer than a single train-test split. For expensive models, k=5 may be preferred over k=10.

Variance of estimates: Report both mean CV score and standard deviation across folds. High variance suggests the model is sensitive to the specific training data.

Nested cross-validation: When tuning hyperparameters, use an inner CV loop for hyperparameter selection and outer CV loop for unbiased performance estimation.


Leave-One-Out Cross-Validation (LOOCV)

Special case where k=N:

Advantages:

Disadvantages:


Where K-Fold Cross-Validation Shows Up

Examples

Example 1

Input
N = 5, k = 2, shuffle = false, seed = 0
Output
[{"train_idx": [3, 4], "val_idx": [0, 1, 2]}, {"train_idx": [0, 1, 2], "val_idx": [3, 4]}]
Explanation
Five indices produce validation folds of sizes three and two, and each fold is held out once.

Example 2

Input
N = 7, k = 3, shuffle = false, seed = 0
Output
[{"train_idx": [3, 4, 5, 6], "val_idx": [0, 1, 2]}, {"train_idx": [0, 1, 2, 5, 6], "val_idx": [3, 4]}, {"train_idx": [0, 1, 2, 3, 4], "val_idx": [5, 6]}]

Hints

  1. Use np.array_split(indices, k) to create balanced validation folds.
  2. Concatenate every fold except the current validation fold for train_idx.

Requirements

Constraints

Starter Code

import numpy as np

def kfold_split(N: int, k: int, shuffle: bool = True, seed: int = 0) -> list:
    """
    Returns a list of dictionaries with train_idx and val_idx.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Basic 2-foldpublic
3-fold unevenpublic