utilisation de l'instruction switch ()

106

Je suis un peu confus au sujet de l'instruction switch dans R. Simplement googler la fonction, j'obtiens un exemple comme suit:

Une utilisation courante du commutateur consiste à créer une branche en fonction de la valeur de caractère de l'un des arguments d'une fonction.

 > centre <- function(x, type) {
 + switch(type,
 +        mean = mean(x),
 +        median = median(x),
 +        trimmed = mean(x, trim = .1))
 + }
 > x <- rcauchy(10)
 > centre(x, "mean")
 [1] 0.8760325
 > centre(x, "median")
 [1] 0.5360891
 > centre(x, "trimmed")
 [1] 0.6086504

Cependant, cela semble être la même chose que d'avoir simplement un tas d' ifénoncés désignés pour chaquetype

Est-ce tout ce qu'il y a à faire switch()? Quelqu'un peut-il me donner d'autres exemples et de meilleures applications?

LostLin
la source
10
Oui, c'est tout ce qu'il y a à faire.
Andrie

Réponses:

119

Eh bien, le moment est venu à la rescousse. Cela semble switchgénéralement plus rapide que les ifdéclarations. Donc, et le fait que le code soit plus court / plus net avec une switchinstruction penche en faveur de switch:

# Simplified to only measure the overhead of switch vs if

test1 <- function(type) {
 switch(type,
        mean = 1,
        median = 2,
        trimmed = 3)
}

test2 <- function(type) {
 if (type == "mean") 1
 else if (type == "median") 2
 else if (type == "trimmed") 3
}

system.time( for(i in 1:1e6) test1('mean') ) # 0.89 secs
system.time( for(i in 1:1e6) test2('mean') ) # 1.13 secs
system.time( for(i in 1:1e6) test1('trimmed') ) # 0.89 secs
system.time( for(i in 1:1e6) test2('trimmed') ) # 2.28 secs

Mise à jour Avec le commentaire de Joshua à l'esprit, j'ai essayé d'autres moyens de comparer. Le microbenchmark semble le meilleur. ... et il montre des horaires similaires:

> library(microbenchmark)
> microbenchmark(test1('mean'), test2('mean'), times=1e6)
Unit: nanoseconds
           expr  min   lq median   uq      max
1 test1("mean")  709  771    864  951 16122411
2 test2("mean") 1007 1073   1147 1223  8012202

> microbenchmark(test1('trimmed'), test2('trimmed'), times=1e6)
Unit: nanoseconds
              expr  min   lq median   uq      max
1 test1("trimmed")  733  792    843  944 60440833
2 test2("trimmed") 2022 2133   2203 2309 60814430

Dernière mise à jour Voici à quel point la polyvalence switchest:

switch(type, case1=1, case2=, case3=2.5, 99)

Ce mappe case2et case3à 2.5et le (sans nom) par défaut à 99. Pour plus d'informations, essayez?switch

Tommy
la source
3
L'utilisation d'une boucle for comme celle-ci peut entraîner des problèmes de récupération de place. La différence est beaucoup plus petite avec une meilleure fonction d' analyse comparative: benchmark(test1('trimmed'), test2('trimmed'), replications=1e6).
Joshua Ulrich
@JoshuaUlrich ... quelle benchmarkfonction utilisez-vous? Pas le plus évident du package "benchmark" semble-t-il?
Tommy
1
Selon stackoverflow.com/questions/6262203/... "microbenchmark" est encore meilleur.
Tommy
@JoshuaUlrich - J'ai mis à jour la réponse avec les résultats de microbencmark, mais ils sont très similaires à ceux d'origine. Je ne vois pas vraiment comment rbenchmark contournerait le problème du GC, mais il semble avoir plus de frais généraux en appelant evalet replicate.
Tommy
tout comme je suis de côté, puis-je avoir plusieurs cas avec la même sortie? ieswitch(type, c(this,that)=do something)
LostLin
4

Bref oui . Mais il y a des moments où vous pourriez favoriser l'un par rapport à l'autre. Google "commutateur de cas vs if else". Il y a déjà des discussions sur SO aussi. Aussi, voici une bonne vidéo qui en parle dans le contexte de MATLAB:

http://blogs.mathworks.com/pick/2008/01/02/matlab-basics-switch-case-vs-if-elseif/

Personnellement, quand j'ai 3 cas ou plus, je vais généralement simplement avec cas / interrupteur.

John Colby
la source