Во-первых, 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