R에서 tryCatch (또는 유사)를 루프로 사용하거나 경고 인수에서 expr을 조작하는 방법이 있습니까?

Nov 27 2020

회귀 모델 ( lm또는 glm또는 lmer...)이 있고 루프 (공식 및 데이터) 내 fitmodel <- lm(inputs)에서 inputs변경 사항을 수행 합니다. 그런 다음 모델 함수가 경고를 생성하지 않으면 유지 fitmodel하고 싶지만 경고가 표시 update되면 모델에 경고 인쇄 되지 않기를 원 하므로 fitmodel <- lm(inputs)내부에서 수행 합니다 tryCatch. 이 경고를 생성한다면, 내부 warning = function(w){f(fitmodel)}, f(fitmodel)같은 것

fitmodel <- update(fitmodel, something suitable to do on the model)

사실,이 할당은 if-else경고에 따라 내부를 if(w$message satisfies something)조정 하는 방식으로 구조 내부에 있을 것 입니다.suitable to do on the modelupdate

문제는 내가 얻는 것 Error in ... object 'fitmodel' not found입니다. withCallingHandlers와 함께 사용 invokeRestarts하면 경고없이 모델 계산이 완료 update됩니다. fitmodel <- lm(inputs)안쪽 에 다시 추가 something suitable to do on the model하면 경고가 인쇄됩니다. 이제 시도해 볼 수 있다고 생각 suppresswarnings(fitmodel <- lm(inputs))하지만 선 fitmodel <- lm(inputs)을 2 배 더하여 모든 계산 (내부 expr및 내부 warning) 을 2 배로 만들어야 하기 때문에 우아한 해결책이 아니라고 생각합니다 .

요약하면 내가 원하지만 실패한 것은 다음과 같습니다.

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}. 어떤 의미에서이 update내부 warningexpr. 이와 같은 것이 가능합니까?

대단히 감사합니다!


최소한의 예가있는 일반적인 질문 버전 :

하자 내가이 말을 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했지만 경고를 받았을 때하고 싶은 것이 아니라 표현 부분을합니다.

당신이 나를 도울 수?

감사합니다!

답변

2 KonradRudolph Nov 28 2020 at 14:24

근본적으로 문제는 할당이 발생하기 전에 핸들러가 호출된다는 것입니다. 그렇지 않은 경우에도 핸들러는 tryCatch표현식과 다른 범위에서 실행 되므로 핸들러는 다른 범위의 이름에 액세스 할 수 없습니다.

값 변환과 처리를 분리해야합니다.

오류 (경고 아님)의 경우 base R 은이 효과를 달성하기 위해 try래핑 하는 함수를 제공합니다 tryCatch. 그러나 try반환 유형이 불건전 하기 때문에 사용 하지 않는 것이 좋습니다 . 1 ekoam의 답변에서 언급했듯이 'purrr'은 유사한 효과를 얻기 위해 건전한 형식의 기능적 래퍼 (예 :)를 제공 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의 반환 유형이 유형의 오류와 오류가 아닌 값을 구별하지 않는다는 것 try-error입니다. 예를 들어 여러 try호출을 중첩 할 때 발생할 수있는 실제 상황입니다 .

2 ekoam Nov 27 2020 at 11:37

함수 호출의 반환 값과 부작용을 모두 캡처하는 기능적 래퍼를 찾고있는 것 같습니다. 나는 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
1 RuiBarradas Nov 27 2020 at 10:38

다음과 같은 것을 찾고 있습니까? 로 실행 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
JoaoPedroMacalos Nov 27 2020 at 10:04

x취급 조건에서 다시 할당 할 수 있을까요?

tryCatch(
  warning = function(cnd) {
    x <- suppressWarnings(as.numeric(y))
    print(x)},
  expr = {x <- as.numeric(y)}
)
#> [1] NA

아마도 가장 우아한 대답은 아니지만 장난감 예제를 해결합니다.

user2554330 Nov 27 2020 at 10:05

과제를 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로 번역되므로 비교를 위해 영어 버전의 메시지를 번역하는 데 사용해야 합니다.