if-else
в RУсловные конструкции в R очень похожи на условные конструкции в Python. Есть оператор if
, после которого формулируется условие и код, выполняемый в случае, если это условие верно, и есть оператор else
, после которого пишется код, выполняемый в противном случае.
Главное отличие заключается в том, что в R для разграничения блоков кода вместо отступов для используются фигурные скобки.
if (condition){
# some code here
} else {
# some code here
}
Фигурные скобки обеспечивают связывание кода в единую программу: если скобки поставлены верно, при запуске строки с if
исполняется весь код до конца конструкции, то есть до else
включительно.
Напишем код, который выводит на экран сообщение Malo
, если оценка в переменной mark
меньше 10, и сообщение OK
, если оценка не ниже 10.
mark <- 7
if (mark < 10){
print("Malo")
} else {
print("OK")
}
## [1] "Malo"
Теперь давайте сделаем наш код более реалистичным – добавим на оценку ограничение сверху. Добавим еще одно условие с if
внутри ветки с else
, которое будет проверять, не превышает ли оценка значение 10:
mark <- 12
if (mark < 10){
print("Malo")
} else if (mark > 10){
print("Stranno")
} else {
print("OK")
}
## [1] "Stranno"
Обратите внимание: готового оператора elif
, как в Python, в R нет, только if
плюс else
.
В продолжение примера с оценками давайте напишем программу, которая определяет, является ли оценка отличной, хорошей, удовлетворительной или плохой. Реализуем ее через серию условий if
:
if (mark >= 8 & mark <= 10){
print("Отлично")
}
if (mark >= 6 & mark < 8){
print("Хорошо")
}
if (mark >= 4 & mark < 6){
print("Удовлетворительно")
}
if (mark < 4 & mark >= 0){
print("Плохо")
}
if (mark > 10){
print("Странно...")
}
## [1] "Странно..."
Важно: последнее условие у нас тоже написано через if
, если напишем else
, он будет относиться только к последнему условию с if
, а не ко всем условиям выше, и программа будет работать не так, как нужно.
Для того чтобы проверить выполнение некоторого условия сразу для всех элементов вектора (столбца в таблице), совсем не обязательно писать цикл и проверять условие для каждого элемента. Достаточно воспользоваться функцией ifselse()
. На первом месте в аргументах этой функции указывается условие, которое мы проверяем, на втором – значение, которое должно возвращаться в случае, если условие выполняется, на третьем – значение, возвращаемое в случае невыполнения условия. Это функция удобна для получения новых векторов (столбцов в таблице) на основе старых. Получим вместо yes
и no
набор из 1 и 0:
answers <- c("yes", "no", "no", "yes")
ifelse(answers == "yes", 1, 0)
## [1] 1 0 0 1
Для условий, состоящих из нескольких частей, эта функция тоже подойдет:
v <- c(-0.6, 0.1, 0.5, 0.8, 2, 3)
binary <- ifelse(v > 1 | v < 0, "invalid", "valid")
binary
## [1] "invalid" "valid" "valid" "valid" "invalid" "invalid"
Если на выходе мы хотим получить вектор, состоящий более, чем из двух значений, придется использовать ifelse()
несколько раз. Для примера напишем код, который вместо пропущенного значения NA
будет выставлять 99, а в остальных случаях записывать 1 для положительных значений и 0 — для отрицательных и нулевых:
w <- c(4, 6, 7, 0, -1, -5, 10, 21, NA)
# is.na(): TRUE для NA, FALSE для остального
ifelse(is.na(w), 99, ifelse(w > 0, 1, 0))
## [1] 1 1 1 0 0 0 1 1 99
Это не очень удобно, но для разбиения значений на категории можно использовать более продвинутые функции из системы библиотек tidyverse
, о них мы поговорим позже.
for
в RОбычно цикл for
используется для повторения операций фиксированное число раз. В R цикл for
выполняет ту же задачу, однако не всегда использование этого цикла действительно необходимо. Дело в том, что основной структурой данных в R является вектор, а это значит, что многие операции являются векторизованными: применяя операцию к вектору, мы автоматически применяем ее сразу ко всем его элементам.
Например, для домножения всех элементов вектора на число цикл нам не понадобится:
v <- c(-0.6, 0.1, 0.5, 0.8, 2, 3)
v ** 2
## [1] 0.36 0.01 0.25 0.64 4.00 9.00
То же самое будет работать и с несколькими векторами одинаковой длины:
# попарно складываем все элементы
v1 <- c(4, 7, 8, 9, 0)
v2 <- c(1, 2, 3, -1, 0)
v1 + v2
## [1] 5 9 11 8 0
# считаем математическое ожидание
# по вектору значений и вероятностей
x <- c(0, 3, 6)
p <- c(0.2, 0.5, 0.3)
sum(x * p)
## [1] 3.3
Однако, если мы работаем не с готовыми векторами, цикл for
все же понадобится. Например, для перебора файлов в какой-нибудь папке. Представим себе такую ситуацию: у нас есть набор CSV-файлов с одинаковой структурой (один и тот же опрос в разное время, одни и те же индексы по разным странам, результаты выборов в разных регионах), и мы хотим из каждого файла извлечь столбец с одним и тем же названием и построить для него какой-нибудь график. Тут-то и пригодится цикл for
.
Давайте для простоты пока не будем брать файлы с данными, а просто создадим вектор с реалистичными названиями файлов:
files <- c("France.csv",
"Spain.csv",
"Slovenia.csv",
"Brazil.csv")
Переберем названия файлов в цикле и выведем их названия на экран (как обычно в циклах, вместо f
мы могли вписать любое название):
for (f in files){
print(f)
}
## [1] "France.csv"
## [1] "Spain.csv"
## [1] "Slovenia.csv"
## [1] "Brazil.csv"
Теперь рассмотрим задачу поинтереснее. Предположим, дальше для данных из каждого файла мы будем строить графики. И мы хотим, чтобы заголовки графиков соответствовали названиям стран. Чтобы это автоматизировать, нам потребуется код, который будет извлекать название страны из названия CSV-файла. Начнем с одного названия, сохраним его как f
:
f <- files[1]
Сначала название файла нужно разбить по точке. Для разбиения строки на части в R есть функция strsplit()
:
strsplit(f, "\\.")
## [[1]]
## [1] "France" "csv"
Почему в функции выше вместо разделителя .
у нас указано \\.
? Многие функции для работы со строками в R автоматически поддерживают работу с регулярными выражениями для поиска и изменения текста по заданному шаблону (почитать можно здесь и здесь), а в регулярных обозначениях точка является специальным символом. В регулярных выражениях точка означает любой символ. То есть, выражение т.к
будет находить слова ток, тик, так, тук или даже т9к. Поэтому, чтобы R понимал точку как точку, ее пришлось экранировать — выделить с помощью \\
.
Функция strsplit()
возвращает результат в виде списка, заберем из этого списка единственный вектор с индексом 1:
strsplit(f, "\\.")[[1]]
## [1] "France" "csv"
Теперь из этого вектора извлечем первый элемент — название страны и сделаем все буквы заглавными:
name <- toupper(strsplit(f, "\\.")[[1]][1])
name
## [1] "FRANCE"
Осталось применять эту операцию в цикле и посмотреть на результаты:
for (f in files){
name <- toupper(strsplit(f, "\\.")[[1]][1])
print(name)
}
## [1] "FRANCE"
## [1] "SPAIN"
## [1] "SLOVENIA"
## [1] "BRAZIL"
Отлично! Запомним эти манипуляции со строками, они нам понадобятся в практической части!