Почему операторы ifelse в R не могут возвращать векторы?

118

Время от времени я обнаружил, что ifelse-операторы R. Например:

ifelse(TRUE,1,2)
# [1] 1
ifelse(FALSE,1,2)
# [1] 2

Но меня несколько смущает следующее поведение.

ifelse(TRUE,c(1,2),c(3,4))
# [1] 1
ifelse(FALSE,c(1,2),c(3,4))
# [1] 3

Это вариант дизайна, который выше моей зарплаты?

Кристофер Дюбуа
источник
1
немного странный дизайн для ifelse, учитывая тот факт, что простой if else работает.
2сб
4
ifelse - векторизованная функция. Их следует использовать для разных задач.
marbel

Ответы:

99

В документации ifelseговорится:

ifelseвозвращает значение с той же формой, testкоторая заполнена с элементами , выбранными из либо yesили в noзависимости от того , элемент testявляется TRUEили FALSE.

Поскольку вы передаете тестовые значения длины 1, вы получаете результаты длины 1. Если вы передаете более длинные тестовые векторы, вы получите более длинные результаты:

> ifelse(c(TRUE, FALSE), c(1, 2), c(3, 4))
[1] 1 4

So ifelseпредназначен для конкретной цели тестирования вектора логических значений и возврата вектора той же длины, заполненного элементами, взятыми из (вектора) yesи noаргументов.

Это обычная путаница из-за имени функции, когда if () {} else {}вместо этого вам нужна обычная конструкция.

Натан Китчен
источник
16
Возможно, то, что вы действительно хотели для второго набора утверждений, было if (TRUE) c(1,2) else c(3,4).
Джонатан Чанг,
69

Держу пари, вам нужен простой ifоператор вместо ifelse- в R ifэто не просто структура потока управления, она может возвращать значение:

> if(TRUE) c(1,2) else c(3,4)
[1] 1 2
> if(FALSE) c(1,2) else c(3,4)
[1] 3 4
Кен Уильямс
источник
@ Кен, у меня это работает, хотя я получаю то, что мне нужно, постоянное предупреждение, " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"что мне делать, чтобы избавиться от этого предупреждения?
user5249203 05
1
@ user5249203, вопрос и ответ Кена относятся к случаю, когда условие представляет собой одно значение, т. е. вектор длины 1. Предупреждение указывает, что в req(inputval)нем больше элементов. Чтобы получить одно значение, функции any()или all()могут быть полезны.
Uwe
12

Обратите внимание, что вы можете обойти проблему, если назначите результат внутри ifelse:

ifelse(TRUE, a <- c(1,2), a <- c(3,4))
a
# [1] 1 2

ifelse(FALSE, a <- c(1,2), a <- c(3,4))
a
# [1] 3 4
Cath
источник
3
IMHO, это обнадеживает неправильное использование векторизованной ifelse()функции вместо потока управления if ... else ...для назначения. Если условие - одно TRUEили FALSEзначение, я бы предпочел написать a <- if (TRUE) c(1,2) else c(3,4)илиif (TRUE) a <- c(1,2) else a <- c(3,4)
Уве
1
@Uwe, хотя я не думаю, что разница в производительности при использовании ifelseвместо if... elseв случае одного условия действительно может быть проблемой и ifelseможет быть предпочтительнее в некоторых случаях внутри кода (простое предположение здесь), я не могу с вами не согласиться ;-). Я просто хотел показать свой путь ifelse.
Cath
9

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

plot(x,y, col = ifelse(x>2,  'red', 'blue'))

Если у вас большой длинный вектор тестов , но хотел пар для выходов, вы можете использовать sapply()или plyr«S llply()или что - то, возможно.

Брендан ОКоннор
источник
4

Иногда пользователю просто нужен switchоператор вместо ifelse. В таком случае:

condition <- TRUE
switch(2-condition, c(1, 2), c(3, 4))
#### [1] 1 2

(это еще один вариант синтаксиса ответа Кена Уильямса)

agenis
источник
4

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

Он основан на использовании следующего get():

a <- c(1,2)
b <- c(3,4)
get(ifelse(TRUE, "a", "b"))
# [1] 1 2
bmonger
источник
4

используйте `if`, например

> `if`(T,1:3,2:4)
[1] 1 2 3
blueskyddd
источник
Это единственный ответ, который действительно может обеспечить ожидаемую функциональность ifelse.
sus_mlm
2

В вашем случае было бы полезно использовать if_elsefrom dplyr: if_elseболее строгий, чем ifelse, и выдает ошибку для вашего случая:

library(dplyr)
if_else(TRUE,c(1,2),c(3,4))
#> `true` must be length 1 (length of `condition`), not 2
Matifou
источник
0

Найдено на каждом дропре :

ifelse(rep(TRUE, length(c(1,2))), c(1,2),c(3,4))
#>[1] 1 2

Может воспроизвести результат вашего состояния, чтобы вернуть желаемую длину

SJGD
источник