Dlaczego instrukcje ifelse języka R nie mogą zwracać wektorów?

118

Od czasu do czasu stwierdziłem, że instrukcje ifelse w języku R są bardzo przydatne. Na przykład:

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

Ale jestem nieco zdezorientowany następującym zachowaniem.

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

Czy jest to wybór projektu, który przekracza mój abonament?

Christopher DuBois
źródło
1
trochę dziwny projekt dla ifelse, biorąc pod uwagę fakt, że proste if else działa.
2sb
4
ifelse jest funkcją wektoryzowaną. Powinny być używane do różnych zadań.
marbel

Odpowiedzi:

99

Dokumentacja dla ifelsestanów:

ifelsezwraca wartość o takim samym kształcie, jaki testjest wypełniona elementami wybranymi z jednego yeslub w nozależności od tego, czy element testjest TRUEczy FALSE.

Ponieważ przekazujesz wartości testowe o długości 1, otrzymujesz wyniki o długości 1. Jeśli przejdziesz dłuższe wektory testowe, otrzymasz dłuższe wyniki:

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

Więc ifelsejest przeznaczony do konkretnego celu testowania wektora wartości logicznych i zwracania wektora o tej samej długości, wypełnionego elementami pobranymi z (wektor) yesi noargumentami.

Ze względu na nazwę funkcji częstym zamieszaniem jest używanie tego, gdy naprawdę chcesz if () {} else {}zamiast tego zwykłą konstrukcję.

Nathan Kitchen
źródło
16
Być może to, czego naprawdę chciałeś w przypadku drugiego zestawu stwierdzeń, to if (TRUE) c(1,2) else c(3,4).
Jonathan Chang
69

Założę się, że ifzamiast tego chcesz prostego oświadczenia ifelse- w R ifnie jest tylko strukturą przepływu sterowania, może zwrócić wartość:

> if(TRUE) c(1,2) else c(3,4)
[1] 1 2
> if(FALSE) c(1,2) else c(3,4)
[1] 3 4
Ken Williams
źródło
@Ken, to działa dla mnie, mimo że otrzymuję to, czego potrzebuję, ciągłe ostrzeżenie, " Warning in if (req(inputval) == "All") { : the condition has length > 1 and only the first element will be used"co mam zrobić, aby pozbyć się tego ostrzeżenia?
user5249203
1
@ user5249203, pytanie i odpowiedź Kena odnoszą się do przypadku, w którym warunkiem jest pojedyncza wartość, tj. wektor o długości 1. Ostrzeżenie wskazuje, że req(inputval)ma więcej elementów. Aby uzyskać pojedynczą wartość, przydatne mogą być funkcje any()lub all().
Uwe,
12

Pamiętaj, że możesz obejść problem, jeśli przypiszesz wynik w 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
źródło
3
IMHO, to zachęca do niewłaściwego wykorzystania ifelse()funkcji wektoryzowanej zamiast przepływu kontrolnego if ... else ...do przypisania. Jeśli warunek jest pojedynczym TRUElub FALSEwartością, wolałbym napisać a <- if (TRUE) c(1,2) else c(3,4)lubif (TRUE) a <- c(1,2) else a <- c(3,4)
Uwe
1
@Uwe chociaż nie sądzę, aby różnica w wydajności podczas używania ifelsezamiast if... elsew przypadku pojedynczego warunku może naprawdę stanowić problem i ifelsemoże być preferowana w niektórych przypadkach wewnątrz kodu (tutaj proste zgadywanie), nie mogę się z tobą nie zgodzić ;-). Chciałem tylko pokazać sposób ifelse.
Cath
9

tak, myślę, że ifelse () jest naprawdę zaprojektowana, gdy masz duży, długi wektor testów i chcesz odwzorować każdy z nich na jedną z dwóch opcji. Na przykład często robię kolory dla plot () w ten sposób:

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

Gdybyś miał duży, długi wektor testów, ale chciałbyś uzyskać pary dla wyników, mógłbyś użyć sapply()lub plyr's llply()lub czegoś takiego.

Brendan OConnor
źródło
4

Czasami użytkownik potrzebuje po prostu switchoświadczenia zamiast pliku ifelse. W tym wypadku:

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

(co jest kolejną opcją składni odpowiedzi Kena Williamsa)

agenis
źródło
4

Oto podejście podobne do sugerowanego przez Cath, ale może działać z istniejącymi wstępnie przypisanymi wektorami

Opiera się na użyciu czegoś get()podobnego:

a <- c(1,2)
b <- c(3,4)
get(ifelse(TRUE, "a", "b"))
# [1] 1 2
bmonger
źródło
4

użyj „if”, np

> `if`(T,1:3,2:4)
[1] 1 2 3
blueskyddd
źródło
To jedyna odpowiedź, która faktycznie może zapewnić oczekiwaną funkcjonalność ifelse.
sus_mlm
2

W twoim przypadku pomocne byłoby użycie if_elsefrom dplyr: if_elsejest bardziej rygorystyczne niż ifelsei generuje błąd dla twojego przypadku:

library(dplyr)
if_else(TRUE,c(1,2),c(3,4))
#> `true` must be length 1 (length of `condition`), not 2
Matifou
źródło
0

Znalezione na każdym kroku :

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

Może odtworzyć wynik twojego stanu, aby zwrócić żądaną długość

SJGD
źródło