从 R 中的 lme4 包捕获收敛消息

capturing convergence message from lme4 package in R

我想知道是否有一种方法可以编写逻辑测试 (TRUE/FALSE) 来显示来自 lme4 包的模型是否已经收敛?

下面是一个示例,我想捕获是否有任何模型带有收敛警告(即Model failed to converge)消息?

library(lme4)

dat <- read.csv('https://raw.githubusercontent.com/rnorouzian/e/master/nc.csv')

m <- lmer(math ~ ses*sector + (ses | sch.id), data = dat)

Warning message:
In checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv,  :
  Model failed to converge with max|grad| = 0.00279 (tol = 0.002, component 1)

我们可以使用 tryCatch,使用 withCallingHandlersthis post 获得灵感。

dat <- read.csv('https://raw.githubusercontent.com/rnorouzian/e/master/nc.csv')

m <- tryCatch({
          withCallingHandlers({
            error <- FALSE
            list(model = lmer(math ~ ses*sector + (ses | sch.id), data = dat),
                 error = error)
          },warning = function(w) {
              if(grepl('failed to converge', w$message)) error <<- TRUE
          }
          )})


m$model
#Linear mixed model fit by REML ['lmerMod']
#Formula: math ~ ses * sector + (ses | sch.id)
#   Data: dat
#REML criterion at convergence: 37509.07
#Random effects:
# Groups   Name        Std.Dev. Corr
# sch.id   (Intercept) 1.9053       
#          ses         0.8577   0.46
# Residual             3.1930       
#Number of obs: 7185, groups:  sch.id, 160
#Fixed Effects:
#(Intercept)          ses       sector   ses:sector  
#     11.902        2.399        1.677       -1.322  
#convergence code 0; 0 optimizer warnings; 1 lme4 warnings 

m$error
#[1] TRUE

输出 m 是一个包含 modelerror 元素的列表。


如果我们需要在创建模型后测试警告,我们可以使用:

is_warning_generated <- function(m) {
  df <- summary(m)
  !is.null(df$optinfo$conv$lme4$messages) && 
           grepl('failed to converge', df$optinfo$conv$lme4$messages)
}

m <- lmer(math ~ ses*sector + (ses | sch.id), data = dat)
is_warning_generated(m)
#[1] TRUE

我们可以使用 purrr 中的 safely。它还将 return error 作为 list 元素并捕获错误。如果没有报错,就是NULL

library(purrr)
safelmer <- safely(lmer, otherwise = NA)
out <- safelmer(math ~ ses*sector + (ses | sch.id), data = dat)

我将Ronak的解决方案应用到我自己的模拟数据中,发现了一个问题。 该消息可能是多个条目的向量,导致 grepl() 也有多个条目。但是,&& 运算符仅将字符串与第一个条目进行比较,这样 'failed to converge' 的进一步出现就不会被观察到。为了避免这种行为,我将 && 更改为 &

现在,如果根本没有消息,就会出现问题。在这种情况下,!is.null() 部分变为正确的 FALSE(即没有生成警告),但 grepl() 部分变为 logical(0),函数值变为 FALSE & logical(0),这是 logical(0)。事实上,它适用于 FALSE && logical(0),即 FALSE(正确)。

一个对我有用的解决方案是 if(is.null(mess)) FALSE else grepl('failed to converge', mess)

在收敛失败的情况下,在放置警告的条目处提供一个值为 TRUE 的向量。例如,可以通过构建大于 0 或 TRUE 的数字(或布尔)总和来评估该向量。

我只想说@RonakShah 的 is_warning_generated 可以稍微紧凑一些:

function(m) { 
    w <- m@optinfo$conv$lme4$messages
    !is.null(w) && grepl('failed to converge', w) 
}
> sm=summary(model)
> sm$optinfo$conv$lme4$messages
[1] "Model failed to converge with max|grad| = 0.0120186 (tol = 0.002, component 1)"
>