Конструкция 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.