86 votes

Pourquoi `vapply` est-il plus sûr que` sapply`?

La documentation dit

vapply est similaire à sapply, mais possède un type de valeur de retour prédéfini, ce qui rend son utilisation [...] plus sûre.

Pourriez-vous préciser pourquoi c'est généralement plus sûr, peut-être donner des exemples?


PS: Je connais la réponse et j'ai déjà tendance à éviter sapply . Je souhaite juste qu'il y ait une bonne réponse ici sur SO afin que je puisse la diriger vers mes collègues. S'il vous plaît, pas de réponse "lisez le manuel".

75voto

Ari B. Friedman Points 24940

Comme cela a déjà été noté, vapply fait deux choses:

  • Légère amélioration de la vitesse
  • Améliore la cohérence en fournissant limité le type de retour de chèques.

Le deuxième point est le plus grand avantage, car il permet de détecter les erreurs avant qu'ils ne surviennent et conduit à code plus robuste. Cette valeur de retour de la vérification peut être effectuée séparément par le biais d' sapply suivie par stopifnot pour vous assurer que les valeurs de retour sont cohérents avec ce que vous attendiez, mais vapply est un peu plus facile (si plus limitée, puisque la coutume d'erreur de code de vérification pourraient vérifier les valeurs à l'intérieur de limites, etc.).

Voici un exemple d' vapply veillant à ce que votre résultat est comme prévu. Cela équivaut à quelque chose, j'étais en train de travailler sur tout PDF de grattage, d'où l' findD serait d'utiliser une pour correspondre à un modèle à des données textuelles (par exemple, j'aimerais avoir une liste qui a été split par entité, et une expression régulière pour rechercher des adresses au sein de chaque entité. Parfois, le PDF a été converti out-of-order et il y aurait deux adresses pour une entité, qui a causé la méchanceté).

> 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

Comme je l'ai dis à mes étudiants, une partie de devenir un programmeur est de changer votre mentalité de "les erreurs sont ennuyeux" pour "les erreurs sont mon ami."

Longueur zéro entrées
Un point, c'est que si l'entrée de la longueur est égale à zéro, sapply retournera toujours une liste vide, quel que soit le type d'entrée. Comparer:

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)

Avec vapply, vous avez la garantie d'avoir un type particulier de sortie, de sorte que vous n'avez pas besoin d'écrire des contrôles supplémentaires pour une longueur de zéro intrants.

Repères

vapply peut être un peu plus rapide car il sait déjà ce format il faut attendre les résultats.

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

15voto

Matthew Plourde Points 18649

La clé des traits impliqués avec vapply pourrait vous faire économiser de débogage au moment de confusion plus tard. Si la fonction que vous êtes d'appel peut renvoyer différents types de données, vapply doit certainement être utilisée.

Un exemple qui vient à l'esprit serait sqlQuery dans la RODBC package. Si il y a une erreur lors de l'exécution d'une requête, cette fonction renvoie un character de vecteur avec le message. Ainsi, par exemple, dire que vous essayez de parcourir un vecteur de noms de table tnames et sélectionner la valeur maximale de la colonne numérique 'NumCol" dans chaque tableau avec:

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

Si tous les noms de table sont valides, ce qui aboutirait à un numeric vecteur. Mais si l'un des noms de table qui arrive à modifier dans la base de données et la requête échoue, les résultats vont être contraint de mode character. À l'aide de vapply avec FUN.VALUE=numeric(1), cependant, va cesser de l'erreur ici et à l'empêcher d'éclater quelque part en bas de la ligne---ou, pire, pas du tout.

13voto

user1317221_G Points 6991

Si vous voulez toujours que votre résultat soit quelque chose de particulier ... par exemple un vecteur logique. vapply s'assure que cela se produit mais sapply ne le fait pas nécessairement.

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

is.logical(a)
is.logical(b)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X