-
Notifications
You must be signed in to change notification settings - Fork 0
/
class02.Rmd
182 lines (137 loc) · 11 KB
/
class02.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
---
title: "Занятие 02: управляющие конструкции в R"
author: "Алла Тамбовцева"
output: html_document
---
### Конструкция `if-else` в R
Условные конструкции в R очень похожи на условные конструкции в Python. Есть оператор `if`, после которого формулируется условие и код, выполняемый в случае, если это условие верно, и есть оператор `else`, после которого пишется код, выполняемый в противном случае.
Главное отличие заключается в том, что в R для разграничения блоков кода вместо отступов для используются фигурные скобки.
```
if (condition){
# some code here
} else {
# some code here
}
```
Фигурные скобки обеспечивают связывание кода в единую программу:
если скобки поставлены верно, при запуске строки с `if` исполняется весь код до конца конструкции, то есть до `else` включительно.
Напишем код, который выводит на экран сообщение `Malo`, если оценка в переменной `mark` меньше 10, и сообщение `OK`, если оценка не ниже 10.
```{r}
mark <- 7
if (mark < 10){
print("Malo")
} else {
print("OK")
}
```
Теперь давайте сделаем наш код более реалистичным – добавим на оценку ограничение сверху. Добавим еще одно условие с `if` внутри ветки с `else`, которое будет проверять, не превышает ли оценка значение 10:
```{r}
mark <- 12
if (mark < 10){
print("Malo")
} else if (mark > 10){
print("Stranno")
} else {
print("OK")
}
```
Обратите внимание: готового оператора `elif`, как в Python, в R нет, только `if` плюс `else`.
В продолжение примера с оценками давайте напишем программу, которая определяет, является ли оценка отличной, хорошей, удовлетворительной или плохой. Реализуем ее через серию условий `if`:
```{r}
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("Странно...")
}
```
**Важно:** последнее условие у нас тоже написано через `if`, если напишем `else`, он будет относиться только к последнему условию с `if`, а не ко всем условиям выше, и программа будет работать не так, как нужно.
### Проверка условий на векторах
Для того чтобы проверить выполнение некоторого условия сразу для всех элементов вектора (столбца в таблице), совсем не обязательно писать цикл и проверять условие для каждого элемента. Достаточно воспользоваться функцией `ifselse()`. На первом месте в аргументах этой функции указывается условие, которое мы проверяем, на втором – значение, которое должно возвращаться в случае, если условие выполняется, на третьем – значение, возвращаемое в случае невыполнения условия. Это функция удобна для получения новых векторов (столбцов в таблице) на основе старых. Получим вместо `yes` и `no` набор из 1 и 0:
```{r}
answers <- c("yes", "no", "no", "yes")
ifelse(answers == "yes", 1, 0)
```
Для условий, состоящих из нескольких частей, эта функция тоже подойдет:
```{r}
v <- c(-0.6, 0.1, 0.5, 0.8, 2, 3)
binary <- ifelse(v > 1 | v < 0, "invalid", "valid")
binary
```
Если на выходе мы хотим получить вектор, состоящий более, чем из двух значений, придется использовать `ifelse()` несколько раз. Для примера напишем код, который вместо пропущенного значения `NA` будет выставлять 99, а в остальных случаях записывать 1 для положительных значений и 0 — для отрицательных и нулевых:
```{r}
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))
```
Это не очень удобно, но для разбиения значений на категории можно использовать более продвинутые функции из системы библиотек `tidyverse`, о них мы поговорим позже.
### Цикл `for` в R
Обычно цикл `for` используется для повторения операций фиксированное число раз. В R цикл `for` выполняет ту же задачу, однако не всегда использование этого цикла действительно необходимо. Дело в том, что основной структурой данных в R является вектор, а это значит, что многие операции являются векторизованными: применяя операцию к вектору, мы автоматически применяем ее сразу ко всем его элементам.
Например, для домножения всех элементов вектора на число цикл нам не понадобится:
```{r}
v <- c(-0.6, 0.1, 0.5, 0.8, 2, 3)
v ** 2
```
То же самое будет работать и с несколькими векторами одинаковой длины:
```{r}
# попарно складываем все элементы
v1 <- c(4, 7, 8, 9, 0)
v2 <- c(1, 2, 3, -1, 0)
v1 + v2
```
```{r}
# считаем математическое ожидание
# по вектору значений и вероятностей
x <- c(0, 3, 6)
p <- c(0.2, 0.5, 0.3)
sum(x * p)
```
Однако, если мы работаем не с готовыми векторами, цикл `for` все же понадобится. Например, для перебора файлов в какой-нибудь папке. Представим себе такую ситуацию: у нас есть набор CSV-файлов с одинаковой структурой (один и тот же опрос в разное время, одни и те же индексы по разным странам, результаты выборов в разных регионах), и мы хотим из каждого файла извлечь столбец с одним и тем же названием и построить для него какой-нибудь график. Тут-то и пригодится цикл `for`.
Давайте для простоты пока не будем брать файлы с данными, а просто создадим вектор с реалистичными названиями файлов:
```{r}
files <- c("France.csv",
"Spain.csv",
"Slovenia.csv",
"Brazil.csv")
```
Переберем названия файлов в цикле и выведем их названия на экран (как обычно в циклах, вместо `f` мы могли вписать любое название):
```{r}
for (f in files){
print(f)
}
```
Теперь рассмотрим задачу поинтереснее. Предположим, дальше для данных из каждого файла мы будем строить графики. И мы хотим, чтобы заголовки графиков соответствовали названиям стран. Чтобы это автоматизировать, нам потребуется код, который будет извлекать название страны из названия CSV-файла. Начнем с одного названия, сохраним его как `f`:
```{r}
f <- files[1]
```
Сначала название файла нужно разбить по точке. Для разбиения строки на части в R есть функция `strsplit()`:
```{r}
strsplit(f, "\\.")
```
Почему в функции выше вместо разделителя `.` у нас указано `\\.`? Многие функции для работы со строками в R автоматически поддерживают работу с регулярными выражениями для поиска и изменения текста по заданному шаблону (почитать можно здесь и здесь), а в регулярных обозначениях точка является специальным символом. В регулярных выражениях точка означает любой символ. То есть, выражение `т.к` будет находить слова *ток*, *тик*, *так*, *тук* или даже *т9к*. Поэтому, чтобы R понимал точку как точку, ее пришлось экранировать — выделить с помощью `\\`.
Функция `strsplit()` возвращает результат в виде списка, заберем из этого списка единственный вектор с индексом 1:
```{r}
strsplit(f, "\\.")[[1]]
```
Теперь из этого вектора извлечем первый элемент — название страны и сделаем все буквы заглавными:
```{r}
name <- toupper(strsplit(f, "\\.")[[1]][1])
name
```
Осталось применять эту операцию в цикле и посмотреть на результаты:
```{r}
for (f in files){
name <- toupper(strsplit(f, "\\.")[[1]][1])
print(name)
}
```
Отлично! Запомним эти манипуляции со строками, они нам понадобятся в практической части!