instrukcja switch () użycie

106

Jestem trochę zdezorientowany co do instrukcji switch w R. Po prostu wyszukując w Google funkcję, otrzymuję następujący przykład:

Typowym zastosowaniem przełącznika jest rozgałęzianie zgodnie z wartością znaku jednego z argumentów funkcji.

 > 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

Jednak wydaje się to być tym samym, co po prostu posiadanie zestawu ifinstrukcji przeznaczonych dla każdegotype

Czy to wszystko, co trzeba switch()? Czy ktoś może mi podać dalsze przykłady i lepsze zastosowania?

LostLin
źródło
10
Tak, to wszystko.
Andrie,

Odpowiedzi:

119

Cóż, znowu czas na ratunek. Wydaje się, że switchjest generalnie szybszy niż ifoświadczenia. Tak więc, a fakt, że kod jest krótszy / schludniejszy z switchoświadczeniem, przechyla się na korzyść 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

Aktualizacja Mając na uwadze komentarz Joshuy, wypróbowałem inne sposoby analizy porównawczej. Mikroznak wydaje się najlepszy. ... i pokazuje podobne czasy:

> 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

Ostatnia aktualizacja Oto jak wszechstronny switchjest:

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

To mapuje case2i case3do 2.5i (nienazwany) domyślnie 99. Aby uzyskać więcej informacji, spróbuj?switch

Tommy
źródło
3
Użycie takiej pętli for może powodować problemy z usuwaniem elementów bezużytecznych. Różnica jest znacznie mniejsza z funkcją lepiej analizy porównawcze: benchmark(test1('trimmed'), test2('trimmed'), replications=1e6).
Joshua Ulrich
@JoshuaUlrich ... której benchmarkfunkcji używasz? Wydaje się, że nie jest to oczywiste z pakietu „benchmark”?
Tommy
1
Według stackoverflow.com/questions/6262203/ ... „microbenchmark” jest jeszcze lepszym rozwiązaniem.
Tommy
@JoshuaUlrich - zaktualizowałem odpowiedź o wyniki z microbencmark, ale są one bardzo podobne do moich oryginalnych. Naprawdę nie rozumiem, jak rbenchmark poradziłby sobie z problemem GC, ale wydaje się, że ma więcej narzutów, wywołując evali replicate.
Tommy
tak jak na uboczu, czy mogę mieć wiele przypadków z tym samym wynikiem? tj.switch(type, c(this,that)=do something)
LostLin
4

Krótko mówiąc, tak . Ale są chwile, kiedy możesz faworyzować jedno przeciwko drugiemu. Google „zmiana przypadku a jeśli jeszcze”. Trwają już dyskusje na temat SO. Tutaj jest również dobry film, który mówi o tym w kontekście MATLAB:

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

Osobiście, gdy mam 3 lub więcej przypadków, zwykle wybieram po prostu obudowę / przełącznik.

John Colby
źródło