Как использовать tryCatch, когда результат не отличается - PullRequest
0 голосов
/ 25 апреля 2018

У меня есть созданная функция, которая возвращает фрейм данных с двумя переменными.В качестве простого примера:

test <- function(x) {y <- matrix( 5 , nrow= x , ncol =  2)
                    z<- data.frame(y) 
                    return(z) }

Я хочу выяснить, при каких значениях x эта функция выдает ошибку.(в нашем примере я думаю об отрицательных значениях, но я просто хочу передать концепцию.) Поэтому я пытаюсь:

z <- rep(0)
testnumbers <- c(0,1,2,3,4,-1,5)

for (i in 1:length(testnumbers)) {

  tempo <- tryCatch( testfun(testnumbers[i]) , error= function(e) return(0) )

  if (tempo == 0 )  z[i] <- {testnumbers[i] next}

}

Что не так с моим процессом и как я могу найти, где в моей функции нетработать?

Ответы [ 3 ]

0 голосов
/ 25 апреля 2018

Вам нужно tryCatch, чтобы вернуть ошибку, а не ноль.

testfun <- function(x) {
    y <- matrix(5, nrow = x, ncol = 2)
    z <- as.data.frame(y)
    z
}

testnumbers <- c(0, 1, 2, 3, 4, -1, 5)
z <- numeric(length(testnumbers))

for (i in seq_along(testnumbers)) {
    tempo <- tryCatch(testfun(testnumbers[i]), error = function(e) e)
    if (inherits(tempo, "error")) {
        z[i] <- testnumbers[i]
    }
}

z
#[1]  0  0  0  0  0 -1  0

Также,

  • Для того, чтобы принудительно matrix к data.frame использовать as.data.frame.
  • Я удалил вызовы return, поскольку последнее значение функции является ее возвращаемым значением.
  • rep(0) совпадает с 0, заменяется на numeric(length(testnumbers)).
  • seq_along(testnumbers) всегда лучше, чем 1:length(testnumbers).Попробуйте с testnumbers длины ноль и посмотрите, что получится.
0 голосов
/ 25 апреля 2018

Если вы хотите запустить все из testnumbers независимо от того, какой из них вышел из строя, я предлагаю немного другой такт.

Base R

Это заимствовано из использования Руи inherits, которое является более надежным и однозначным.Это делает еще один шаг, сохраняя не только, какая из них имела ошибку, но и фактический текст ошибки:

testfun <- function(x) {
    y <- matrix(5, nrow = x, ncol = 2)
    z <- as.data.frame(y)
    z
}
testnumbers <- c(0, 1, 2, 3, 4, -1, 5)

rets <- setNames(
  lapply(testnumbers, function(n) tryCatch(testfun(n), error=function(e) e)),
  testnumbers
)

sapply(rets, inherits, "error")
#     0     1     2     3     4    -1     5 
# FALSE FALSE FALSE FALSE FALSE  TRUE FALSE 
Filter(function(a) inherits(a, "error"), rets)
# $`-1`
# <simpleError in matrix(5, nrow = x, ncol = 2): invalid 'nrow' value (< 0)>

(setNames(lapply(...), ...) - потому что входные данные являются числами, поэтому sapply(..., simplify=F) не сохранилимена, что я считал важным.)

Все это соответствует тому, что некоторые считают хорошей практикой: если вы выполняете одну функцию для множества «вещей», то делайте это в list, и, следовательно, в одной из *apply функций.

tidyverse

В purrr есть функция, которая немного формализует это: safely, которая возвращаетФункция обернута вокруг своего аргумента.Например:

library(purrr)
safely(testfun)
# function (...) 
# capture_error(.f(...), otherwise, quiet)
# <environment: 0x0000000015151d90>

Возвращает функцию, которую затем можно передать.Одноразовый вызов будет выглядеть следующим образом:

safely(testfun)(0)
# $result
# [1] V1 V2
# <0 rows> (or 0-length row.names)
# $error
# NULL
testfun_safe <- safely(testfun)
testfun_safe(0)
# $result
# [1] V1 V2
# <0 rows> (or 0-length row.names)
# $error
# NULL

Чтобы использовать его здесь, вы можете сделать:

rets <- setNames(
  lapply(testnumbers, safely(testfun)),
  testnumbers
)
str(rets[5:6])
# List of 2
#  $ 4 :List of 2
#   ..$ result:'data.frame':    4 obs. of  2 variables:
#   .. ..$ V1: num [1:4] 5 5 5 5
#   .. ..$ V2: num [1:4] 5 5 5 5
#   ..$ error : NULL
#  $ -1:List of 2
#   ..$ result: NULL
#   ..$ error :List of 2
#   .. ..$ message: chr "invalid 'nrow' value (< 0)"
#   .. ..$ call   : language matrix(5, nrow = x, ncol = 2)
#   .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
Filter(Negate(is.null), sapply(rets, `[[`, "error"))
# $`-1`
# <simpleError in matrix(5, nrow = x, ncol = 2): invalid 'nrow' value (< 0)>

и получить результаты всех запусков (включая ошибочный):

str(sapply(rets, `[[`, "result"))
# List of 7
#  $ 0 :'data.frame':   0 obs. of  2 variables:
#   ..$ V1: num(0) 
#   ..$ V2: num(0) 
#  $ 1 :'data.frame':   1 obs. of  2 variables:
#   ..$ V1: num 5
#   ..$ V2: num 5
#  $ 2 :'data.frame':   2 obs. of  2 variables:
#   ..$ V1: num [1:2] 5 5
#   ..$ V2: num [1:2] 5 5
#  $ 3 :'data.frame':   3 obs. of  2 variables:
#   ..$ V1: num [1:3] 5 5 5
#   ..$ V2: num [1:3] 5 5 5
#  $ 4 :'data.frame':   4 obs. of  2 variables:
#   ..$ V1: num [1:4] 5 5 5 5
#   ..$ V2: num [1:4] 5 5 5 5
#  $ -1: NULL
#  $ 5 :'data.frame':   5 obs. of  2 variables:
#   ..$ V1: num [1:5] 5 5 5 5 5
#   ..$ V2: num [1:5] 5 5 5 5 5

или только результаты без неудачного запуска:

str(Filter(Negate(is.null), sapply(rets, `[[`, "result")))
# List of 6
#  $ 0:'data.frame':    0 obs. of  2 variables:
#   ..$ V1: num(0) 
#   ..$ V2: num(0) 
#  $ 1:'data.frame':    1 obs. of  2 variables:
#   ..$ V1: num 5
#   ..$ V2: num 5
#  $ 2:'data.frame':    2 obs. of  2 variables:
#   ..$ V1: num [1:2] 5 5
#   ..$ V2: num [1:2] 5 5
#  $ 3:'data.frame':    3 obs. of  2 variables:
#   ..$ V1: num [1:3] 5 5 5
#   ..$ V2: num [1:3] 5 5 5
#  $ 4:'data.frame':    4 obs. of  2 variables:
#   ..$ V1: num [1:4] 5 5 5 5
#   ..$ V2: num [1:4] 5 5 5 5
#  $ 5:'data.frame':    5 obs. of  2 variables:
#   ..$ V1: num [1:5] 5 5 5 5 5
#   ..$ V2: num [1:5] 5 5 5 5 5
0 голосов
/ 25 апреля 2018

Вы были на самом деле довольно близко.Я не уверен, что сделал трюк в конце, но я

  1. Изменил 1: длина (номера тестов), так как это излишне
  2. Изменил return(0) на символ
  3. Оберните ваш if в другом, если он продолжал работать, если длина была больше 1 или не могла быть оценена.

Тогда вы получите правильные результаты.Вы можете попытаться изменить код по крупицам, чтобы увидеть, что не так.

test <- function(x) {y <- matrix( 5 , nrow = x , ncol =  2)
z<- data.frame(y) 
return(z) }

errored <- numeric()
testnumbers <- c(0,1,2,3,4,-1,5)

for (i in testnumbers) {      
  tempo <- tryCatch(test(i), error = function(e) "error")      
  if (length(tempo) == 1) {
    if (tempo == "error")  errored <- c(errored, i)
  }      
}
errored
> -1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...