Menu

If Else в R: if, else if, ifelse() и switch()

Условная логика в R — синтаксис if/else if/else, ловушка фигурных скобок, ломающая скрипты, векторизованный ifelse() для целых векторов и switch() для множественного выбора.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Конструкция if

Конструкция if в R выполняет блок кода только тогда, когда условие равно TRUE. Условие идёт в круглых скобках, код — в фигурных, а необязательный else задаёт, что происходит иначе:

Условие score >= 60 вычисляется в одиночное TRUE, поэтому выполняется первая ветка. Операторы сравнения и логические операторы==, >=, &&, %in% и им подобные — это то, из чего вы строите такие условия.

Для более чем двух исходов сцепляйте проверки через else if. R идёт по цепочке от первого условия вниз и выполняет первую ветку, чьё условие равно TRUE, пропуская остальные:

Порядок важен: поскольку 85 >= 80 проверяется раньше 85 >= 70, оценка равна "B". Если бы вы расставили проверки в обратном порядке, каждый балл выше 70 получил бы "C".

Ловушка фигурных скобок: где должен стоять else

Это ошибка, которая кусает почти каждого в первом же скрипте на R. В скрипте (или в чём угодно, запускаемом через Rscript) else должен стоять на той же строке, что и закрывающая скобка предыдущей ветки. Вот так не работает:

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

Почему: R разбирает инструкцию за инструкцией. Когда он доходит до } в начале строки, конструкция if синтаксически завершена, и R тут же её вычисляет. else на следующей строке приходит как начало новой инструкции — а инструкция не может начинаться с else. Решение — тот же приём, что используется на всей этой странице:

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

Внутри тела функции или любого внешнего набора фигурных скобок строгое правило смягчается (R продолжает чтение, пока не закроется внешняя скобка), но не полагайтесь на это — стиль } else { работает везде и именно его предписывают руководства по стилю R.

Условие должно быть одним TRUE или FALSE

if() требует ровно одно логическое значение. Два случая это нарушают, и оба являются ошибками:

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

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

Случай с NA важен на практике, потому что сравнения с пропущенными данными порождают NA: если x равно NA, то x > 10 тоже NA, и if, которому скормили такое условие, останавливает ваш скрипт. Защитный приём использует сокращённое вычисление &&:

Поскольку !is.na(x) равно FALSE, часть x > 10 никогда не вычисляется и никакой NA до условия не доходит. Как NA распространяется через сравнения, разобрано в статье про пропущенные значения.

Ошибка про длину больше 1 (действует с R 4.2; до этого R молча брал первый элемент) обычно означает, что вы имели в виду что-то из этого: any(v > 5), если достаточно одного совпадения, all(v > 5), если проверку должен пройти каждый элемент, или ifelse(), если вам нужен ответ для каждого элемента, — о нём следующий раздел.

ifelse(): векторизованный if else

if решает один раз. ifelse(test, yes, no) решает для каждого элемента вектора и возвращает вектор той же длины:

Каждый элемент temps проверяется независимо: "hot" "mild" "mild" "hot" "mild". Это инструмент для вывода нового вектора или столбца датафрейма из существующего — без всякого цикла.

В отличие от if, ifelse() аккуратно обращается с пропущенными значениями: NA в проверке даёт NA в результате, а не ошибку:

Вызовы ifelse() можно вкладывать для разбиения на три группы, и на такой глубине это ещё читаемо:

Но на этом остановитесь. Три и более вложенных ifelse() превращаются в головоломку из скобок; в этот момент берите cut() (для числовых диапазонов) или dplyr::case_when(), который размещает каждое условие на отдельной строке.

switch() для множественного выбора

Когда вы сопоставляете одно значение со списком известных имён, а не проверяете диапазоны, длинная цепочка else if — неуклюжий инструмент. switch() говорит об этом прямо:

Три поведения, которые нужно знать:

  • Сопоставление: switch() сравнивает символьное значение с именами аргументов и возвращает подходящий результат.
  • Проваливание: имя с пустой правой частью (sat = ,) проваливается к результату следующего случая — так "sat" и "sun" делят "weekend" без повторения.
  • Значение по умолчанию: последний безымянный аргумент ("weekday") возвращается, когда ничего не совпало. Без значения по умолчанию несовпавшее значение заставляет switch() невидимо вернуть NULL — ваша функция молча ничего не возвращает, и это сбивающий с толку баг. Всегда указывайте значение по умолчанию (или делайте его явным stop()).

switch() блистает внутри функций, которые ветвятся по аргументу режима или метода; для всего, что основано на числовых порогах, придерживайтесь else if или cut().

Что вы уносите с собой

  • if (condition) { } else { } — условие в круглых скобках, выполняется одна ветка; цепляйте через else if, побеждает первое совпадение.
  • В скриптах пишите } else { на одной строке — else в начале строки это синтаксическая ошибка.
  • if() нужно ровно одно TRUE/FALSE: NA — ошибка, вектор длиннее — ошибка (с R 4.2). Защищайтесь через !is.na(x) && или сворачивайте через any()/all().
  • ifelse(test, yes, no) — векторизованная версия; используйте её для вычисления новых векторов и не вкладывайте более двух раз.
  • switch() обслуживает множественный выбор по именам с проваливанием через пустые случаи — всегда давайте ему значение по умолчанию.

Дальше: как сделать что-то больше одного раза — циклы for и когда цикл является (и не является) правильным инструментом.

Часто задаваемые вопросы

Как написать конструкцию if else в R?

if (condition) { ... } else { ... } — условие идёт в круглых скобках, а каждая ветка в фигурных. Дальнейшие проверки цепляются через else if (condition). В скрипте else должен стоять на той же строке, что и закрывающая скобка предыдущей ветки (} else {), иначе R сообщит unexpected 'else'.

В чём разница между if и ifelse() в R?

if — конструкция управления потоком, которая проверяет ровно одно значение TRUE/FALSE и выполняет одну ветку кода. ifelse(test, yes, no) — векторизованная функция: она проверяет сразу каждый элемент вектора и возвращает вектор результатов. Используйте if, чтобы решить, что делает ваша программа; используйте ifelse(), чтобы вычислить новый столбец или вектор из существующего.

Почему R говорит «the condition has length > 1»?

Вы передали в if() целый вектор, а ему нужно одно TRUE или FALSE. Начиная с R 4.2 это ошибка, а не молчаливое использование первого элемента. Исправьте, сведя вектор к одному значению — any(x > 5), all(x > 5) — или перейдя на векторизованный ifelse(), если вам нужен результат для каждого элемента.

Как работает switch() в R?

switch(value, name1 = result1, name2 = result2, default) сравнивает символьное значение с именами аргументов и возвращает подходящий результат. Имя с пустой правой частью проваливается к следующему результату, поэтому несколько случаев могут разделять один исход. Последний безымянный аргумент работает как значение по умолчанию; без него несовпавшее значение невидимо возвращает NULL.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ