uso da instrução switch ()

106

Estou um pouco confuso sobre a instrução switch em R. Simplesmente pesquisando a função no Google, recebo um exemplo como segue:

Um uso comum de switch é desviar de acordo com o valor do caractere de um dos argumentos para uma função.

 > 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

No entanto, isso parece ser o mesmo que ter um monte de ifdeclarações designadas para cadatype

Isso é tudo que há para fazer switch()? Alguém pode me dar mais exemplos e melhores aplicações?

LostLin
fonte
10
Sim, isso é tudo que há para fazer.
Andrie,

Respostas:

119

Bem, o tempo para o resgate novamente. Parece switchgeralmente mais rápido do que ifdeclarações. Portanto, e o fato de que o código é mais curto / mais puro com uma switchdeclaração, inclina-se a favor 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

Atualização Com o comentário de Joshua em mente, tentei outras maneiras de avaliar. O microbenchmark parece o melhor. ... e mostra tempos semelhantes:

> 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

Atualização final Aqui está mostrando como switché versátil :

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

Este mapeia case2e case3para 2.5e o padrão (sem nome) para 99. Para mais informações, tente?switch

Tommy
fonte
3
Usar um loop for como esse pode causar problemas com a coleta de lixo. A diferença é muito menor com uma melhor função de benchmarking: benchmark(test1('trimmed'), test2('trimmed'), replications=1e6).
Joshua Ulrich,
@JoshuaUlrich ... qual benchmarkfunção você está usando? Não é o óbvio do pacote "benchmark", parece?
Tommy
1
De acordo com stackoverflow.com/questions/6262203/… "microbenchmark" é ainda melhor.
Tommy
@JoshuaUlrich - Eu atualizei a resposta com os resultados de microbencmark, mas eles são muito semelhantes aos meus originais. Eu realmente não vejo como o rbenchmark contornaria o problema do GC, mas parece ter mais sobrecarga chamando evale replicate.
Tommy
assim como estou à parte, posso ter vários casos com a mesma saída? ieswitch(type, c(this,that)=do something)
LostLin de
4

Resumindo, sim . Mas há momentos em que você pode favorecer um em relação ao outro. Google "troca de caso vs. if else". Já existem algumas discussões sobre o SO também. Além disso, aqui está um bom vídeo que fala sobre isso no contexto do MATLAB:

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

Pessoalmente, quando tenho 3 ou mais casos, geralmente só vou com caso / switch.

John Colby
fonte