通常,我想在包含一些因子变量的数据集上运行交叉验证,并且在运行一段时间后,交叉验证例程失败并出现错误:factor x has new levels Y
.
例如,使用包启动:
library(boot)
d <- data.frame(x=c('A', 'A', 'B', 'B', 'C', 'C'), y=c(1, 2, 3, 4, 5, 6))
m <- glm(y ~ x, data=d)
m.cv <- cv.glm(d, m, K=2) # Sometimes succeeds
m.cv <- cv.glm(d, m, K=2)
# Error in model.frame.default(Terms, newdata, na.action = na.action, xlev = object$xlevels) :
# factor x has new levels B
更新:这是一个玩具示例。同样的问题也出现在较大的数据集上,其中出现了几次级别,但训练分区C
中都不存在。
包中的函数createDataPartition
函数对结果变量进行分层抽样并正确警告:caret
此外,对于“createDataPartition”,非常小的班级规模 (<= 3) 班级可能不会同时出现在训练和测试数据中。
有两种解决方案浮现在脑海中:
- 首先,通过首先选择一个随机样本来创建数据的子集
factor level
,从最稀有的类别(按频率)开始,然后贪婪地满足下一个稀有类别,依此类推。然后createDataPartition
在数据集的其余部分上使用并合并结果以创建一个新的火车数据集,其中包含所有levels
. - 使用
createDataPartitions
和做拒绝抽样。
到目前为止,由于数据大小的原因,选项2对我有用,但我不禁认为必须有比手动推出的解决方案更好的解决方案。
理想情况下,我想要一个仅适用于创建分区的解决方案,如果无法创建此类分区,则会提前失败。
软件包不提供此功能是否有根本的理论原因?他们是否提供它而我只是因为盲点而无法发现它们?有没有更好的方法来进行这种分层抽样?
如果我应该在stats.stackoverflow.com上提出这个问题,请发表评论。
更新:
这就是我手工推出的解决方案 (2) 的样子:
get.cv.idx <- function(train.data, folds, factor.cols = NA) {
if (is.na(factor.cols)) {
all.cols <- colnames(train.data)
factor.cols <- all.cols[laply(llply(train.data[1, ], class), function (x) 'factor' %in% x)]
}
n <- nrow(train.data)
test.n <- floor(1 / folds * n)
cond.met <- FALSE
n.tries <- 0
while (!cond.met) {
n.tries <- n.tries + 1
test.idx <- sample(nrow(train.data), test.n)
train.idx <- setdiff(1:nrow(train.data), test.idx)
cond.met <- TRUE
for(factor.col in factor.cols) {
train.levels <- train.data[ train.idx, factor.col ]
test.levels <- train.data[ test.idx , factor.col ]
if (length(unique(train.levels)) < length(unique(test.levels))) {
cat('Factor level: ', factor.col, ' violated constraint, retrying.\n')
cond.met <- FALSE
}
}
}
cat('Done in ', n.tries, ' trie(s).\n')
list( train.idx = train.idx
, test.idx = test.idx
)
}