Asked at Cubist
Theory: The Learning ProblemRead 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.
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.