2009-09-08 23 views
28

El código escrito usando lapply y friends suele ser más fácil para los ojos y más Rish que los bucles. Me encantan tanto como el siguiente tipo, pero ¿cómo puedo depurarlo cuando las cosas van mal? Por ejemplo:Debugging lapply/sapply calls

> ## 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 

Si hubiera usado un bucle 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 

Pero me gustaría saber dónde ocurrió el error:

> i 
[1] 2 

¿Qué debo hacer cuando se utiliza lapply/sapply?

Respuesta

23

utilizar las técnicas R estándar de depuración para detener exactamente cuando se produce el error:

options(error = browser) 

o

options(error = recover) 

Cuando se hace , vuelva al comportamiento estándar:

options(error = NULL) 
22

Si envuelve la función de su interior con una sentencia try(), se obtiene más información:

> 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" 

En este caso, se puede ver qué índice de falla.

0

Puede depurar() la función, o poner un navegador() dentro del cuerpo. Esto es especialmente útil si no tiene una iteración de Gajillion para trabajar.

Además, personalmente no he hecho esto, pero sospecho que podría poner un navegador() como parte de un tryCatch(), de modo que cuando se genera el error puede usar la interfaz del navegador().

8

utilizar el paquete plyr, con .inform = TRUE:

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

He enfrentado el mismo problema y he tendido a hacer que mis llamadas con (l) (m) (s) (t) se apliquen a funciones que puedo depurar().

Así, en lugar de bla < -sapply (x, la función (x) {x + 1})

diría,

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

y el uso de depuración (myfn) con opciones (error = recuperar).

También me gusta el consejo de pegar líneas de impresión() aquí y allá para ver qué está pasando.

Aún mejor es diseñar una prueba de myfn (x) que tenga que pasar y asegurarse de que pasa dicha prueba antes de someterla a sapply. Solo tengo paciencia para esto la mitad del tiempo.

+0

O simplemente 'blah <-sapply (x, mfn)'. Y no debería necesitar tanto 'debug' como recover' – hadley

1

Como geoffjentry dijo:

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

Además, el bucle podría reescribirse a ser mucho más limpio (posiblemente un poco más lento):

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

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

Para bucles son lentos en R, pero a menos realmente necesitas la velocidad con un enfoque iterativo simple sobre una lista de comprensión confusa.

Si necesito averiguar algo de código sobre la marcha, siempre a ir:

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

y escribir el código desde el interior de la función así que no veo lo que estoy haciendo.

- Dan

+3

Llamada incorrecta en el ciclo for. Eso hará que las cosas ** sean mucho más lentas. – hadley

+1

@hadley ... porque toda la matriz necesita ser reasignada (memoria de reserva, copiar datos antiguos) en cada paso de bucle :) :) y <- numérico (longitud (x)) 'debe ser la forma más rápida de preasignar. – AlexR

1

El uso de depuración o el navegador no es una buena idea en este caso, ya que dejará su código con tanta frecuencia. Use Try o TryCatch en su lugar, y trate la situación cuando surja.