有没有办法在 R 中使用 tryCatch(或类似的)作为循环,或者在警告参数中操纵 expr?
Is there a way to use tryCatch (or similar) in R as a loop, or to manipulate the expr in the warning argument?
我有一个回归模型(lm
或 glm
或 lmer
...)并且我做 fitmodel <- lm(inputs)
其中 inputs
在循环内发生变化(公式和数据)。然后,如果模型函数没有产生任何警告,我想保留 fitmodel
,但如果我收到警告,我想 update
模型并且我想要警告 not 打印出来,所以我在 tryCatch
里面做了 fitmodel <- lm(inputs)
。因此,如果它产生警告,在 warning = function(w){f(fitmodel)}
、f(fitmodel)
内将类似于
fitmodel <- update(fitmodel, something suitable to do on the model)
事实上,这个赋值将在 if-else
结构中,根据警告 if(w$message satisfies something)
我会在 update
中调整 suitable to do on the model
。
问题是我得到 Error in ... object 'fitmodel' not found
。如果我将 withCallingHandlers
与 invokeRestarts
一起使用,它只会在没有 update
警告的情况下完成模型的计算。如果我在 something suitable to do on the model
中再次添加 fitmodel <- lm(inputs)
,我会打印警告;现在我想我可以尝试 suppresswarnings(fitmodel <- lm(inputs))
,但我认为这不是一个优雅的解决方案,因为我必须将行 fitmodel <- lm(inputs)
添加 2 倍,使所有计算成为 2 倍(在 [=36= 内) ] 和内部 warning
).
总结一下,我想要但失败的是:
tryCatch(expr = {fitmodel <- lm(inputs)},
warning = function(w) {if (w$message satisfies something) {
fitmodel <- update(fitmodel, something suitable to do on the model)
} else if (w$message satisfies something2){
fitmodel <- update(fitmodel, something2 suitable to do on the model)
}
}
)
我能做什么?
问题的循环部分是因为我认为它如下(也许是另一个问题,但目前我把它留在这里):可能会在 update
之后收到另一个警告,所以我会做类似 while(get a warning on update){update}
的事情;在某种程度上,warning
中的 update
也应该理解为 expr
。这样的事情可能吗?
非常感谢!
带有最少示例的问题的通用版本:
假设我有一个 tryCatch(expr = {result <- operations}, warning = function(w){f(...)}
,如果我在 expr
中收到警告(实际上是在 operations
中生成的),我想用 result
做一些事情,所以我会做 warning = function(w){f(result)}
,但后来我得到 Error in ... object 'result' not found
.
一个最小的例子:
y <- "a"
tryCatch(expr = {x <- as.numeric(y)},
warning = function(w) {print(x)})
Error in ... object 'x' not found
我尝试使用 withCallingHandlers
而不是 tryCatch
但没有成功,并且还使用 invokeRestart
但它执行表达式部分,而不是我收到警告时想要执行的操作。
你能帮帮我吗?
谢谢!
也许你可以在处理条件中再次分配x
?
tryCatch(
warning = function(cnd) {
x <- suppressWarnings(as.numeric(y))
print(x)},
expr = {x <- as.numeric(y)}
)
#> [1] NA
也许不是最优雅的答案,但可以解决您的玩具示例。
不要把赋值放在tryCatch
调用里面,放在外面。例如,
y <- "a"
x <- tryCatch(expr = {as.numeric(y)},
warning = function(w) {y})
这会将 y
分配给 x
,但您可以在警告正文中放入任何内容,结果将分配给 x
。
您的“我想要的”示例更复杂,因为您想要访问 expr
值,但在生成警告时尚未在任何地方分配它。我认为你必须重新计算它:
fitmodel <- tryCatch(expr = {lm(inputs)},
warning = function(w) {if (w$message satisfies something) {
update(lm(inputs), something suitable to do on the model)
} else if (w$message satisfies something2){
update(lm(inputs), something2 suitable to do on the model)
}
}
)
编辑添加:
要让评估在处理警告之前继续完成,您不能使用 tryCatch
。 evaluate
包有一个函数(也称为 evaluate
)可以做到这一点。例如,
y <- "a"
res <- evaluate::evaluate(quote(x <- as.numeric(y)))
for (i in seq_along(res)) {
if (inherits(res[[i]], "warning") &&
conditionMessage(res[[i]]) == gettext("NAs introduced by coercion",
domain = "R"))
x <- y
}
一些注意事项:res
列表将包含许多不同的内容,包括消息、警告、错误等。我的代码只查看警告。我使用 conditionMessage
来提取警告消息,但是
它将被翻译成当地语言,因此您应该使用 gettext
翻译英文版本的消息以进行比较。
您是否在寻找类似以下的内容?如果是 运行 和 y <- "123"
,将打印 "OK"
消息。
y <- "a"
#y <- "123"
x <- tryCatch(as.numeric(y),
warning = function(w) w
)
if(inherits(x, "warning")){
message(x$message)
} else{
message(paste("OK:", x))
}
将上面的代码重写为函数,可以更轻松地测试多个参数值。
testWarning <- function(x){
out <- tryCatch(as.numeric(x),
warning = function(w) w
)
if(inherits(out, "warning")){
message(out$message)
} else{
message(paste("OK:", out))
}
invisible(out)
}
testWarning("a")
#NAs introduced by coercion
testWarning("123")
#OK: 123
您似乎正在寻找一个函数包装器,它可以捕获函数调用的返回值和副作用。我认为 purrr::quietly
是此类任务的完美人选。考虑这样的事情
quietly <- purrr::quietly
foo <- function(x) {
if (x < 3)
warning(x, " is less than 3")
if (x < 4)
warning(x, " is less than 4")
x
}
update_foo <- function(x, y) {
x <- x + y
foo(x)
}
keep_doing <- function(inputs) {
out <- quietly(foo)(inputs)
repeat {
if (length(out$warnings) < 1L)
return(out$result)
cat(paste0(out$warnings, collapse = ", "), "\n")
# This is for you to see the process. You can delete this line.
if (grepl("less than 3", out$warnings[[1L]])) {
out <- quietly(update_foo)(out$result, 1.5)
} else if (grepl("less than 4", out$warnings[[1L]])) {
out <- quietly(update_foo)(out$result, 1)
}
}
}
输出
> keep_doing(1)
1 is less than 3, 1 is less than 4
2.5 is less than 3, 2.5 is less than 4
[1] 4
> keep_doing(3)
3 is less than 4
[1] 4
从根本上说,问题是在分配发生之前调用了处理程序。即使不是这种情况,处理程序在与 tryCatch
表达式不同的范围内运行,因此处理程序无法访问其他范围内的名称。
我们需要将处理与值转换分开。
对于错误(但不是警告),基础 R 提供了函数 try
,它包装了 tryCatch
以实现此效果。但是,不鼓励使用 try
,因为它的 return 类型是 unsound.1 As mentioned , ‘purrr’ provides soundly typed functional wrappers(例如 safely
)以达到类似的效果。
但是,我们也可以构建自己的,这可能更适合这种情况:
with_warning = function (expr) {
self = environment()
warning = NULL
result = withCallingHandlers(expr, warning = function (w) {
self$warning = w
tryInvokeRestart('muffleWarning')
})
list(result = result, warning = warning)
}
这为我们提供了一个区分结果值和警告的包装器。我们现在可以使用它来实现您的要求:
fitmodel = with(with_warning(lm(inputs)), {
if (! is.null(warning)) {
if (conditionMessage(warning) satisfies something) {
update(result, something suitable to do on the model)
} else {
update(result, something2 suitable to do on the model)
}
} else {
result
}
})
1 这意味着 try
的 return 类型不区分类型 [ 的错误值和非错误值=18=]。这是可能发生的真实情况,例如,嵌套多个 try
调用时。
我有一个回归模型(lm
或 glm
或 lmer
...)并且我做 fitmodel <- lm(inputs)
其中 inputs
在循环内发生变化(公式和数据)。然后,如果模型函数没有产生任何警告,我想保留 fitmodel
,但如果我收到警告,我想 update
模型并且我想要警告 not 打印出来,所以我在 tryCatch
里面做了 fitmodel <- lm(inputs)
。因此,如果它产生警告,在 warning = function(w){f(fitmodel)}
、f(fitmodel)
内将类似于
fitmodel <- update(fitmodel, something suitable to do on the model)
事实上,这个赋值将在 if-else
结构中,根据警告 if(w$message satisfies something)
我会在 update
中调整 suitable to do on the model
。
问题是我得到 Error in ... object 'fitmodel' not found
。如果我将 withCallingHandlers
与 invokeRestarts
一起使用,它只会在没有 update
警告的情况下完成模型的计算。如果我在 something suitable to do on the model
中再次添加 fitmodel <- lm(inputs)
,我会打印警告;现在我想我可以尝试 suppresswarnings(fitmodel <- lm(inputs))
,但我认为这不是一个优雅的解决方案,因为我必须将行 fitmodel <- lm(inputs)
添加 2 倍,使所有计算成为 2 倍(在 [=36= 内) ] 和内部 warning
).
总结一下,我想要但失败的是:
tryCatch(expr = {fitmodel <- lm(inputs)},
warning = function(w) {if (w$message satisfies something) {
fitmodel <- update(fitmodel, something suitable to do on the model)
} else if (w$message satisfies something2){
fitmodel <- update(fitmodel, something2 suitable to do on the model)
}
}
)
我能做什么?
问题的循环部分是因为我认为它如下(也许是另一个问题,但目前我把它留在这里):可能会在 update
之后收到另一个警告,所以我会做类似 while(get a warning on update){update}
的事情;在某种程度上,warning
中的 update
也应该理解为 expr
。这样的事情可能吗?
非常感谢!
带有最少示例的问题的通用版本:
假设我有一个 tryCatch(expr = {result <- operations}, warning = function(w){f(...)}
,如果我在 expr
中收到警告(实际上是在 operations
中生成的),我想用 result
做一些事情,所以我会做 warning = function(w){f(result)}
,但后来我得到 Error in ... object 'result' not found
.
一个最小的例子:
y <- "a"
tryCatch(expr = {x <- as.numeric(y)},
warning = function(w) {print(x)})
Error in ... object 'x' not found
我尝试使用 withCallingHandlers
而不是 tryCatch
但没有成功,并且还使用 invokeRestart
但它执行表达式部分,而不是我收到警告时想要执行的操作。
你能帮帮我吗?
谢谢!
也许你可以在处理条件中再次分配x
?
tryCatch(
warning = function(cnd) {
x <- suppressWarnings(as.numeric(y))
print(x)},
expr = {x <- as.numeric(y)}
)
#> [1] NA
也许不是最优雅的答案,但可以解决您的玩具示例。
不要把赋值放在tryCatch
调用里面,放在外面。例如,
y <- "a"
x <- tryCatch(expr = {as.numeric(y)},
warning = function(w) {y})
这会将 y
分配给 x
,但您可以在警告正文中放入任何内容,结果将分配给 x
。
您的“我想要的”示例更复杂,因为您想要访问 expr
值,但在生成警告时尚未在任何地方分配它。我认为你必须重新计算它:
fitmodel <- tryCatch(expr = {lm(inputs)},
warning = function(w) {if (w$message satisfies something) {
update(lm(inputs), something suitable to do on the model)
} else if (w$message satisfies something2){
update(lm(inputs), something2 suitable to do on the model)
}
}
)
编辑添加:
要让评估在处理警告之前继续完成,您不能使用 tryCatch
。 evaluate
包有一个函数(也称为 evaluate
)可以做到这一点。例如,
y <- "a"
res <- evaluate::evaluate(quote(x <- as.numeric(y)))
for (i in seq_along(res)) {
if (inherits(res[[i]], "warning") &&
conditionMessage(res[[i]]) == gettext("NAs introduced by coercion",
domain = "R"))
x <- y
}
一些注意事项:res
列表将包含许多不同的内容,包括消息、警告、错误等。我的代码只查看警告。我使用 conditionMessage
来提取警告消息,但是
它将被翻译成当地语言,因此您应该使用 gettext
翻译英文版本的消息以进行比较。
您是否在寻找类似以下的内容?如果是 运行 和 y <- "123"
,将打印 "OK"
消息。
y <- "a"
#y <- "123"
x <- tryCatch(as.numeric(y),
warning = function(w) w
)
if(inherits(x, "warning")){
message(x$message)
} else{
message(paste("OK:", x))
}
将上面的代码重写为函数,可以更轻松地测试多个参数值。
testWarning <- function(x){
out <- tryCatch(as.numeric(x),
warning = function(w) w
)
if(inherits(out, "warning")){
message(out$message)
} else{
message(paste("OK:", out))
}
invisible(out)
}
testWarning("a")
#NAs introduced by coercion
testWarning("123")
#OK: 123
您似乎正在寻找一个函数包装器,它可以捕获函数调用的返回值和副作用。我认为 purrr::quietly
是此类任务的完美人选。考虑这样的事情
quietly <- purrr::quietly
foo <- function(x) {
if (x < 3)
warning(x, " is less than 3")
if (x < 4)
warning(x, " is less than 4")
x
}
update_foo <- function(x, y) {
x <- x + y
foo(x)
}
keep_doing <- function(inputs) {
out <- quietly(foo)(inputs)
repeat {
if (length(out$warnings) < 1L)
return(out$result)
cat(paste0(out$warnings, collapse = ", "), "\n")
# This is for you to see the process. You can delete this line.
if (grepl("less than 3", out$warnings[[1L]])) {
out <- quietly(update_foo)(out$result, 1.5)
} else if (grepl("less than 4", out$warnings[[1L]])) {
out <- quietly(update_foo)(out$result, 1)
}
}
}
输出
> keep_doing(1)
1 is less than 3, 1 is less than 4
2.5 is less than 3, 2.5 is less than 4
[1] 4
> keep_doing(3)
3 is less than 4
[1] 4
从根本上说,问题是在分配发生之前调用了处理程序。即使不是这种情况,处理程序在与 tryCatch
表达式不同的范围内运行,因此处理程序无法访问其他范围内的名称。
我们需要将处理与值转换分开。
对于错误(但不是警告),基础 R 提供了函数 try
,它包装了 tryCatch
以实现此效果。但是,不鼓励使用 try
,因为它的 return 类型是 unsound.1 As mentioned safely
)以达到类似的效果。
但是,我们也可以构建自己的,这可能更适合这种情况:
with_warning = function (expr) {
self = environment()
warning = NULL
result = withCallingHandlers(expr, warning = function (w) {
self$warning = w
tryInvokeRestart('muffleWarning')
})
list(result = result, warning = warning)
}
这为我们提供了一个区分结果值和警告的包装器。我们现在可以使用它来实现您的要求:
fitmodel = with(with_warning(lm(inputs)), {
if (! is.null(warning)) {
if (conditionMessage(warning) satisfies something) {
update(result, something suitable to do on the model)
} else {
update(result, something2 suitable to do on the model)
}
} else {
result
}
})
1 这意味着 try
的 return 类型不区分类型 [ 的错误值和非错误值=18=]。这是可能发生的真实情况,例如,嵌套多个 try
调用时。