Coding
Machine Learning

K-Fold and Forward Splits

Difficulty

Read the problem, hints and solution here. The editor needs a bigger screen: open this page on a laptop to write and run your code.

Cross-validation estimates how a model does on data it has not seen. You split the rows into \( k \) folds, fit on all the folds but one, score on the fold you left out, and repeat for each fold. On time-ordered data, how you build the folds decides whether the estimate means anything.

Shuffled folds put rows from the future into the training set of nearly every fold. Contiguous folds without shuffling are better, but standard k-fold still trains on the blocks after the test block, so the model can learn from what happens later. A forward split (also called walk-forward validation) trains only on the past, which is how the model is used in production. Overlapping labels need more care: a label at time \( t \) can depend on prices up to \( t + h \). Purged cross-validation removes those rows near each test block, and the problem Purged Cross-Validation Splitter builds it. This problem builds the two splits that it starts from.

Implement kfold_splits(n, k, mode).

  • n: the number of rows, indexed 0 to n - 1 in time order.
  • k: the number of folds.
  • mode: 'standard' or 'forward'.

Never shuffle. Split the indices into \( k \) contiguous blocks in order. Each block has n // k indices, and the first n % k blocks take one extra (the scikit-learn KFold convention).

  • 'standard'. Return one split per block, in block order. test is the block. train is every other index, in increasing order. There are \( k \) splits.
  • 'forward'. For each block after the first, test is the block and train is every index strictly before the block. The first block has no past, so it has no split. There are \( k - 1 \) splits.

Return a list of tuples (train, test), where train and test are lists of ints in increasing order.

Raise ValueError if k < 2, if k > n, or if mode is not one of the two names above. A hidden test calls your function with bad arguments and checks that the exception is a ValueError.

Examples

kfold_splits(7, 3, 'standard')
# [([3, 4, 5, 6], [0, 1, 2]), ([0, 1, 2, 5, 6], [3, 4]), ([0, 1, 2, 3, 4], [5, 6])]

Seven rows in three folds: \( 7 = 3 + 2 + 2 \), so the first block takes the extra row. The middle split trains on rows from both sides of its test block.

kfold_splits(7, 3, 'forward')
# [([0, 1, 2], [3, 4]), ([0, 1, 2, 3, 4], [5, 6])]

The same blocks. Block 0 has no past and is skipped. Each later block trains only on the rows before it, so the training set grows.

Constraints

  • \( 1 \le n \le 200{,}000 \).
  • One hidden test has \( n = 200{,}000 \) and \( k = 10 \). Building each train list by searching the test list is far too slow at that size.
Rate this problem
Language: Python 3.12kfold_splits
Rate this problem