2012-09-09 14 views
68

La documentación dice¿Por qué `vapply` es más seguro que` sapply`?

vapply es similar a sapply, pero tiene un tipo de pre-especificado de valor de retorno, por lo que puede ser más seguro [...] para utilizar.

¿Podría explicarnos por qué es generalmente más seguro, tal vez dando ejemplos?


P.S .: Yo sé la respuesta y ya tienden a evitar sapply. Solo desearía que hubiera una buena respuesta aquí en SO, así puedo señalarles a mis compañeros de trabajo. Por favor, no hay una respuesta de "leer el manual".

+1

Es más predecible, lo que hace que el código sea menos ambiguo y más robusto. Particularmente en proyectos más grandes, por ejemplo, un paquete grande, esto es relevante. –

+3

No es necesario el "P.S." ... simplemente responda usted mismo la pregunta. –

+5

@KonradRudolph A veces, las notas al respecto pueden enfocar la pregunta y evitar las respuestas "RTFM". :-) –

Respuesta

58

Como ya se ha señalado, vapply hace dos cosas:

  • mejora de la velocidad Poca
  • mejora la consistencia proporcionando controles de tipo de retorno limitados.

El segundo punto es la mayor ventaja, ya que ayuda a detectar errores antes de que ocurran y conduce a un código más robusto. Esta verificación del valor de retorno se puede hacer por separado usando sapply seguido de stopifnot para asegurarse de que los valores devueltos sean consistentes con lo que esperaba, pero vapply es un poco más fácil (si es más limitado, dado que el código de verificación de errores podría verificar valores dentro de límites , etc.)

Aquí hay un ejemplo de vapply que garantiza que el resultado sea el esperado. Esto es paralelo a algo en lo que estaba trabajando al raspar PDF, donde findD usaría un para que coincida con un patrón en datos de texto sin procesar (por ejemplo, tendría una lista que era split por entidad y una expresión regular para hacer coincidir las direcciones dentro de cada entidad. Ocasionalmente, el PDF se había convertido fuera de servicio y habría dos direcciones para una entidad, lo que causaba maldad).

> input1 <- list(letters[1:5], letters[3:12], letters[c(5,2,4,7,1)]) 
> input2 <- list(letters[1:5], letters[3:12], letters[c(2,5,4,7,15,4)]) 
> findD <- function(x) x[x=="d"] 
> sapply(input1, findD) 
[1] "d" "d" "d" 
> sapply(input2, findD) 
[[1]] 
[1] "d" 

[[2]] 
[1] "d" 

[[3]] 
[1] "d" "d" 

> vapply(input1, findD, "") 
[1] "d" "d" "d" 
> vapply(input2, findD, "") 
Error in vapply(input2, findD, "") : values must be length 1, 
but FUN(X[[3]]) result is length 2 

Como les digo a mis alumnos, parte de convertirse en un programador está cambiando su mentalidad de "errores son molestos" a "errores son mi amigo."

cero entradas de longitud
Un punto relacionado es que si la longitud de entrada es cero, sapply siempre devolverá una lista vacía, sin importar el tipo de entrada. Compare:

sapply(1:5, identity) 
## [1] 1 2 3 4 5 
sapply(integer(), identity) 
## list()  
vapply(1:5, identity) 
## [1] 1 2 3 4 5 
vapply(integer(), identity) 
## integer(0) 

Con vapply, usted está garantizado para tener un determinado tipo de producto, por lo que no tiene que escribir cheques adicionales para las entradas de longitud cero.

puntos de referencia

vapply puede ser un poco más rápido porque ya sabe qué formato se debe esperar los resultados en.

input1.long <- rep(input1,10000) 

library(microbenchmark) 
m <- microbenchmark(
    sapply(input1.long, findD), 
    vapply(input1.long, findD, "") 
) 
library(ggplot2) 
library(taRifx) # autoplot.microbenchmark is moving to the microbenchmark package in the next release so this should be unnecessary soon 
autoplot(m) 

autoplot

12

Si siempre quiere que su resultado sea algo en particular ... p. Ej. un vector lógico vapply se asegura de que esto ocurra, pero sapply no necesariamente lo hace.

a<-vapply(NULL, is.factor, FUN.VALUE=logical(1)) 
b<-sapply(NULL, is.factor) 

is.logical(a) 
is.logical(b) 
+3

Creo que lo más obvio es 'logical (1)' en este caso, ya que FALSE parece que establece una opción en "OFF" en lugar de especificar un tipo –

13

Las pulsaciones de teclas adicionales asociados con vapply se podrían ahorrar tiempo de depuración resultados confusos después. Si la función a la que llama puede devolver diferentes tipos de datos, ciertamente se debe usar vapply.

Un ejemplo que viene a la mente sería sqlQuery en el paquete RODBC. Si hay un error al ejecutar una consulta, esta función devuelve un vector character con el mensaje. Así, por ejemplo, digamos que usted está tratando de iterar sobre un vector de nombres de tabla tnames y seleccione el valor máximo de la columna numérica 'NumCol' en cada mesa con:

sapply(tnames, 
    function(tname) sqlQuery(cnxn, paste("SELECT MAX(NumCol) FROM", tname))[[1]]) 

Si todos los nombres de las tablas son válidos , esto daría como resultado un vector numeric. Pero si uno de los nombres de la tabla cambia en la base de datos y la consulta falla, los resultados serán forzados al modo character. Sin embargo, al usar vapply con FUN.VALUE=numeric(1), se detendrá el error aquí y se evitará que aparezca en algún lugar de la línea, o peor, en absoluto.

Cuestiones relacionadas