广义线性混合模型交叉验证报data[[cat_col]]各ID内取值需恒定如何解决
问题描述
尝试使用groupdata2和cvms包对广义线性混合模型执行5折交叉验证,运行代码如下:
data <- groupdata2::fold(detect, k = 5, cat_col = 'outcome', id_col = 'bird') %>% arrange(.folds) cvms::cross_validate( data, "outcome ~ sex + year + season + (1 | bird) + (1 | obsname)", family="binomial", fold_cols = ".folds", control = NULL, REML = FALSE)
运行后收到报错:
Error in groupdata2::fold(detect, k = 4, cat_col = "outcome", id_col = "bird") %>% : 1 assertions failed: * The value in 'data[[cat_col]]' must be constant within each ID.
包文档给出的解释为:要求同一个ID对应的分类标签取值恒定,避免同一ID被分到多个折叠,该逻辑适用于诊断结果固定的参与者类场景。但当前数据为鸟类重观测数据,outcome是每次调查的观测结果,同一个鸟类ID会出现不同的outcome取值,属于合理场景,需要绕过该限制。
可复现示例代码如下:
bird <- c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3) outcome <- c(0,1,1,1,0,0,0,1,0,1,0,1,0,0,1) df <- data.frame(bird, outcome) df$outcome <- as.factor(df$outcome) df$bird <- as.factor(df$bird) data <- groupdata2::fold(df, k = 5, cat_col = 'outcome', id_col = 'bird') %>% arrange(.folds)
解决方案
- 方案1:删除
cat_col参数(仅保留按ID分组折叠)groupdata2::fold要求cat_col取值同ID恒定的前提是同时指定了cat_col和id_col,如果不需要严格保证每个折叠的outcome类别比例平衡,仅需要保证同一个鸟类ID的所有观测不被拆分到不同折叠,直接删除cat_col参数即可。
注意:k的取值不能超过数据中独立鸟类ID的数量,示例数据中仅3个鸟类ID,k最多取3:
# 示例运行代码 data <- groupdata2::fold(df, k = 3, id_col = 'bird') %>% arrange(.folds)
- 方案2:自定义分层折叠(兼顾ID不拆分+outcome比例平衡)
如果需要同时满足「同一ID不拆分」和「各折叠outcome比例接近总体」的要求,可以先按鸟类ID分组,统计每个ID的outcome分布,再手动将ID分配到不同折叠,保证各折叠的总outcome比例接近,之后为每个ID对应的所有行统一打上折叠标签即可,生成的.folds列可以直接传入cvms使用。 - 方案3:换用rsample包实现分组交叉验证
rsample包的group_vfold_cv()函数原生支持按分组变量拆分数据,不会要求同一个分组的标签恒定,更适合当前场景,拆分后生成的折叠可以直接转换为cvms要求的格式使用:
library(rsample) library(dplyr) # 按bird分组做3折交叉验证 splits <- group_vfold_cv(df, group = bird, v = 3) # 转换为带.folds列的数据集 df_with_folds <- df %>% mutate(.folds = NA_integer_) for (i in 1:nrow(splits)) { test_ids <- assessment(splits$splits[[i]])$bird %>% unique() df_with_folds$.folds[df_with_folds$bird %in% test_ids] <- i }
注意事项
- 含有随机效应的混合模型交叉验证必须保证同一个分组ID(此处为bird)的所有观测完全属于训练集或完全属于测试集,避免数据泄露导致模型性能高估。
- 交叉验证的折数k最大不能超过独立分组ID的总数,否则无法实现完整分组拆分。
内容的提问来源于stack exchange,提问作者user17047272
相关产品推荐
相关产品推荐

