广义线性混合模型交叉验证错误:'data[[cat_col]]'中的值必须在每个ID内保持不变

Error in generalized linear mixed model cross-validation: The value in 'data[[cat_col]]' must be constant within each 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.

在包小插图中,给出了以下解释:“参与者必须在整个数据集中始终具有相同的诊断(‘a’或‘b’)。否则,参与者可能会被置于多个折叠中。”这在示例中是有意义的。然而,我的数据是基于重新观察鸟类的结果,因此结果会根据特定调查中是否观察到鸟类而有所不同。有解决办法吗?

可重现的例子:

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)

完整文档说:

cat_col: Name of categorical variable to balance between folds. E.g. when predicting a binary variable (a or b), we usually want both classes represented in every fold. N.B. If also passing an ‘id_col’, ‘cat_col’ should be constant within each ID.

因此,在这种情况下,结果因个别鸟类而异 (id_col),您根本无法指定折数根据结果进行平衡。 (我不是 100% 理解软件中的这个限制:似乎应该可以通过选择具有平衡结果范围的组(鸟类)来至少进行近似平衡,但我可以看到它如何使平衡过程更难了)。

不过,在我看来,平衡结果的重要性总体上有些被高估了。缺乏平衡意味着 ?binomial_metrics 中一些较简单的指标(例如准确性、灵敏度、特异性)不是很有用,但其他指标(平衡准确性、AUC、aic)应该没问题。

一个可能更大的问题是您似乎具有(可能)交叉随机效应(即 (1|bird) + (1|obsname))。我猜 obsname 是一个观察者的名字:如果一些观察者检测到(或未能检测到)多只鸟,而一些鸟被多个观察者 detected/failed ,那么可能无法定义折叠实际上是独立的,或者至少它可能非常困难。

为此,您可以使用 groupdata2 v2.0.0 中的新 collapse_groups() 函数代替 fold()。它允许您采用现有组(例如 bird)并将它们折叠为更少的组(例如折叠),并尝试平衡多个分类列、数字列和因子列(唯一级别的数量 - 尽管相同的级别可能在多个组中)。

它没有 fold() 在改变结果方面所做的限制,但另一方面在“不变结果”上下文中没有相同的“保证”。例如。它不保证在所有折叠中至少有一个结果级别。

虽然你需要的鸟比折叠的数量更多,所以我在测试数据中添加了一些:


bird <- c(1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,
          4,4,4,5,5,5,5,5,6,6,6,6,6,7,7,7,7)
outcome <- c(0,1,1,1,0,0,0,1,0,1,0,1,0,0,1,0,1,
             0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1,1)
df <- data.frame(bird, outcome)
df$outcome <- as.factor(df$outcome)
df$bird <- as.factor(df$bird)

# Combine 'bird' groups to folds
data <- groupdata2::collapse_groups(
  data = df, 
  n = 3, 
  group_cols="bird", 
  cat_col="outcome", 
  col_name = ".folds"
) %>% 
  arrange(.folds)

# Check the balance of the relevant columns
groupdata2::summarize_balances(
  data=data, 
  group_cols=".folds", 
  cat_cols="outcome"
)$Groups

> # A tibble: 3 × 6
>   .group_col .group `# rows` `# bird` `# outc_0` `# outc_1`
>   <fct>      <fct>     <int>    <int>      <dbl>      <dbl>
> 1 .folds     1            14        3          7          7
> 2 .folds     2            10        2          6          4
> 3 .folds     3            10        2          4          6

summarize_balances() 向我们展示了我们创建了 3 个折叠,第一个折叠有 14 行,其他折叠有 10 行。第一个折叠有 3 个独特的鸟级别,其他有 2 个(通常只在组内唯一,但在这里我们知道鸟只在一个组中,因为这就是 collapse_groups() 与其 [=18 一起工作的方式=] 参数)。 结果变量(这里 # outc_0# outc_1)在某种程度上是平衡的。

对于较大的数据集,您可能想要 运行 多次折叠并从摘要中选择具有最佳平衡的那个。这可以通过将 num_new_group_cols = 10 添加到 collapse_groups() 来完成(为了获得更好的结果,启用 auto_tune 设置)然后在 运行ning [=16] 时列出所有创建的组列=].

希望这对您或处于类似情况的其他人有所帮助。 fold() 中的约束很难通过其当前的内部方法解决,但 collapse_groups 希望在这些情况下可以解决问题。

查看更多https://rdrr.io/cran/groupdata2/man/collapse_groups.html