Отладка звонков lapply/sapply

Код, написанный с использованием lapply и друзей, обычно проще на глазах и больше Rish, чем на петлях. Я обожаю себя так же, как и следующий парень, но как мне отлаживать его, когда что-то идет не так? Например:

> ## a list composed of numeric elements 
> x <- as.list(-2:2)
> ## turn one of the elements into characters
> x[[2]] <- "what?!?"
> 
> ## using sapply
> sapply(x, function(x) 1/x)
Error in 1/x : non-numeric argument to binary operator

Если бы я использовал цикл for:

> y <- rep(NA, length(x))
> for (i in 1:length(x)) {
+     y[i] <-  1/x[[i]]
+ }
Error in 1/x[[i]] : non-numeric argument to binary operator

Но я бы знал, где произошла ошибка:

> i
[1] 2

Что мне делать при использовании lapply/sapply?

Ответ 1

Используйте стандартные методы отладки R, чтобы точно остановиться при возникновении ошибки:

options(error = browser) 

или

options(error = recover)

По завершении верните стандартное поведение:

options(error = NULL)

Ответ 2

Если вы завернете свою внутреннюю функцию с помощью инструкции try(), вы получите дополнительную информацию:

> sapply(x, function(x) try(1/x))
Error in 1/x : non-numeric argument to binary operator
[1] "-0.5"                                                    
[2] "Error in 1/x : non-numeric argument to binary operator\n"
[3] "Inf"                                                     
[4] "1"                                                       
[5] "0.5"

В этом случае вы можете увидеть, какой индекс терпит неудачу.

Ответ 3

Используйте пакет plyr, .inform = TRUE:

library(plyr)
laply(x, function(x) 1/x, .inform = TRUE)

Ответ 4

Как сказал geoffjentry:

> sapply(x, function(x) {
  res <- tryCatch(1 / x,
                  error=function(e) {
                          cat("Failed on x = ", x, "\n", sep="") ## browser()
                          stop(e)
                        })
})

Кроме того, ваш цикл for может быть переписан, чтобы быть намного чище (возможно, немного медленнее):

> y <- NULL
> for (xi in x)
    y <- c(y, 1 / xi)

Error in 1/xi : non-numeric argument to binary operator

Для циклов в R медленнее, но если вам действительно не нужна скорость, я бы пошел с простым итеративным подходом к запутанному пониманию списка.

Если мне нужно выяснить код на лету, я всегда пойду:

sapply(x, function(x) {
  browser()
  ...
})

И напишите код внутри функции, чтобы я увидел, что я получаю.

- Дэн

Ответ 5

Использование отладки или браузера не является хорошей идеей в этом случае, потому что это будет часто останавливать ваш код. Вместо этого используйте Try или TryCatch и рассмотрите ситуацию, когда она возникает.

Ответ 6

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

Кроме того, я лично не сделал этого, но я подозреваю, что вы можете поместить браузер() как часть tryCatch(), так что при возникновении ошибки вы можете использовать интерфейс browser().

Ответ 7

Я столкнулся с той же проблемой и имел тенденцию делать мои вызовы с (l) (m) (s) (t) применимыми к функции, которые я могу отлаживать().

Итак, вместо blah < -sapply (x, function (x) {x + 1})

Я бы сказал,

 myfn<-function(x){x+1}
 blah<-sapply(x,function(x){myfn(x)})

и используйте debug (myfn) с параметрами (ошибка = восстановление).

Мне также нравится совет о вставке print() строк здесь и там, чтобы увидеть, что происходит.

Еще лучше разработать тест myfn (x), который он должен пройти, и убедиться, что он проходит тест, прежде чем подвергать его дряблости. У меня есть только терпение к этому примерно в половине случаев.