Menu

If else w R: if, else if, ifelse() i switch()

Instrukcje warunkowe w R: składnia if/else if/else, pułapka z nawiasem klamrowym, która psuje skrypty, wektorowe ifelse() dla całych wektorów i switch() do wyboru spośród wielu opcji.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Instrukcja if

Instrukcja if w R uruchamia blok kodu tylko wtedy, gdy warunek ma wartość TRUE. Warunek trafia do nawiasów okrągłych, kod do klamrowych, a opcjonalne else mówi, co zrobić w przeciwnym razie:

Warunek score >= 60 daje jedno TRUE, więc uruchamia się pierwsza gałąź. Takie warunki budujesz z operatorów porównania i logicznych: ==, >=, &&, %in% i pokrewnych.

Przy więcej niż dwóch możliwościach łączysz sprawdzenia przez else if. R przechodzi łańcuch od pierwszego warunku w dół i uruchamia pierwszą gałąź, której warunek jest TRUE, a resztę pomija:

Kolejność ma znaczenie: 85 >= 80 jest sprawdzane przed 85 >= 70, więc ocena to "B". Gdyby sprawdzenia stały w odwrotnej kolejności, każdy wynik powyżej 70 dostałby "C".

Pułapka z klamrą: gdzie musi stać else

Ten błąd trafia prawie każdego w pierwszym skrypcie w R. W skrypcie (albo w czymkolwiek uruchamianym przez Rscript) else musi stać w tej samej linii co nawias zamykający poprzedniej gałęzi. To nie zadziała:

if (score >= 60) {
  print("You passed")
}
else {              # Error: unexpected 'else'
  print("Try again")
}

Dlaczego? R analizuje kod instrukcja po instrukcji. Gdy dociera do } na początku linii, instrukcja if jest składniowo kompletna i R od razu ją wykonuje. else w następnej linii trafia jako początek nowej instrukcji, a instrukcja nie może zaczynać się od else. Rozwiązaniem jest wzorzec używany na całej tej stronie:

if (score >= 60) {
  print("You passed")
} else {            # brace and else on the same line
  print("Try again")
}

Wewnątrz ciała funkcji albo dowolnej zewnętrznej pary klamer ta surowa zasada łagodnieje (R czyta dalej, aż zamknie się zewnętrzna klamra), ale nie polegaj na tym. Styl } else { działa wszędzie i to on jest zalecany w przewodnikach stylu R.

Warunek musi być jednym TRUE albo FALSE

if() wymaga dokładnie jednej wartości logicznej. Dwie sytuacje łamią tę zasadę i obie kończą się błędem:

if (NA) print("hi")
# Error: missing value where TRUE/FALSE needed

if (c(TRUE, FALSE)) print("hi")
# Error: the condition has length > 1

Przypadek z NA ma znaczenie w praktyce, bo porównania z brakującymi danymi dają NA: jeśli x to NA, to x > 10 też jest NA, a if z takim warunkiem zatrzymuje skrypt. Wzorzec obronny korzysta ze skróconego &&:

Ponieważ !is.na(x) jest FALSE, część x > 10 nigdy nie jest obliczana i żadne NA nie trafia do warunku. To, jak NA przechodzi przez porównania, opisuje artykuł o brakujących wartościach.

Błąd o długości większej niż 1 (egzekwowany od R 4.2; wcześniej R po cichu brał pierwszy element) zwykle oznacza, że chodziło o coś innego: any(v > 5), jeśli wystarczy jedno dopasowanie, all(v > 5), jeśli każdy element musi spełnić warunek, albo ifelse(), jeśli potrzebna jest odpowiedź dla każdego elementu. O tym jest kolejna sekcja.

ifelse(): wektorowe if else

if decyduje raz. ifelse(test, yes, no) decyduje dla każdego elementu wektora i zwraca wektor tej samej długości:

Każdy element temps jest sprawdzany osobno: "hot" "mild" "mild" "hot" "mild". To narzędzie do wyprowadzania nowego wektora albo kolumny ramki danych z istniejących danych, bez żadnej pętli.

W przeciwieństwie do if, ifelse() łagodnie obsługuje brakujące wartości: NA w teście daje NA w wyniku zamiast błędu:

Wywołania ifelse() można zagnieżdżać przy podziale na trzy grupy i na tym poziomie kod nadal jest czytelny:

Ale na tym poprzestań. Trzy lub więcej zagnieżdżonych ifelse() zamienia się w łamigłówkę z dopasowywaniem nawiasów. Wtedy sięgnij po cut() (dla zakresów liczbowych) albo dplyr::case_when(), który umieszcza każdy warunek w osobnej linii.

switch() do wyboru spośród wielu opcji

Gdy dopasowujesz jedną wartość do listy znanych nazw, a nie sprawdzasz zakresów, długi łańcuch else if jest niezgrabnym narzędziem. switch() mówi to wprost:

Trzy zachowania, które warto znać:

  • Dopasowanie: switch() porównuje wartość tekstową z nazwami argumentów i zwraca pasujący wynik.
  • Przejście dalej: nazwa z pustą prawą stroną (sat = ,) przechodzi do wyniku następnego przypadku. Dzięki temu "sat" i "sun" współdzielą "weekend" bez powtarzania.
  • Wartość domyślna: ostatni nienazwany argument ("weekday") jest zwracany, gdy nic nie pasuje. Bez wartości domyślnej niedopasowana wartość sprawia, że switch() zwraca niewidocznie NULL: funkcja po cichu nic nie zwraca, co jest mylącym błędem. Zawsze podawaj wartość domyślną (albo jawne stop()).

switch() błyszczy wewnątrz funkcji, które wybierają działanie na podstawie argumentu trybu albo metody; przy progach liczbowych zostań przy else if albo cut().

Co warto zapamiętać

  • if (condition) { } else { }: warunek w nawiasach, uruchamia się jedna gałąź; łańcuch tworzysz przez else if, wygrywa pierwsze dopasowanie.
  • W skryptach pisz } else { w jednej linii, bo else na początku linii to błąd składni.
  • if() potrzebuje dokładnie jednego TRUE/FALSE: NA to błąd, dłuższy wektor to błąd (od R 4.2). Zabezpieczaj się przez !is.na(x) && albo redukuj przez any()/all().
  • ifelse(test, yes, no) to wersja wektorowa: używaj jej do wyliczania nowych wektorów i nie zagnieżdżaj więcej niż dwa razy.
  • switch() obsługuje wybór według nazw, z przejściem dalej przez puste przypadki. Zawsze dawaj mu wartość domyślną.

Dalej: robienie czegoś więcej niż raz, czyli pętle for i sytuacje, w których pętla jest (albo nie jest) właściwym narzędziem.

Najczęściej zadawane pytania

Jak napisać instrukcję if else w R?

if (condition) { ... } else { ... }: warunek w nawiasach okrągłych, każda gałąź w klamrowych. Kolejne sprawdzenia dołączasz przez else if (condition). W skrypcie else musi stać w tej samej linii co nawias zamykający poprzedniej gałęzi (} else {), inaczej R zgłasza unexpected 'else'.

Czym różni się if od ifelse() w R?

if to instrukcja sterująca, która sprawdza dokładnie jedną wartość TRUE/FALSE i uruchamia jedną gałąź kodu. ifelse(test, yes, no) to funkcja wektorowa: sprawdza naraz każdy element wektora i zwraca wektor wyników. Używaj if, żeby zdecydować, co robi program, a ifelse(), żeby wyliczyć nową kolumnę albo wektor z istniejącego.

Dlaczego R zgłasza 'the condition has length > 1'?

Przekazano cały wektor do if(), a ta instrukcja potrzebuje jednego TRUE albo FALSE. Od R 4.2 to błąd, a nie ciche użycie pierwszego elementu. Napraw to, redukując wektor do jednej wartości (any(x > 5), all(x > 5)), albo przejdź na wektorowe ifelse(), jeśli chodziło o wynik dla każdego elementu.

Jak działa switch() w R?

switch(value, name1 = result1, name2 = result2, default) porównuje wartość tekstową z nazwami argumentów i zwraca pasujący wynik. Nazwa z pustą prawą stroną przechodzi do kolejnego wyniku, więc kilka przypadków może mieć wspólny rezultat. Ostatni nienazwany argument działa jako wartość domyślna; bez niego niedopasowana wartość zwraca niewidocznie NULL.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ