caret combine the stratified createMultiFolds repeatedCV and groupKFold
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In caret, createMultiFolds() and groupKFold() solve different validation problems. The first gives repeated, roughly stratified folds based on the outcome, while the second prevents leakage by keeping all rows from the same group together.
Why these helpers do not combine automatically
createMultiFolds(y, k, times) works at the row level. It tries to preserve the outcome distribution in each training split. groupKFold(group, k) also works at the row level, but its main rule is stronger: all records that share a group must travel together.
When observations are clustered by patient, account, device, or site, group integrity usually matters more than perfect row-level stratification. If rows from the same group land in both training and validation sets, you get leakage and an overly optimistic score.
The practical consequence is that there is no built-in one-liner in caret that gives you repeated, grouped, and stratified folds at the same time. The usual solution is to build custom resampling indices and pass them into trainControl(index = ..., indexOut = ...).
Build grouped folds first
Start by deciding what each group represents. Then create folds on the group ids, not on the raw rows.
Each element in group_folds is a vector of training row indices. All rows from a patient stay together.
If grouping is your only requirement, you can already use those indices in trainControl.
Add repetition with custom indices
To imitate repeated cross-validation, generate grouped folds multiple times and store every training split in one list.
This creates repeated grouped splits, but it is not yet stratified. For many grouped problems, that is acceptable because leakage prevention is the priority.
Approximate stratification at the group level
If each group can be assigned a single class label, you can stratify groups rather than rows. That works well when each patient or account belongs to one target class.
Now the repetition and approximate stratification happen at the group table, and the row indices are expanded afterward. This is the safest way to combine the ideas when each group has one stable label.
If a single group contains mixed outcome classes, perfect stratification and strict grouping can conflict. In that case, keep grouping intact and accept only approximate balance, because leakage is the larger modeling error.
Use the indices in train()
Once you have the training indices, pass them directly to trainControl.
When index is supplied, caret uses your custom resampling plan instead of generating one from method = "repeatedcv".
Common Pitfalls
The biggest mistake is trying to use createMultiFolds() on row-level outcomes and groupKFold() on row-level groups independently, then assuming the results can be merged afterward. They describe different split geometries, so naïve merging usually breaks group integrity.
Another issue is demanding perfect stratification when groups have mixed labels internally. In that situation, there may be no mathematically clean split that satisfies both rules. Document the tradeoff and favor no leakage over ideal class balance.
It is also easy to forget that custom resampling indices replace the built-in repeated CV logic. If you pass index, you are responsible for creating the repeated structure yourself and naming or tracking the resamples clearly.
Finally, verify the split quality before training. Check that no group appears in both train and holdout sets for the same fold, and inspect class counts across folds so you understand how much imbalance remains.
Summary
- '
createMultiFolds()gives repeated stratified splits at the row level.' - '
groupKFold()preserves group boundaries to prevent leakage.' - '
caretdoes not provide a single helper that combines both automatically.' - Use custom
indexvalues intrainControl()to implement repeated grouped resampling. - If stratification matters, do it at the group level when each group has one stable label.

