forked from Pozdniakov/tidy_stats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path110-tidyverse_basic.qmd
599 lines (416 loc) · 35 KB
/
110-tidyverse_basic.qmd
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
# Введение в tidyverse {#sec-tidy_intro}
## Вселенная tidyverse {#sec-tidy_verse}
tidyverse - это не один, а целое множество пакетов. Есть ключевые пакеты (ядро тайдиверса), а есть побочные - в основном для работы со специфическими видами данных.
[*tidyverse*](https://www.tidyverse.org) --- это набор пакетов:
- *ggplot2*, для визуализации
- *tibble*, для работы с тибблами, продвинутый вариант датафрейма
- *tidyr*, для формата tidy data
- *readr*, для чтения файлов в R
- *purrr*, для функционального программирования (замена семейства функций \*apply())
- *dplyr*, для преобразованиия данных
- *stringr*, для работы со строковыми переменными
- *forcats*, для работы с переменными-факторами
Полезно также знать о следующих пакетах, не включенных в ядро, но также считающихся частью тайдиверса:
- *vroom*, для быстрой загрузки табоичных данных
- *readxl*, для чтения .xls и .xlsx
- *jsonlite*, для работы с JSON
- *xml*, для работы с XML
- *DBI*, для работы с базами данных
- *rvest*, для веб-скреппинга
- *lubridate*, для работы с временем
- *tidytext*, для работы с текстами и корпусами
- *glue*, для продвинутого объединения строк
- *magrtittr*, с несколькими вариантами pipe оператора
- *tidymodels*, для моделирования и машинного обучения[^110-tidyverse_basic-1]
- *dtplyr*, для ускорения `dplyr` за счет перевод синтаксиса на `data.table`
[^110-tidyverse_basic-1]: Как и пакет `tidyverse`, `tidymodels` --- это пакет с несколькими пакетами.
И это еще не все пакеты tidyverse! Есть еще много других небольших пакетов, которые тоже считаются частью tidyverse. Кроме официальных пакетов tidyverse есть множество пакетов, которые пытаются соответствовать принципам tidyverse и дополняют его.
Все пакеты tidyverse объединены tidy философией и взаимосовместимым синтаксисом. Это означает, что, во многих случаях даже не нужно думать о том, из какого именно пакета тайдиверса пришла функция. Можно просто установить и загрузить пакет `tidyverse`.
```{r, eval = FALSE}
install.packages("tidyverse")
```
Пакет `tidyverse` --- это такой [пакет с пакетами](https://cs11.pikabu.ru/post_img/big/2019/03/12/11/1552415351186680692.jpg).
```{r, message = TRUE}
library("tidyverse")
```
Подключение пакета `tidyverse` автоматически приводит к подключению ядра tidyverse, остальные же пакеты нужно подключать дополнительно при необходимости.
## Загрузка данных с помощью `readr`
Стандартной функцией для чтения `.csv` файлов в R является функция `read.csv()`, но мы будем использовать функцию `read_csv()` из пакета `readr`. Синтаксис функции `read_csv()` очень похож на `read.csv()`: первым аргументом является путь к файлу (в том числе можно использовать URL), некоторые остальные параметры тоже совпадают.
```{r, message = TRUE}
heroes <- read_csv("https://raw.githubusercontent.com/Pozdniakov/tidy_stats/master/data/heroes_information.csv",
na = c("-", "-99"))
```
Подробнее про импорт данных, в том числе в tidyverse, смотри в \@ref(real_data).
## tibble {#sec-tibble}
Когда мы загрузили данные с помощью `read_csv()`, то мы получили `tibble`, а не `data.frame`:
```{r}
class(heroes)
```
Тиббл (`tibble`) - это такой "усовершенствованный" `data.frame`. [Почти](https://www.jumpingrivers.com/blog/the-trouble-with-tibbles/) все, что работает с `data.frame`, работает и с тибблами. Однако у тибблов есть свои дополнительные фишки. Самая очевидная из них - более аккуратный вывод в консоль:
```{r}
heroes
```
Выводятся только первые 10 строк, если какие-то колонки не влезают на экран, то они просто перечислены внизу. Ну а тип данных написан прямо под названием колонки.
Функции различных пакетов tidyverse сами конвертируют в тиббл при необходимости. Если же нужно это сделать самостоятельно, то можно это сделать так:
```{r}
heroes_df <- as.data.frame(heroes) #создаем простой датафрейм
class(heroes_df)
as_tibble(heroes_df) #превращаем обратно в тиббл
```
> В дальнейшем мы будем работать только с tidyverse, а это значит, что только с тибблами, а не обычными датафреймами. Тем не менее, тибблы и датафреймы будут в дальнейшем использоваться как синонимы.
Можно создавать тибблы вручную с помощью функции `tibble()`, которая работает аналогично функции `data.frame()`:
```{r}
tibble(
a = 1:3,
b = letters[1:3]
)
```
## magrittr::`%>%` {#sec-pipe}
Оператор `%>%` называется "пайпом" (pipe), т.е. "трубой". Он означает, что следующая функция (справа от пайпа) принимает на вход в качестве первого аргумента результат выполнения предыдущей функции (той, что слева). Фактически, это примерно то же самое, что и вставлять результат выполнения функции в качестве первого аргумента в другую функцию. Просто выглядит это красивее и читабельнее. Как будто данные пропускаются через трубы функций или конвеерную ленту на заводе, если хотите. А то, что первый параметр функции - это почти всегда данные, работает нам здесь на руку. Этот оператор взят из пакета `magrittr`[^110-tidyverse_basic-2]. Возможно, даже если вы не захотите пользоваться tidyverse, использование пайпов Вам понравится.
[^110-tidyverse_basic-2]: Если быть точным, то оператор `%>%` был импортирован во все основные пакеты tidyverse, а сам пакет `magrittr` не входит в базовый набор tidyverse. Тем не менее, в самом `magrittr` есть еще несколько интересных операторов.
Важно понимать, что пайп не дает какой-то дополнительной функциональности или дополнительной скорости работы[^110-tidyverse_basic-3]. Он создан исключительно для читабельности и комфорта.
[^110-tidyverse_basic-3]: Даже наоборот, использование пайпов незначительно снижает скорость выполнения команды.
С помощью пайпов вот эту команду...
```{r}
sum(sqrt(abs(sin(1:22))))
```
...можно переписать вот так:
```{r}
1:22 %>%
sin() %>%
abs() %>%
sqrt() %>%
sum()
```
![](images/morning_pipe.png){width="400"}
В очень редких случаях результат выполнения функции нужно вставить не на первую позицию (или же мы хотим использовать его несколько раз). В этих случаях можно использовать `.`, чтобы обозначить, куда мы хотим вставить результат выполнения выражения слева от `%>%`.
```{r}
"Всем привет!" %>%
c("--", ., "--")
```
Основные функции в tidyverse ...
## Главные пакеты tidyverse: `dplyr` и `tidyr`
`dplyr`[^110-tidyverse_basic-4] --- это самая основа всего `tidyverse`. Этот пакет предоставляет основные функции для манипуляции с тибблами. Пакет `dplyr` является наследником и более усовершенствованной версией `plyr`, так что если увидите использование пакета `plyr`, то, скорее всего, скрипт был написан очень давно.
[^110-tidyverse_basic-4]: [Есть споры о том, как это правильно читать](https://community.rstudio.com/t/pronunciations-of-common-r-terms/1810). Используемые варианты: *диплаер*, *диплюр*, *диплир*.
Пакет `tidyr` дополняет `dplyr`, предоставляя полезные функции для тайдификации тибблов. Тайдификация ("аккуратизация") данных означает приведение табличных данных к такому формату, в котором:
- Каждая переменная имеет собственный столбец
- Каждый наблюдение имеет собственную строку
- Каждое значение имеет свою собственную ячейку
Впрочем, многие функции `dplyr` часто используются при тайдификации, так же как и многие функции `tidyr` имеет применение вне тайдификации. В общем, функционал этих двух пакетов несколько смешался, поэтому мы будем рассматривать их вместе. А чтобы представлять, какая функция относится к какому пакету (хотя запоминать это необязательно), я буду использовать запись с двумя двоеточиями `::`, которая обычно используется для использования функции без подгрузки всего пакета, при первом упоминании функции.
Пакет `tidyr` --- это более усовершенствованная версия пакета `reshape2`, который в свою очередь является усовершенствованной версией `reshape`. По аналогии с `plyr`, если вы видите использование этих пакетов, то это указывает на то, что перед вами морально устаревший код.
Код с использованием `dplyr` и `tidyr`сильно непохож на то, что мы видели раньше. Большинство функций `dplyr` и `tidyr` работают с целым тибблом сразу, принимая его в качестве первого аргумента и возвращая измененный тиббл. Это позволяет превратить весь код в последовательный набор применяемых функций, соединенный пайпами. На практике это выглядит очень элегантно, и вы в этом скоро убедитесь.
## Работа с колонками тиббла {#sec-tidy_select_cols}
### Выбор колонок: `dplyr::select()`
Функция `dplyr::select()` позволяет выбирать колонки по номеру или имени (кавычки не нужны).
```{r}
heroes %>%
select(1,5)
```
```{r}
heroes %>%
select(name, Race, Publisher, `Hair color`)
```
Обратите внимание, если в названии колонки присутствует пробел или, например, колонка начинается с цифры или точки и цифры, то это синтаксически невалидное имя (\@ref(variables)). Это не значит, что такие названия колонок недопустимы. Но такие названия колонок нужно обособлять \` грависом (правый штрих, на клавиатуре находится там же где и буква ё и \~).
Еще обратите внимание на то, что функции tidyverse не изменяют сами изначальные тибблы/датафреймы. Это означает, что если вы хотите полученный результат сохранить, то нужно добавить присвоение:
```{r}
heroes_some_cols <- heroes %>%
select(name, Race, Publisher, `Hair color`)
heroes_some_cols
```
### Мини-язык tidyselect для выбора колонок {#sec-tidyselect}
Для выбора столбцов (не только в `select()`, но и для других функций tidyverse) используется специальный мини-язык tidyselect из одноименного пакета[^110-tidyverse_basic-5]. tidyselect дает очень широкие возможности для выбора колонок.
[^110-tidyverse_basic-5]: Как и в случае с `magrittr`, пакет `tidyselect` не содержатся в базовом tidyverse, но функции импортируются основыми пакетами tidyverse.
Можно использовать оператор `:` для выбора нескольких соседних колонок (по аналогии с созданием числового вектора с шагом 1).
```{r}
heroes %>%
select(name:Publisher)
```
```{r}
heroes %>%
select(name:`Eye color`, Publisher:Weight)
```
Используя `!` можно вырезать ненужные колонки.
```{r}
heroes %>%
select(!...1)
heroes %>%
select(!(Gender:Height))
```
Другие известные нам логические операторы (`&` и `|`) тоже работают в tidyselect.
В дополнение к логическим операторам и `:`, в tidyselect есть набор вспомогательных функций, работающих исключительно в контексте выбора колонок с помощью tidyselect.
Вспомогательная функция `last_col()` позволит обратиться к последней колонке тиббла:
```{r}
heroes %>%
select(name:last_col())
```
А функция `everything()` позволяет выбрать все колонки.
```{r}
heroes %>%
select(everything())
```
При этом `everything()` не будет дублировать выбранные колонки, поэтому можно использовать `everything()` для перестановки колонок в тиббле:
```{r}
heroes %>%
select(name, Publisher, everything())
```
Впрочем, для перестановки колонок удобнее использовать специальную функцию `relocate()` (\@ref(tidy_relocate)) Можно даже выбирать колонки по паттернам в названиях. Например, с помощью `ends_with()` можно выбрать все колонки, заканчивающиеся одинаковым суффиксом:
```{r}
heroes %>%
select(ends_with("color"))
```
Аналогично, с помощью функции `starts_with()` можно найти колонки с одинаковым префиксом, с помощью `contains()` --- все колонки с выбранным паттерном в любой части названия колонки[^110-tidyverse_basic-6].
[^110-tidyverse_basic-6]: Выбранный паттерн будет найден посимвольно, если же вы хотите искать по регулярным выражениям, то вместо `contains()` нужно использовать `matches()`.
```{r}
heroes %>%
select(starts_with("Eye") & ends_with("color"))
heroes %>%
select(contains("eight"))
```
Ну и наконец, можно выбирать по содержимому колонок с помощью `where()`. Это напоминает применение `sapply()`(\@ref(apply_other)) на датафрейме для индексирования колонок: в качестве аргумента для `where` принимается функция, которая применяется для каждой из колонок, после чего выбираются только те колонки, для которых было получено `TRUE`.
```{r}
heroes %>%
select(where(is.numeric))
```
Функция `where()` дает невиданную мощь. Например, можно выбрать все колонки без `NA`:
```{r}
heroes %>%
select(where(function(x) !any(is.na(x))))
```
###Переименование колонок: `dplyr::rename()`
Внутри `select()` можно не только выбирать колонки, но и переименовывать их:
```{r}
heroes %>%
select(id = ...1)
```
Однако удобнее для этого использовать специальную функцию `dplyr::rename()`. Синтаксис у нее такой же, как и у `select()`, но `rename()` не выбрасывает колонки, которые не были упомянуты.
```{r}
heroes %>%
rename(id = ...1)
```
Для массового переименования колонок можно использовать функцию `rename_with()`. Эта функция так же использует tidyselect синтаксис для выбора колонок (по умолчанию выбираются все колонки) и применяет функцию в качестве аргумента, которая изменяет
```{r}
heroes %>%
rename_with(make.names)
```
###Перестановка колонок: `dplyr::relocate()` {#sec-tidy_relocate}
Для изменения порядка колонок можно использовать функцию `relocate()`. Она тоже работает похожим образом на `select()` и `rename()`[^110-tidyverse_basic-7]. Как и `rename()`, функция `relocate()` не выкидывает неиспользованные колонки:
[^110-tidyverse_basic-7]: `relocate()` не позволяет переименовывать колонки в отличие от `select()` и `rename()`
```{r}
heroes %>%
relocate(Publisher)
```
При этом `relocate()` имеет дополнительные параметры `.after =` и `.before =`, которые позволяют выбирать, куда поместить выбранные колонки.
```{r}
heroes %>%
relocate(Publisher, .after = name)
```
`relocate()` очень хорошо работает в сочетании с выбором колонок с помощью tidyselect. Например, можно передвинуть в одно место все колонки с одним типом данных:
```{r}
heroes %>%
relocate(Publisher, where(is.numeric), .after = name)
```
Последняя важная функция для выбора колонок --- `pull()`. Эта функция делает то же самое, что и индексирование с помощью `$`, т.е. вытаскивает из тиббла вектор с выбранным названием. Это лучше вписывается в логику tidyverse, поскольку позволяет извлечь колонку из тиббла с использованием пайпа:
```{r}
heroes %>%
select(Height) %>%
pull() %>%
head()
heroes %>%
pull(Height) %>%
head()
```
У функции `pull()` есть аргумент `name =`, который позволяет создать проименованный вектор:
```{r}
heroes %>%
pull(Height, name) %>%
head()
```
В отличие от базового R, tidyverse нигде не сокращает имплицитно результат вычислений до вектора, поэтому функция `pull()` - это основной способ извлечения колонки из тиббла как вектора.
## Работа со строками тиббла {#sec-tidy_select_rows}
### Выбор строк по номеру: `dplyr::slice()` {#sec-tidy_slice}
Начнем с выбора строк. Функция `dplyr::slice()` выбирает строчки по их числовому индексу.
```{r}
heroes %>%
slice(1:3)
```
### Выбор строк по условию: `dplyr::filter()` {#sec-tidy_filter}
Функция `dplyr::filter()` делает то же самое, что и `slice()`, но уже по условию. Причем для условий нужно использовать не векторы из тиббла, а название колонок (без кавычек) как будто бы они были переменными в окружении.
```{r}
heroes %>%
filter(Publisher == "DC Comics")
```
### Семейство функций `slice()` {#sec-slice_family}
У функции `slice()` есть множество родственников, которые объединяют функционал обычного `slice()` и `filter()`. Например, с помощью функций `dplyr::slice_max()` и `dplyr::slice_min()` можно выбрать заданное количество строк, содержащих наибольшие или наименьшие значения по колонке соответственно:
```{r}
heroes %>%
slice_max(Weight, n = 3)
heroes %>%
slice_min(Weight, n = 3)
```
Функция `slice_sample()` позволяет выбирать заданное количество случайных строчек:
```{r}
heroes %>%
slice_sample(n = 3)
```
Или же долю строчек:
```{r}
heroes %>%
slice_sample(prop = .01)
```
Если поставить значение параметра `prop =` равным `1`, то таким образом можно перемешать порядок строчек в тиббле:
```{r}
heroes %>%
slice_sample(prop = 1)
```
### Удаление строчек с NA: `tidyr::drop_na()` {#sec-tidy_drop_na}
Если нужно выбрать только строчки без пропущенных значений, то можно воспользоваться удобной функцией `tidyr::drop_na()`.
```{r}
heroes %>%
drop_na()
```
Можно выбрать колонки, наличие `NA` в которых будет приводить к удалению соответствующих строчек (не затрагивая другие строчки, в которых есть `NA` в остальных столбцах).
```{r}
heroes %>%
drop_na(Weight)
```
Для выбора колонок в `drop_na()` используется tidyselect, с которым мы недавно познакомились (\@ref(tidyselect)).
### Сортировка строк: `dplyr::arrange()` {#sec-tidy_arrange}
Функция `dplyr::arrange()` сортирует строчки от меньшего к большему (или по алфавиту - для текстовых значений) по выбранной колонке.
```{r}
heroes %>%
arrange(Weight)
```
Чтобы отсортировать в обратном порядке, воспользуйтесь функцией `desc()`.
```{r}
heroes %>%
arrange(desc(Weight))
```
Можно сортировать по нескольким колонкам сразу. В таких случаях удобно в качестве первой переменной выбирать переменную, обозначающую принадлежность к группе, а в качестве второй --- континуальную числовую переменную:
```{r}
heroes %>%
arrange(Gender, desc(Weight))
```
## Создание колонок: `dplyr::mutate()` и `dplyr::transmute()` {#sec-tidy_mutate}
Функция `dplyr::mutate()` позволяет создавать новые колонки в тиббле.
```{r}
heroes %>%
mutate(imt = Weight/(Height/100)^2) %>%
select(name, imt) %>%
arrange(desc(imt))
```
`dplyr::transmute()` - это аналог `mutate()`, который не только создает новые колонки, но и сразу же выкидывает все старые:
```{r}
heroes %>%
transmute(imt = Weight/(Height/100)^2)
```
Внутри `mutate()` и `transmute()` мы можем использовать либо векторизованные операции (длина новой колонки должна равняться длине датафрейма), либо операции, которые возвращают одно значение. В последнем случае значение будет одинаковым на всю колонку, т.е. будет работать правило ресайклинга (\@ref(recycling)):
```{r}
heroes %>%
transmute(name, weight_mean = mean(Weight, na.rm = TRUE))
```
Однако в функциях `mutate()` и `transmute()` правило ресайклинга не будет работать в остальных случаях: если полученный вектор будет не равен 1 или длине датафрейма, то мы получим ошибку.
```{r, error = TRUE}
heroes %>%
mutate(one_and_two = 1:2)
```
Это не баг, а фича: авторы пакета `dplyr` считают, что ресайклинг кратных друг другу векторов --- это слишком удобное место для выстрелов себе в ногу. Поэтому в таких случаях разработчики `dplyr` рекомендуют использовать функцию `rep()`, знакомую нам уже очень давно (\@ref(atomic)).
```{r}
heroes %>%
mutate(one_and_two = rep(1:2, length.out = nrow(.)))
```
## Агрегация данных в тиббле {#sec-tidy_aggregate}
### Подытоживание: `summarise()` {#sec-summarise}
Аггрегация по группам - это очень часто возникающая задача, например, это может использоваться для усреднения данных по испытуемым или условиям. Сделать аггрегацию в датафрейме удобной Хэдли Уикхэм пытался еще в предшественнике `dplyr`, пакете `plyr`. `dplyr` позволяет делать аггрегацию очень симпатичным и понятным способым. Аггрегация в `dplyr` состоит из двух этапов: группировки (`group_by()`) и подытоживания (`summarise()`). Начнем с последнего.
Функция `dplyr::summarise()`[^110-tidyverse_basic-8] позволяет аггрегировать данные в тиббле. Работает она очень похоже на `mutate()`, но если внутри `mutate()` используются векторизованные функции, возвращающие вектор такой же длины, что и колонки, использовавшиеся для расчетов, то в `summarise()` используются функции, которые возвращают вектор длиной 1. Например, `min()`, `mean()`, `max()` и т.д. Можно создавать несколько колонок через запятую (это работает и для `mutate()`).
[^110-tidyverse_basic-8]: У функции `dplyr::summarise()` есть синоним `dplyr::summarize()`, которая делает абсолбтно то же самое. Просто потому что в американском английском и британском английском это слово пишется по-разному.
```{r}
heroes %>%
mutate(imt = Weight/(Height/100)^2) %>%
summarise(min(imt, na.rm = TRUE),
max(imt, na.rm = TRUE))
```
В `dplyr` есть дополнительные суммирующие функции для более удобного индексирования в стиле tidyverse. Например, функции `dplyr::nth()`, `dplyr::first()` и `dplyr::last()`, которые позволяют вытаскивать значения из вектора по индексу (что-то вроде `slice()`, но для векторов)
```{r}
heroes %>%
mutate(imt = Weight/(Height/100)^2) %>%
arrange(imt) %>%
summarise(first = first(imt),
tenth = nth(imt, 10),
last = last(imt))
```
В отличие от `mutate()`, функции внутри `summarise()` вполне позволяют функциям внутри возвращать вектор из нескольких значений, создавая тиббл такой же длины, как и получившийся вектор.
```{r}
heroes %>%
mutate(imt = Weight/(Height/100)^2) %>%
summarise(imt_range = range(imt, na.rm = TRUE)) #функция range() возвращает вектор из двух значений: минимальное и максимальное
```
### Группировка: `group_by()` {#sec-tidy_group}
`dplyr::group_by()` - это функция для группировки данных в тиббле по дискретной переменной для дальнейшей аггрегации с помощью `summarise()`. После применения `group_by()` тиббл будет выглядеть так же, но у него появятся атрибут `groups`[^110-tidyverse_basic-9]:
[^110-tidyverse_basic-9]: Снять группировку можно с помощью функции `ungroup()`.
```{r}
heroes %>%
group_by(Gender)
```
Если после этого применить на тиббле функцию `summarise()`, то мы получим не тиббл длиной один, а тиббл со значением для каждой из групп.
```{r}
heroes %>%
mutate(imt = Weight/(Height/100)^2) %>%
group_by(Gender) %>%
summarise(min(imt, na.rm = TRUE),
max(imt, na.rm = TRUE))
```
Схематически это выглядит вот так:
![](images/group_by_s.png){width="400"}
### Подсчет строк: `dplyr::n()`, `dplyr::count()` {#sec-tidy_count}
Для подсчет количества значений можно воспользоваться функцией `n()`.
```{r}
heroes %>%
group_by(Gender) %>%
summarise(n = n())
```
Функция `n()` вместе с `group_by()` внутри `filter()` позволяет удобным образом "отрезать" от тиббла редкие группы...
```{r}
heroes %>%
group_by(Race) %>%
filter(n() > 10) %>%
select(name, Race)
```
или же наоборот, выделить только маленькие группы:
```{r}
heroes %>%
group_by(Race) %>%
filter(n() == 1) %>%
select(name, Race)
```
Таблицу частот можно создать без `group_by()` и `summarise(n = n())`. Функция `count()` заменяет эту конструкцию:
```{r}
heroes %>%
count(Gender)
```
Эту таблицу частот удобно сразу проранжировать, указав в параметре `sort =` значение `TRUE`.
```{r}
heroes %>%
count(Gender, sort = TRUE)
```
> Функция `count()`, несмотря на свою простоту, является одной из наиболее используемых в tidyverse.
### Уникальные значения: `dplyr::distinct()` {#sec-tidy_distinct}
`dplyr::distinct()` - это более быстрый аналог `unique()`, позволяет извлекать уникальные значения для одной или нескольких колонок.
```{r}
heroes %>%
distinct(Gender)
```
```{r}
heroes %>%
distinct(Gender, Race)
```
Иногда нужно аггрегировать данные, но при этом сохранить исходную структуру тиббла. Например, нужно посчитать размер групп или посчитать средние значения по группе для последующего сравнения с индивидуальными значениями.
### Создание колонок с группировкой {#sec-tidy_group_mutate}
В tidyverse это можно сделать с помощью сочетания `group_by()` и `mutate()` (вместо `summarise()`):
```{r}
heroes %>%
group_by(Race) %>%
mutate(Race_n = n()) %>%
select(Race, name, Gender, Race_n)
```
Результаты аггрегации были записаны в отдельную колонку, при этом значения этой колонки внутри одной группы повторяются:
![](images/group_by_m.png){width="400"}