lapply vs boucle for - Performance de la R
Il est souvent dit que l'on doit préférer lapply
sur for
boucles.
Il y a quelques exception comme par exemple Hadley Wickham souligne dans son avant-R livre.
(http://adv-r.had.co.nz/Functionals.html) (Modification en place, la Récursivité, etc).
Le suivant est un de ces cas.
Juste pour l'amour de l'apprentissage, j'ai essayé de réécrire un perceptron de l'algorithme dans une forme fonctionnelle pour indice de référence
la performance relative.
source (https://rpubs.com/FaiHas/197581).
Voici le code.
# prepare input
data(iris)
irissubdf <- iris[1:100, c(1, 3, 5)]
names(irissubdf) <- c("sepal", "petal", "species")
head(irissubdf)
irissubdf$y <- 1
irissubdf[irissubdf[, 3] == "setosa", 4] <- -1
x <- irissubdf[, c(1, 2)]
y <- irissubdf[, 4]
# perceptron function with for
perceptron <- function(x, y, eta, niter) {
# initialize weight vector
weight <- rep(0, dim(x)[2] + 1)
errors <- rep(0, niter)
# loop over number of epochs niter
for (jj in 1:niter) {
# loop through training data set
for (ii in 1:length(y)) {
# Predict binary label using Heaviside activation
# function
z <- sum(weight[2:length(weight)] * as.numeric(x[ii,
])) + weight[1]
if (z < 0) {
ypred <- -1
} else {
ypred <- 1
}
# Change weight - the formula doesn't do anything
# if the predicted value is correct
weightdiff <- eta * (y[ii] - ypred) * c(1,
as.numeric(x[ii, ]))
weight <- weight + weightdiff
# Update error function
if ((y[ii] - ypred) != 0) {
errors[jj] <- errors[jj] + 1
}
}
}
# weight to decide between the two species
return(errors)
}
err <- perceptron(x, y, 1, 10)
### my rewriting in functional form auxiliary
### function
faux <- function(x, weight, y, eta) {
err <- 0
z <- sum(weight[2:length(weight)] * as.numeric(x)) +
weight[1]
if (z < 0) {
ypred <- -1
} else {
ypred <- 1
}
# Change weight - the formula doesn't do anything
# if the predicted value is correct
weightdiff <- eta * (y - ypred) * c(1, as.numeric(x))
weight <<- weight + weightdiff
# Update error function
if ((y - ypred) != 0) {
err <- 1
}
err
}
weight <- rep(0, 3)
weightdiff <- rep(0, 3)
f <- function() {
t <- replicate(10, sum(unlist(lapply(seq_along(irissubdf$y),
function(i) {
faux(irissubdf[i, 1:2], weight, irissubdf$y[i],
1)
}))))
weight <<- rep(0, 3)
t
}
Je n'ai pas attendu une amélioration constante en raison de ladite
les questions de. Mais néanmoins, j'ai été vraiment surpris quand j'ai vu la forte détérioration
à l'aide de lapply
et replicate
.
J'ai obtenu ces résultats en utilisant microbenchmark
fonction de microbenchmark
bibliothèque
Ce que pourrait être les raisons?
Pourrait-il être certains de fuite de mémoire?
expr min lq mean median uq
f() 48670.878 50600.7200 52767.6871 51746.2530 53541.2440
perceptron(as.matrix(irissubdf[1:2]), irissubdf$y, 1, 10) 4184.131 4437.2990 4686.7506 4532.6655 4751.4795
perceptronC(as.matrix(irissubdf[1:2]), irissubdf$y, 1, 10) 95.793 104.2045 123.7735 116.6065 140.5545
max neval
109715.673 100
6513.684 100
264.858 100
La première fonction est lapply
/replicate
fonction
La seconde est la fonction avec for
boucles
La troisième est la même fonction, dans C++
à l'aide de Rcpp
Ici Selon Roland, le profil de la fonction.
Je ne suis pas sûr que je puisse l'interpréter dans le droit chemin.
Il ressemble pour moi la plupart du temps est passé dans subsetting
La fonction de profilage
- Veuillez être précis. Je ne vois pas du tout appel à
apply
dans votre fonctionf
. - Je suggère que vous apprenez comment les fonctions de profil: adv-r.had.co.nz/Profiling.html
- Il y a quelques erreurs dans votre code; tout d'abord,
irissubdf[, 4] <- 1
devrait êtreirissubdf$y <- 1
, de sorte que vous pouvez utiliser ce nom plus tard, et la deuxième,weight
n'est pas défini avant de l'utiliser dansf
. Il n'est également pas clair pour moi que le<<-
est en train de faire la bonne chose dans votrelapply
etreplicate
de commande, mais il n'est pas clair pour moi ce que c'est censé faire. Cela peut également être une différence majeure entre les deux; la<<-
doit composer avec les environnements, tandis que l'autre n'a pas, et si je ne sais pas exactement quel effet cela pourrait avoir, c'est pas tout à fait une des pommes avec des pommes comparaison plus. - Grâce à ce point, j'ai juste oublié la copie le code d'initialisation de poids( et weightdiff). J'ai utilisé <<- parce que la modification de l'algorithme du vecteur de poids à chaque itération, la seule solution que j'ai trouvé est de mettre à jour les données dans un vecteur de l'appelant de l'environnement
- Salut, j'ai essayé par curiosité pour supprimer <<-. bien sûr, le code est maintenant le mal, mais il n'y a pas d'amélioration de la performance. Ainsi, le champ d'application le détachement n'est pas la cause
- 1. Votre code ne s'exécute pas comme l'écrit; vous voulez probablement
y
et pasirissubdf$y
. 2. Votre code ne fonctionne pas comme l'écrit;f()
ne renvoie pas le même résultat queperceptron(*)
. - vous avez raison, c'est maintenant correct
- Votre code est toujours mauvais.
Vous devez vous connecter pour publier un commentaire.
Tout d'abord, c'est une déjà longue démystifié le mythe que
for
boucles sont plus lentement quelapply
. Lefor
des boucles de R ont été faites beaucoup plus performant et qui sont actuellement au moins aussi vite quelapply
.Cela dit, vous devez repenser votre utilisation de
lapply
ici. Votre mise en œuvre des exigences de l'affectation à l'environnement mondial, parce que votre code vous demande de mettre à jour le poids lors de la boucle. Et c'est une raison valable de ne pas considérerlapply
.lapply
est une fonction que vous devez utiliser pour ses effets secondaires (ou l'absence d'effets secondaires). La fonctionlapply
combine les résultats dans une liste automatiquement et ne plaisante pas avec l'environnement dans lequel vous travaillez, contrairement à unfor
boucle. Il en va de même pourreplicate
. Voir aussi cette question:Est R de famille, plus de sucre syntaxique?
La raison de votre
lapply
solution est beaucoup plus lent, c'est parce que de votre façon de l'utiliser crée beaucoup plus de surcharge.replicate
n'est rien d'autre maissapply
en interne, de sorte que vous combinersapply
etlapply
pour mettre en œuvre votre double boucle.sapply
crée une charge supplémentaire, car il a pour tester si oui ou non le résultat peut être simplifiée. Ainsi, unfor
boucle sera effectivement plus rapide que l'utilisationreplicate
.lapply
fonction anonyme, vous avez accès à la dataframe de x et de y pour chaque observation. Cela signifie que -contrairement à dans votre boucle for - par exemple la fonction$
doit être appelée à chaque fois.for
solution que seuls les appels 26. Ces fonctions supplémentaires pour lalapply
solution inclure des appels à des fonctions commematch
,structure
,[[
,names
,%in%
,sys.call
,duplicated
, ...Toutes les fonctions ne sont pas nécessaires par votre
for
boucle tant que l'on ne doit pas faire l'une de ces vérifications.Si vous voulez voir où cette surcharge vient de regarder le code interne de
replicate
,unlist
,sapply
etsimplify2array
.Vous pouvez utiliser le code suivant pour obtenir une meilleure idée de l'endroit où vous perdez vos performances avec le
lapply
. Exécutez cette ligne par ligne!En fait,
J'ai fait tester la différence avec un un problème que résoudre récemment.
Juste essayer de vous-même.
Dans ma conclusion, n'ont pas de différence, mais pour la boucle de mon cas étaient pas significativement plus rapide que lapply.
Ps: j'essaie surtout de garder la même logique à utiliser.
Comme vous avez pu le voir, j'ai fait une routine simple de construire un named_list dans un dataframe, la touche func fonction de la colonne des valeurs extraites, de la f1, utilise une boucle for pour parcourir le dataframe et le f2 utilise un lapply fonction.
Dans mon ordinateur, j'obtiens ce résultat:
&&
library()
pour labenchmark()
fonction et également définirds2
?f1