уместно ли это когда-либо в невекторизованной ситуации и наоборот? - PullRequest
9 голосов
/ 19 ноября 2011

(Справочная информация: ifelse оценивает оба выражений, даже если будет возвращено только одно. EDIT: Это неправильное утверждение. См. Ответ Томми)

Есть лиЛюбой пример, где имеет смысл использовать ifelse в не векторизованной ситуации?Я думаю, что «удобочитаемость» может быть правильным ответом, когда мы не заботимся о небольшом выигрыше в эффективности, но, кроме того, это когда-либо быстрее / эквивалентно / лучше в каком-то другом способе использовать ifelse, когда if и затем else выполнит эту работу?

Точно так же, если у меня есть векторизованная ситуация, всегда ли ifelse лучший инструмент для использования?Кажется странным, что оба выражения оцениваются.Быстрее ли перебирать один за другим и делать обычный if, а затем else?Я предполагаю, что это имело бы смысл, только если бы оценка выражений заняла очень много времени.Есть ли другая альтернатива, которая не включала бы явный цикл?

Спасибо

Ответы [ 2 ]

15 голосов
/ 19 ноября 2011

Во-первых, ifelse делает НЕ всегда вычисляет оба выражения - только если в тестовом векторе есть элементы TRUE и FALSE.

ifelse(TRUE, 'foo', stop('bar')) # "foo"

И на мой взгляд:

ifelse следует не использовать в не векторизованной ситуации. всегда медленнее и более подвержен ошибкам для использования ifelse сверх if / else:

# This is fairly common if/else code
if (length(letters) > 0) letters else LETTERS

# But this "equivalent" code will yield a very different result - TRY IT!
ifelse(length(letters) > 0, letters, LETTERS)

Хотя в векторизованных ситуациях ifelse может быть хорошим выбором, но имейте в виду, что длина и атрибуты результата могут не соответствовать вашим ожиданиям (как указано выше, и я считаю ifelse нарушенным в этом отношении).

Вот пример: tst имеет длину 5 и имеет класс. Я ожидаю, что результат будет иметь длину 10 и не будет иметь никакого класса, но это не то, что происходит - он получит несовместимый класс и длину 5!

# a logical vector of class 'mybool'
tst <- structure(1:5 %%2 > 0, class='mybool')

# produces a numeric vector of class 'mybool'!
ifelse(tst, 101:110, 201:210)
#[1] 101 202 103 204 105
#attr(,"class")
#[1] "mybool"

С чего бы мне ожидать, что длина будет 10? Поскольку большинство функций в R "циклически изменяют", чем короче вектор, тем длиннее:

1:5 + 1:10 # returns a vector of length 10.

... Но ifelse только циклически изменяет аргументы да / нет, чтобы соответствовать длине аргумента tst.

Почему я ожидал, что класс (и другие атрибуты) не будут скопированы из тестового объекта? Потому что <, который возвращает логический вектор, не копирует класс и атрибуты из своих (обычно числовых) аргументов. Это не делает этого, потому что это, как правило, было бы очень неправильно.

1:5 < structure(1:10, class='mynum') # returns a logical vector without class

Наконец, может быть эффективнее "сделать это самостоятельно"? Что ж, похоже, что ifelse не такой примитив, как if, и для обработки NA требуется специальный код. Если у вас нет NA s, это может быть быстрее сделать это самостоятельно.

tst <- 1:1e7 %%2 == 0
a <- rep(1, 1e7)
b <- rep(2, 1e7)
system.time( r1 <- ifelse(tst, a, b) )            # 2.58 sec

# If we know that a and b are of the same length as tst, and that
# tst doesn't have NAs, then we can do like this:
system.time( { r2 <- b; r2[tst] <- a[tst]; r2 } ) # 0.46 secs

identical(r1, r2) # TRUE
4 голосов
/ 19 ноября 2011

По второму пункту, как вы определяете «лучший»?Я думаю, что ifelse() является одним из наиболее читаемых решений, но не всегда может быть самым быстрым.В частности, я обнаружил, что написание логических условий и их сложение может дать вам некоторые преимущества в производительности.Вот краткий пример:

> x <- rnorm(1e6)
> system.time(y1 <- ifelse(x > 0,1,2))
   user  system elapsed 
   0.46    0.08    0.53 
> system.time(y2 <- (x > 0) * 1 + (x <= 0) * 2)
   user  system elapsed 
   0.06    0.00    0.06 
> identical(y1, y2)
[1] TRUE

Итак, если скорость - ваша самая большая проблема, логический подход может быть лучше.Тем не менее, для большинства моих целей я нашел ifelse() достаточно быстрым, и его легко обмануть.Ваши мили могут различаться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...