-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy path03-Tables.qmd
773 lines (544 loc) · 78.6 KB
/
03-Tables.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
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
# Таблицы {#tables}
## Предварительные требования {#tables_prerequisites}
Для работы по теме текущей лекции вам понадобятся пакеты из **tidyverse**, а также **writexl**. Установите их, используя следующую команду:
```{r, eval=FALSE}
install.packages('tidyverse')
install.packages('writexl')
```
> Внимание: установка пакетов выполняется один раз из консоли. Вызов функции `install.packages()` не должен присутствовать в ваших скриптах
[**tidyverse**](https://www.tidyverse.org) -- это не самостоятельный пакет, а набор пакетов R, которые позволяют автоматизировать решение рутинных задач по обработке данных (то, что принято называть *data science*). В комплект **tidyverse** входят следующие пакеты:
| Пакет | Назначение |
|-------------|-----------------------------------------------------------|
| **tibble** | Усовершенствованный вариант фрейма данных |
| **dplyr** | Грамматика манипуляций над табличными данными |
| **tidyr** | Приведение таблиц в аккуратный вид, удобный для обработки |
| **readr** | Чтение табличных данных из текстовых файлов |
| **readxl** | Чтение табличных данных из файлов Microsoft Excel |
| **haven** | Чтение табличных данных из файлов SPSS, Stata и SAS |
| **purrr** | Функциональное программирование |
| **stringr** | Работа со строками |
| **forcats** | Автоматизация работы с факторами |
| **ggplot2** | Построение графиков на основе правил грамматики |
Вы можете подключать эти пакеты по одному, или сделать их все доступными в текущей сессии **R**, используя команду `library(tidyverse)`. В текущей лекции мы будем подключать их по мере необходмости, чтобы акцентировать внимание на принадлежности функций к соответствующим пакетам.
> **Вы можете вызвать функцию из любого пакета, не подключая его целиком в текущую сессию R.** Это бывает особенно полезно, когда вы редко используете функции из пакета. В этом случае вызов функции будет выглядеть как `package::function()`, где `package` -- название пакета, а `function` - название функции. Подобный синтаксис бывает особенно удобным, когда *в разных пакетах имеются функции с одинаковым именем*, и при вызове R использует не ту, которая нужна (по умолчанию будет использована функция из пакета, который был подключен *позже*).
## Структуры данных {#tables_datastructures}
Стандартным средством представления табличных данных в R являются **фреймы данных** (`data.frame`), кратко рассмотренные в предыдущей лекции. В современных пакетах типа **tidyverse** используется разновидность фрейма данных, которая называется **тиббл** (`tibble`).
Создать тиббл можно напрямую, либо путем конвертации фрейма данных:
```{r}
library(readxl)
library(writexl)
library(tidyverse)
tibble(
a = 1:3,
b = 1,
c = -1:1
)
dfr = data.frame(a = 1:3, b = 1, c = -1:1)
as_tibble(dfr)
```
Тиббл является расширением класса фрейма данных, то есть любая операция, применимая к фрейму, применима и к тибблу. Однако, тиббл поддерживает дополнительные возможности, которые оказываются очень удобны в анализе:
- При выводе в консоль тиббл печатает только те столбцы, которые помещаются на экран, и только первые 10 строк (что позволяет избежать переполнения консоли при печати больших таблиц)
- Тибблы поддерживают имена столбцов, которые недопустимы в фреймах данных (например, начинающиеся с цифры). Так называть столбцы, вообще говоря, неправильно, но это позволяет сохранить именно те названия, которые даны в исходных файлах. Например, географические таблицы часто содержат данные за разные года, и столбцы названы по этим годам.
- Тибблы поддерживают внутренние группировки данных. Установив группировку по одной или нескольким переменным, можно эффективным и компактным путем считать агрегирующие статистики по группам измерений.
- Тибблы можно создавать вручную не только по столбцам, но и по строкам.
Для реализации последней возможности можно использовать функцию `tribble()` (переводится как *transposed tibble* --- транспонированный тиббл), указав имена столбцов с помощью тильды (`~`):
```{r}
tribble(
~a, ~b, ~c,
1, 1, -1,
2, 1, 0,
3, 1, 1
)
```
> В данной и последующих лекциях понятия мы будем использовать понятия таблица, фрейм данных и тиббл как взаимозаменяемые.
## Чтение {#tables_reading}
Существует множество способов получить набор табличных данных в текущей сессии R. Эти способы варьируются от загрузки данных из пакета до извлечения таблиц из текстовых документов и веб-страниц. В настоящей главе мы рассмотрим наиболее распространенные способы, нацеленные на работу с готовыми таблицами.
### Встроенные данные {#tables_reading_inner}
Пакеты R часто содержат тестовые наборы данных. Эти данные, как правило, предназначены для ознакомления с возможностями пакета. Чтобы узнать, какие данные есть в пакете, вы можете вызвать функцию `data(package = 'packagename')`, где `packagename` --- это имя интересующего вас пакета. Например, посмотрим, какие данные есть в пакете `dplyr`, который мы далее будем использовать для манипуляций с таблицами:
```{r}
data(package = 'dplyr')
```
![*Данные, доступные в пакете dplyr*](images/data_dplyr.png)
На рисунке можно видеть перечень наборов данных и их краткие описания. Для загрузки набора данных передайте его название в качестве первого параметра функции `data()`. Ну-ка, что там с персонажами из *Star Wars*:
```{r}
data(starwars, package = 'dplyr')
starwars
```
> Обратите внимание, что после подключения набора данных он становится доступным в текущей сесси R именно с тем именем, с которым он сохранен в пакете
Если вызвать функцию `data()` без параметров, будет выведен список данных со всех пакетов, которые подключены в текущей сессии R:
```{r}
data()
```
![*Данные, доступные в текущей сессии R*](images/data.png)
По умолчанию в RStudio всегда подключен пакет *datasets*, который устанавливается вместе с базовым дистрибутивом R. Если пакет подключен в текущую сессию, то можно получить набор данных по его имени, не указывая название пакета. Например, в пакете *datasets* есть набор данных `quakes` о землетрясениях на о. Фиджи:
```{r}
data(quakes)
head(quakes) # просмотрим макушку таблицы
```
Таким образом, если вы используйете пакет `dplyr` в своей программе, данные о героях Звездных Войн можно загрузить не указывая пакет, т.к. он был ранее подключен через функцию `library()`:
```{r}
data(starwars)
```
### Текстовые таблицы {#tables_reading_text}
Текстовые таблицы бывают, как правило, двух типов: с разделителем (CSV) и с фиксированной шириной столбца.
#### Файлы с разделителем {#tables_reading_text_csv}
**CSV (Comma-separated value)** --- общее название для формата представления таблиц в виде текстовых файлов, организованных по следующему принципу:
1. Каждая строка в файле соответствует строке в таблице.
2. Ячейки отделяются друг от друга символом-разделителем.
3. Если ячейка пустая, то между соседними разделителями не должно быть никаких символов.
Стандартным разделителем ячеек является запятая (`,`), а десятичным разделителем --- точка (`.`). Однако это не является строгим правилом. Например, в ряде локалей (например, русской) запятая используется в качестве десятичного разделителя, поэтому колонки часто разделяют точкой с запятой (`;`).
Формат CSV никак не оговаривает наличие заголовочной строки с названиями столбцов в начале файла — она может как отсутствовать, так и присутствовать. Поэтому при чтении таблиц из файлов необходимо информировать программу о наличии заголовка путем указания соответствующего параметра.
> Любая функция, используемая вами для чтения файлов, вне зависимости от пакета, который вы используете, как правило, содержит параметры, с помощью которых можно задать символы, отвечающие за десятичный разделитель и разделитель столбцов, а также наличие или отсутствие строки-заголовка и кодировку файла. Если таблица читается некорректно, ознакомьтесь со справкой к функции и при необходимости замените стандартные значения этих параметров на те, что соответствуют формату вашей таблицы.
Например, вот так выглядит текстовая таблица в формате CSV с данными по численности населения в Федеральных округах Российской Федерации за 2005 и 2010-2013 гг. (по данным Росстата):
```
N,Region,Year05,Year10,Year11,Year12,Year13
1,Центральный,4341,3761,3613,3651,3570
2,Северо-Западный,3192,3088,2866,2877,2796
3,Южный федеральный,1409,1446,1436,1394,1321
4,Северо-Кавказский,496,390,397,395,374
5,Приволжский,3162,2883,2857,2854,2849
6,Уральский,1681,1860,1834,1665,1624
7,Сибирский,2575,2218,2142,2077,1941
8,Дальневосточный,871,870,821,765,713
```
Таблицы в формате **CSV** можно прочесть как с помощью стандартных средств языка R, так и с помощью пакета `readr`, который входит в набор пакетов *tidyverse*. Мы будем использовать последние, а стандартные средства языка оставим на самостоятельное изучение.
Для чтения таблиц с разделителем в readr имеется несколько функций:
- `read_csv()` читает файлы с разделителем запятой
- `read_csv2()` читайт файоы с разделителем точкой-с-запятой (может быть особенно актуально для русских файлов)
- `read_tsv()` читает файлы с разделителем табуляцией или пробелом
- `read_delim()` читает файлы с произвольным разделителем (указывается в качестве параметра)
Вышеуказанный файл сохранен с разделителем запятой, поэтому мы можем прочесть его посредством первой функции из списка:
```{r}
(okruga = read_csv('data/okruga.csv'))
```
Как видно, функции пакета `readr` выдают диагностическую информацию о том, к какому типу были приведены столбцы таблицы при чтении. Помимо этого, первая строка была использована в качестве заголовочной.
#### Файлы с фиксированной шириной столбца {#tables-reading-text-fwf}
В файлах с фиксированной шириной на каждый столбец резервируется определенное количество символов. При этом данные выравниваются по правому краю, а короткие строки отбиваются слева пробелами. Такой формат часто используется в численных моделях (например, метеорологических) для представления входных данных или результатов расчетов. Например, файл ниже содержит данные об энергии ветра ($Вт/м^2$) на высотах 50 и 110 м по точкам вдоль побережья Черного моря<!-- TODO: Дать ссылку на статью -->:
```
1 43.500000 28.000000 111.05298 178.41447
2 43.500000 28.500000 187.38620 301.05331
3 44.000000 28.500000 168.82031 271.22421
4 44.500000 28.500000 157.22586 252.59746
5 44.500000 29.000000 189.46452 304.39597
6 45.000000 29.000000 170.40709 273.77536
7 45.000000 29.500000 198.92389 319.58777
8 45.500000 29.500000 188.64406 303.07242
9 46.000000 30.000000 180.10541 289.35379
10 46.000000 30.500000 207.91818 334.03564
```
Для чтения таких файлов в **readr** есть функции:
- `read_fwf()` читает файлы с фиксированной шириной столбца, позволяя задавать ширины столбцов (через `fwf_widths()`) или начальные позиции каждого столбца (через `fwf_positions()`)
- `read_table()` читает наиболее распространенный вариант файла с фиксированной шириной столбца, в котором колонки разделены пробелами. Позиции столбцов определяются автоматически, что очень удобно.
Прочитаем вышеприведенный файл с данными о ветровой энергии:
```{r}
(wenergy = read_table('data/wind_energy.txt', col_names = c('id', 'lat', 'lon', 'energy50', 'energy110')))
```
### Таблицы Microsoft Excel {#tables_reading_excel}
Для чтения таблиц Microsoft Excel, так же как и для текcтовых файлов, существуют множество пакетов, таких как [xlsx](https://cran.r-project.org/web/packages/xlsx/index.html), [openxlsx](https://cran.r-project.org/web/packages/openxlsx/index.html) и [readxl](https://cran.r-project.org/web/packages/readxl/index.html).
D настоящем курсе мы будем пользоваться пакетом **readxl**, поскольку он не имеет внешних зависимостей, а его функции концептуально идентичны функциям пакета **readr**. Прочтем данные о лесовосстановлении (в тысяч га), полученные из регионального ежегодника Росстата за 2017 год. Эта таблица содержит колонку с названием субъекта и еще 8 колонок с данными по годам. Поскольку в таблице есть пропущенные значения, необходимо определить типы столбцов (в противном случае они могут быть определены как текстовые):
```{r}
(reforest = read_excel('data/reforest.xlsx',
col_types = c('text', rep('numeric', 8))))
```
### Параметры {#tables_reading_params}
Функции пакетов `readr` и `readxl` имеют идентичный набор параметров, позволяющих управлять процедурой чтения данных (многоточие используется вместо перечисления параметров):
- `skip = n` позволяет пропустить первые n строк таблицы (например, если в них содержатся какие-то комментарии)
- `col_names = FALSE` позволяет не интерпретировать первую строку как заголовочную (вместо этого она будет читаться как строка с данными)
- `col_names = c(...)` позволяет задать имена столбцов (удобно, если в файле они длинные)
- `col_types = cols(...)` позволяет задать типы столбцов (необходимо, если функция неправильно определяет их сама)
- `na = '-'` позволяет указать символ, который используется для указания пропущенных значений (в данном случае указан прочерк-дефис)
- `locale = locale(...)` управляет локалью (в том числе позволяет указать кодировку файла)
> **Стандартной кодировкой для представления текста** в UNIX-подобных системах (*Ubuntu*, *macOS* и т.д.) является **UTF-8 (Unicode)**, в русскоязычных версиях *Windows* --- **CP1251 (Windows-1251)**. Текстовый файл **CSV**, созданный в разных операционных системах, будет по умолчанию сохраняться в соответствующей кодировке, если вы не указали ее явным образом. Если при загрузке таблицы в **R** вы видите вместо текста нечитаемые символы --- *кракозябры* --- то, скорее всего, вы читаете файл не в той кодировке, в которой он был сохранен. Если вы не знаете, что такое кодировка и Юникод, то вам [сюда](https://ru.wikipedia.org/wiki/Набор_символов)
По умолчанию файлы читаются в той кодировке, которая соответствует операционной системе, на которой запущен R. Если файл создан в другой кодировке, придется указать ее при чтении. Например, вы пользуетесь macOS (UTF-8), а ваш коллега --- Windows (CP1251), то для чтения созданного им файла вам, скорее всего, понадобится указать что-то вроде `locale = locale(encoding = 'CP1251')`
## Просмотр {#tables_view}
Для просмотра фрейма данных в консоли **RStudio** вы можете использовать несколько опций. Пусть наш фрейм данных называется `reforest`. Тогда:
1. `print(reforest)` --- выводит фрейм в консоль целиком (можно написать просто `tab` в консоли).
2. `head(reforest, n)` --- отбирает первые $n$ строк фрейма
3. `tail(reforest, n)` --- отбирает последние $n$ строк фрейма
По умолчанию для функций `head()` и `tail()` $n=6$. Обычно этот параметр опускают, поскольку нужно просмотреть только первые несколько строк и шести вполне достаточно. Если вы напечатаете в консоли `head(reforest)` или `tail(reforest)`, то для выбранных строк будет вызвана функция `print()`, аналогично выводу всего фрейма:
```{r, collapse=TRUE}
# ПРОСМОТР ТАБЛИЦЫ
print(reforest)
head(reforest)
tail(reforest)
```
`RStudio` предоставляет графический интерфейс для просмотра таблиц, в котором таблицу можно сортировать и фильтровать. Чтобы его активировать, надо вызвать функцию `View()`:
```{r, eval = FALSE, collapse=TRUE}
View(tab)
```
Поскольку функции `head()` и `tail()` возвращают строки с хвоста или начала фрейма данных, их можно и подать на вход функции `View()`:
```{r, eval = FALSE, collapse=TRUE}
View(head(reforest, 3))
```
> **Как правило, не следует оставлять вызовы функции `View()` в тексте законченной программы.** Это приведет к тому, что при запуске будут открываться новые вкладки с просмотром таблиц, что может раздражать пользователя (в том числе и вас самих). Используйте `View()` для вывода окончательного результата в конце программы или при отладке программы. Все вызовы `View()` в программе можно легко закомментировать или раскомментировать, выполнив поиск с заменой `'View('` на `'# View('` и наоборот.
## Столбцы, строки и ячейки {#tables_elements}
### Названия {#tables_elements_names}
Столбцы, строки и ячейк представляют собой основные структурные элементы фрейма данных или тиббла. Перед тем как мы поднимемся на уровень выше и рассмотрим обобщенные операции преобразования таблиц, необходимо посмотреть, как извлекать структурные элементы таблиц.
Столбцы и строки таблицы имеют названия, которые можно читать и записывать с помощью функций `colnames()` и `rownames()`:
```{r}
# Чтение названий столбцов и строк
colnames(okruga)
rownames(okruga)
# Замена названий столбцов и строк
colnames(okruga) <- c("N", "Region", "Year05", "Year10", "Year11", "Year12", "Year13")
print(okruga)
```
Названия строк редко заменяются, поскольку с точки зрения реляционной алгебры большого смысла они не имеют.
### Обращение к столбцам {#tables_elements_columns}
К столбцу можно обращаться по номеру и названию (с помощью оператора `$` или в кавычках внутри скобок). Если вы указываете в квадратных скобках номер без запятой, он трактуется именно как номер столбца, а не строки. Тип возвращаемого значения зависит от синтаксиса:
- обращение через `$` возвращает вектор;
- обращение в скобках с запятой к одному столбцу возвращает вектор;
- обращение в скобках с запятой к нескольким столбцам возвращает фрейм данных;
- обращение в скобках без запятой возвращает фрейм данных.
Несколько примеров:
```{r, eval=FALSE, collapse=TRUE}
# Один столбец - результат зависит от запятой
okruga$Year05 # столбец в виде вектора
okruga[, "Year05"] # столбец в виде вектора
okruga[, 2] # столбец в виде вектора
okruga["Year05"] # столбец в виде фрейма данных/тиббла
okruga[2] # столбец в виде фрейма данных/тиббла
# Несколько столбцов - всегда фрейм данных/тиббл
okruga[, c(1, 4)]
okruga[, c("Region", "Year11")]
okruga[c("Region", "Year11")]
okruga[c(1, 4)]
```
### Обращение к строкам {#tables_elements_rows}
Обращаться к строкам можно по их номерам. В этом случае в качестве индекса можно передать номер (номера) интересующих строк, либо вектор логических значений, в котором интересующие строки помечены как TRUE, а остальные — FALSE (в этом случае длина вектора должна равняться количеству строк в таблице):
```{r, eval=FALSE}
okruga[5, ] # Одна строка
okruga[2:4, ] # Несколько строк
okruga[okruga$Year10 > 2000, ] # Несколько строк через TRUE/FALSE
```
> В отличие от работы со столбцами, выбор строк всегда возвращает таблицу (фрейм или тиббл).
### Обращение к ячейкам {#tables_elements_cells}
Чтобы выбрать конкретные ячейки в таблице, необходимо задать оба измерения:
```{r}
okruga[2:3, c("Year11", "Year12")]
```
Обратите внимание на то, что при этом возвращаются все комбинации строк и столбцов. То есть, нельзя выбрать ячейки `2,"Year11"` и `3,"Year2"` --- вместе с ними также будут выбраны ячейки `3,"Year11"` и `2,"Year2"`. Впрочем, подобные задачи возникают довольно редко
## Преобразования {#tables_manipuilations}
### Грамматика манипуляций {#tables_manipiulations_grammar}
Если проанализировать наиболее типичные манипуляции, которые осуществляются над таблицами, то их окажется совсем немного. К таким манипуляциям относятся выбор переменных, фильтрация строк, сортировка, вычисление новых столбцов, агрегирующие статистики и группировка.
Все эти задачи можно решать стандартными средствами R (и мы увидим, как это делается). Однако некоторые из них достаточно громоздки в реализации (например, группировка). К счастью, экосистема R предлагает готовые средства, позволяющие справляться с подобными задачами простым и элегантным путем. Эти средства предоставляет пакет [**dplyr**](https://dplyr.tidyverse.org) (произносится как *deep liar* — 'диплáйер'), входящий в набор инструментов [tidyverse](https://www.tidyverse.org).
В основе конецепции **dplyr** лежит понятие о грамматике табличных манипуляций, которая включает в себя ограниченное число наиболее используемых операций, а также ряд вспомогательных функций.
Основные функции пакета **dplyr** представлены в таблице ниже:
| Функция | Назначение |
|---------------|----------------------------------------------------|
| `select()` | Выбор переменных по их названиям |
| `filter()` | Выбор строк по заданному критерию (запросу) |
| `arrange()` | Упорядочение по указанным переменным |
| `mutate()` | Вычисление новых переменных (мутирование) |
| `summarise()` | Агрегирование значений переменных |
| `group_by()` | Группировка строк (для последующего агрегирования) |
Как можно видеть, этих функций совсем немного. Дополнительно к ним пакет **dplyr** содержит еще множество вспомогательных функций, которые применяются при выполнении основных манипуляций.
Рассмотрим применение этих функций на примере работы с таблицей по восстановлению лесного фонда в регионах России. Для начала переименуем столбцы с годами, чтобы их названия начинались с буквы `y`:
```{r}
old_names = colnames(reforest)
colnames(reforest) = c(old_names[1], paste('y', old_names[2:9], sep = ''))
```
Начнем с **выбора** нужных переменных, используя `select()`. Оставим только название региона и данные за 2010 и 2015 гг:
```{r}
(rdf = select(reforest, Region, y2010, y2015))
```
Ту же самую задачу можно решить от противного — указать со знаком `-` те столбцы, которые надо убрать:
```{r}
(rdf = select(reforest, -y2005, -y2011:-y2014, -y2016))
```
Обратите внимание на то, что можно указывать еще и диапазоны названий столбцов, если они идут друг за другом.
> Названия столбцов в функциях **dplyr** указываются без кавычек, что позволяет сделат код проще и читаемее. Этот прием называется *квотацией*, с ним мы познакомимся подробнее в следующей лекции.
Чтобы осуществить **фильтрацию**, необходимо задать условие, накладываемое на строки. Текущая таблица содержит данные по субъектам, федеральным округам и России в целом. Поскольку данные по округам и стране являются избыточными (их можно получить путем агрегирования данных по субъектам), выполним фильтрацию таблицы, убрав строки, в которых содержатся слова `Федерация` и `федеральный округ`. Для этого используем функцию `str_detect()` из пакета **stringr**, который также входит в *tidyverse*:
```{r}
flt = !str_detect(rdf$Region, 'Федерация|федеральный округ') # готовим фильтр для строк
(regdf = filter(rdf, flt)) # применяем фильтр
```
Условие можно прописать непосредственно при вызове `dplyr::filter()`. Например, выберем регионы, в которых объем лесовосстановительных работ в 2015 году был более 50 тыс. га:
```{r}
filter(regdf, y2015 > 50)
```
Для **сортировки** таблицы посредством `arrange()` необходимо указать столбцы, по которым будет осуществлено упорядочение строк. Чаще всего это один столбец, например *y2015*:
```{r}
arrange(regdf, y2015) # по возрастанию
arrange(regdf, desc(y2015)) # по убыванию
```
**Добавление** новых переменных (столбцов) осуществляется посредством `mutate()`. Например, определим, как изменился объем лесовосстановительных работ в 2015 году по сравнению с 2010 годом:
```{r}
(regdf = mutate(regdf, delta = y2015 - y2010))
```
Существует редко используемая разновидность мутирования, при которой сохраняются только столбцы, указанные в параметрах. Она называется `transmute()` --- по сути это комбинация `mutate()` и `select()`. Если вы хотите просто сохранить какой-то из столбцов, то укажите его через оператор равенства:
```{r}
transmute(regdf, Region = Region, delta = y2015 - y2010) # сохраняем только Region и delta
```
Вы можете выполнять **агрегирование** данных и вычислять суммы, средние значения и т.д. используя `summarise()`. После того как мы избавились от избыточных данных в таблице, мы всегда можем получить их через агрегирование. Например, посчитаем суммарный, минимальный и максимальный объем лесовосстановительных работ по всей стране:
```{r}
summarise(regdf,
sumforest = sum(y2015, na.rm = TRUE),
minforest = min(y2015, na.rm = TRUE),
maxforest = max(y2015, na.rm = TRUE))
```
Как правило, `summarise()` используется в паре с агрегирующими функциями, которые берут вектор значений и возвращают одно значение. К таким функциям относятся стандартные операции типа `min()`, `max()`, `mean()`, `sum()` и т.д. В пакете **dplyr** также имеются полезные агрегирующие функции:
- `n()` вычисляет количество элементов.
- `n_distinct()` вычисляет количество уникальных элементов.
- `first(x)`, `last(x)` и `nth(x, n)` извлекают, соответственно, первый, последний и n-ный элемент (они бывают особенно удобны, если вы сортируете строки по какому-то критерию).
Достаточно часто данные надо агрегировать не по всей таблице, а **по группам** измерений. В этом случае сначала делается группировка, затем агрегирование данных в каждой группе. Предположим, что нам нужно найти регион с наибольшим объемом лесовосставновительных работ в каждом Федеральном округе. Для этого нам потребуется:
- Дополнить каждую строку региона информацией о принадлежности к федеральному округу
- Сгруппировать субъекты по федеральным округам — Отсортировать каждую группу по убыванию значения поля
- Взять первую строку в каждой группе
- Объединить строки в одну таблицу
Для начала вернемся на этап, когда мы избавлялись от федеральных округов в таблице. Поскольку в исходной таблице данные были упорядочены по округам, эту информацию можно использовать для создания нового столбца с названием округа каждого субъекта. В этом нам поможет функция `fill()` из пакета **tidyr**:
```{r}
flt2 = str_detect(rdf$Region, 'федеральный округ') # ищем округа
(rdf2 = mutate(rdf, okrug = if_else(flt2, Region, NA))) # перенесем названия округов в новый столбец
(rdf2 = fill(rdf2, okrug)) # заполним все пустые строчки предыдущим значением
(regdf = filter(rdf2, flt)) # оставим только регионы
```
Теперь мы можем определить регион с максимальным объемом лесовосстановительных работ в каждом Федеральном округе, используя вспомогательную функцию `row_number()` которая возвращает номер для каждой строки таблицы:
```{r}
regdf_gr = group_by(regdf, okrug)
regdf_arr = arrange(regdf_gr, desc(y2015))
(regdf_res = filter(regdf_arr, row_number() == 1))
```
`group_by()` часто используется в паре с `summarise()`. Например мы можем получить суммарный объем лесовосстановительных работ по каждому федеральному округу:
```{r}
regdf_gr = group_by(regdf, okrug)
summarise(regdf_gr, total = sum(y2015, na.rm = TRUE))
```
Использование **dplyr** целым обладает рядом преимуществ по сравнению с применением стандартных средств **R**:
- вызов функций с говорящими названиями операции более понятными;
- код выглядит более чистым и легко читаемым за счет отсутствия обращений к фреймам данных через квадратные скобки, доллары и «закавыченные» названия переменных;
- код с использованием функций **dplyr** часто оказывается короче, чем его традиционные аналоги;
- операции dplyr можно выстраивать в конвейеры с помощью пайп-оператора `|>`.
Последнюю возможность мы рассмотрим в следующем параграфе.
### Конвейер манипуляций {#tables_manipiulations_pipeline}
В предыдущем параграфе было показано как найти регион-лидер в каждой группе по выбранному показателю. При этом, несмотря на то что интерес представляет только конечный результат, нам пришлось шесть раз осуществить запись промежуточного результата в соответствующую переменную. Чтобы избежать подобного многословия в программах, в **R** реализована возможность организации **конвейера манипуляций** (pipeline) посредством использования *пайп-оператора* `|>`.
**Пайп-оператор `|>`** предназначен для компактной и наглядной записи *последовательностей* обработки данных. Работает он следующим образом:
- `x |> f` эквивалентно `f(x)`
- `x |> f(y)` эквивалентно `f(x, y)`
- `x |> f |> g |> h` эквивалентно `h(g(f(x)))`
Коротко говоря, пайп оператор берет результат вычисления выражения слева и подставляет его в качестве первого аргумента в выражение справа. С помощью этого оператора вышеприведенный код по нахождению региона-лидера можно записать так:
```{r, collapse=TRUE}
regdf = rdf |>
mutate(okrug = if_else(flt2, Region, NA)) |>
fill(okrug) |>
filter(flt)
leaders = regdf |>
group_by(okrug) |>
arrange(desc(y2015)) |>
filter(row_number() == 1)
print(leaders)
```
Если бы мы попытались написать те же последовательности операций одним выражением в традиционной «матрешечной» парадигме, это выглядело так:
```{r, collapse=TRUE}
regdf = filter(
fill(
mutate(
rdf,
okrug = if_else(flt2, Region, NA)
),
okrug
),
flt
)
result = filter(
arrange(
group_by(
regdf,
okrug
),
desc(y2015)
),
row_number() == 1
)
```
Выглядит несколько устрашающе. К тому же, читать такой код приходится задом наперед (изнутри наружу), чтобы понять последовательность действий. Таким образом, организация конвейера манипуляций с использованием пайп-оператора позволяет:
- упорядочить операции по обработке данных слева направо (в противоположность направлению изнутри наружу);
- избежать вложенных вызовов функций (матрёшки);
- минимизировать количество переменных для храненния промежуточных результатов;
- упростить добавление новых операций по обработке данных в любое место последовательности.
> **Пайп-оператор `|>` можно быстро набрать в RStudio**, нажав клавиатурное сочетание <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> (<kbd>Cmd</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> на помпьютерах Mac)
### Преобразование структуры {#tables_tidy}
Одни и те же данные можно предствить в табличной форме по-разному. Одна форма будет удобной для ручного заполнения таблицы. Другая форма будет удобной для программной обработки и анализа. Большинство же остальных форм не будут оптимальными ни для того, ни для другого. Наш курс посвящен автоматизированной обработке данных на языке R, поэтомы мы должны определить, какая форма таблицы удобна для этих целей.
В экосистеме R такие данные принято называть «аккуратными», или по-английски [**tidy data**](https://www.jstatsoft.org/article/view/v059i10). Аккуратные таблицы отвечают следующим требованиям:
1. Каждый столбец представляет переменную
2. Каждая строка представляет измерение
3. Каждая ячейка представляет значение
С некоторой долей условности можно говорить, что это [третья нормальная форма](https://ru.wikipedia.org/wiki/Третья_нормальная_форма) реляционного отношения.
Таблицы, с которыми мы работали до настоящего момента в этой главе, не отвечают данным требованиям. В частности, данные по лесовосстановлению содержат столбцы, в которых приведены данные за соответствующие года. Это **одна** переменная, разбитая на несколько столбцов. При этом *год измерения* является второй переменной. Такая форма удобна для заполнения и визуального анализа, но неудобна для программной обработки. Предположим, что нам надо найти все регионы, у которых в промежутке между 2012 и 2015 годами лесовосстановительные работы хотя бы в один год не производились (их объем был равен нулю). В текущей форме таблицы нам придется сделать 4 условия --- по одному на каждый год-столбец. Это при том, что мы должны быть уверены, что все промежуточные года в таблице присутствуют. Приведя таблицу к аккуратному виду, мы можем решить задачу более компактно, отправив всего 2 запроса: один на год измерения и второй на величину показателя.
Приведение таблицы к аккуратному виду можно сделать, используя функции из пакета [**tidyr**](https://tidyr.tidyverse.org). Основных функций в этом пакете всего две[^03-tables-1]:
[^03-tables-1]: Ранее вместо `pivot_longer()` и `pivot_wider()` использовались функции `gather()` и `spread()` соответственно. Новые функции имеют более очевидный синтаксис и более широкие возможности, поэтому использование `gather()` и `spread()` начиная с версии 1.0.0 пакета **tidyr** более не рекомендуется.
- [`pivot_longer()`](https://tidyr.tidyverse.org/reference/pivot_longer.html) берет несколько колонок и преобразует их к виду «ключ---значение»: широкие таблицы становятся длинными.
- [`pivot_wider()`](https://tidyr.tidyverse.org/reference/pivot_wider.html) берет две колонки, соответствующие ключу и значению, и распределяет их на множество колонок: длинные таблицы становятся широкими.
Помимо этого, есть еще 2 полезных функции, которые позволяют «распиливать» или «склеивать» колонки:
- `separate()` разделяет колонку на несколько колонок, используя заданный символ-разделитель или позицию.
- `unite()` скливает несколько колонок, используя заданный символ-разделитель.
Функция `pivot_longer()` имеет четыре основных параметра:
- `data` --- входная таблица (фрейм данных или тиббл)
- `cols` --- перечисление столбцов, которые необходимо подвергнуть преобразованию
- `names_to` --- имя нового столбца, который будет хранить ключи (бывшие названия столбцов)
- `values_to` --- имя нового столбца, который будет хранить значения
Рассмотрим на примере таблицы *reforest* приведение к аккуратному виду:
```{r}
(reforest_tidy = reforest |>
pivot_longer(cols = y2005:y2016,
names_to = 'year',
values_to = 'value'))
```
> Обратите внимание на то, что параметры `names_to` и `values_to` надо передавать как строки, поскольку они содержат еще *не существующие* объекты.
Полученный результат еще не вполне пригоден для анализа, поскольку в переменной `year` мы имеем строковые значения, начинающиеся с `y`. Чтобы избавиться от этого префикса, можно при вызове `pivot_longer()` указать параметр `names_prefix`. Поскольку по умолчанию ключи конвертируются в строковый столбец, а год — это целочисленное значение, следует дополнительно указать параметр `names_ptypes`. Помимо этого, конвертации подвергаются все столбцы, *кроме* `Region` — это означает, что можно пойти от обратного в параметре `cols`. Резюмируя перечисленные соображения, вышеприведенный вызов можно оптимизировать следующим образом:
```{r}
(reforest_tidy2 = reforest |>
pivot_longer(cols = -Region,
names_to = 'year',
names_prefix = 'y',
names_transform = list(year = as.integer),
values_to = 'value'))
```
Если по какой-то причине вам уже досталась таблица, в которой в ячейках сцеплены несколько сущностей, разделить их можно с помощью функции `separate()`. Вышеприведенный тиббл `reforest_tidy` можно "довести до ума" последовательным вызовом `separate()`, `select()` и `mutate()`:
```{r}
(reforest_tidy = reforest_tidy |>
separate(year, c('y', 'year'), 1) |>
select(-y) |>
mutate(year = as.integer(year)))
```
Теперь можно выполнять любые запросы, комбинирующие год измерения и величину показателя. Найдем субъекты, в которых с 2012 по 2015 год не производились лесовосстановительные работы:
```{r}
reforest_tidy |>
filter(year > 2011 & year < 2016 & value == 0)
```
"Длинная" форма данных удобна для программного анализа, но может быть неудобна для ручного редактирования, визуальной оценки или передачи в другие программы, ожидающие на входе данные в "широком" формате. В этих случаях будет полезна функция `pivot_wider()`, которая по своему действию противоположна `pivot_longer()`. Данная функция имеет три основных параметра:
- `data` --- входная таблица (фрейм данных или тиббл)
- `names_from` --- имя переменной, из которой будут браться названия столбцов
- `values_from` --- имя переменной, из которой будут браться значения в ячейках
Преобразуем ранее удлиненную таблицу к "широкому" виду:
```{r}
(reforest = reforest_tidy |>
pivot_wider(names_from = year, values_from = value))
```
> Обратите внимание на то, что параметры `names_from` и `values_from` можно задавать как имена переменных, а не как строки.
В качестве примера операции с широкой таблицей вычислим разности по сравнению с предыдущим годом:
```{r}
diffs = reforest |> select(`2011`:`2016`) -
reforest |> select(`2010`:`2015`)
diffs |>
mutate(Region = reforest$Region) |>
select(Region, `2011`:`2016`) |>
head() # Посмотрим шапку таблицы
```
## Соединение {#tables_join}
Данные, с которыми мы работаем, часто распределены по нескольким таблицам. Если возникает задача их совместного использования (сравнения, вычисления производных показателей), таблицы необходимо соединить.
В процессе **соединения** в обеих таблицах находятся строки, соответствующие одному и тому же измерению (например, региону). После этого столбцы второй таблицы пристыковываются к столбцам первой таблицы, а строки — к соответствующим строкам *(мутирующее соединение)*, либо же происходит фильтрация строк первой таблицы на основе нахождения соответствующих строк во второй таблице *(фильтрующее соединение)*.
Чтобы найти соответствие, в обеих таблицах должен быть по крайней мере один столбец, идентифицирующий каждую строку. В первой таблице он называется **первичным ключом** *(primary key)*, во второй таблице --- **внешним ключом** *(foreign key)*.
Для выполнения соедининия в пакете dplyr имеется несколько функций.
*Мутирующее соединение:*
- `inner_join(x, y, by = )` возвращает все строки из `x`, для которых имеются соответствующие строки в `y`, а также все столбцы из `x` и `y`.
- `left_join(x, y, by = )` возвращает все строки из `x`, а также все столбцы из `x` и `y`. Строки в `x`, для которых не найдены соответствия в `y`, будут иметь значения `NA` в присоединенных столбцах
- `right_join(x, y, by = )` возвращает все строки из `y`, а также все столбцы из `x` и `y`. Строки в `y`, для которых не найдены соответствия в `x`, будут иметь значения `NA` в присоединенных столбцах
- `full_join(x, y, by = )` возвращает все строки и колонки из `x` и `y`. В строках, для которых не найдено соответствие ячейки присоединяемых стольков будут заполнены значениями `NA`
*Фильтрующее соединение:*
- `semi_join(x, y, by = )` возвращает все строки из `x`, для которых имеются соответствующие строки в `y`, а также все столбцы из `x`
- `anti_join(x, y, by = )` возвращает все строки из `x`, для которыхне найдены соответствующие строки в `y`, а также все столбцы из `x`
Рассмотрим соединение таблиц на примере данных по лесовосстановлению и заготовкам древесины. Наша задача — оценить количество гектаров восстанавливаемой лесной площади (в га) на тысячу кубометров лесозаготовок (и таким образом оценить эффективность лесовосстановительных мероприятий).
Подгрузим таблицу по лесозаготовкам:
```{r}
(timber = read_excel('data/timber.xlsx',
col_types = c('text', rep('numeric', 8))) |>
filter(!str_detect(Регион, 'Федерация|федеральный округ')))
```
Приведем ее к аккуратному виду, который соответствует виду таблицы по лесовосстановлению:
```{r}
(timber_tidy = timber |>
gather(year, harvest, `2010`:`2016`) |>
transmute(Region = Регион,
year = as.numeric(year),
harvest = harvest))
```
Теперь нам осталось присоединить данные по лесозаготовкам к таблице по лесовосстановлению, используя имя региона (`Region`) и год (`year`) в качестве ключевых полей.
Для этого мы используем функцию `inner_join()`, поскольку нас интересует сравнение по тем годам, для которых имеются данные в обеих таблицах:
```{r}
(compare = reforest_tidy |>
inner_join(timber_tidy, by = c("Region" = "Region", "year" = "year")))
```
Наконец, вычислим искомое отношние и упорядочим регионы по году (возрастание) и отношению (убывание):
```{r}
(compare = compare |>
mutate(ratio = 1000 * value / harvest) |>
select(Region, year, ratio, value, harvest) |>
arrange(year, desc(ratio)))
```
Из этой таблицы видно, что площадь восстанавливаемых лесов далеко не всегда пропорциональна объему заготовок необработанной древесины.
## Запись {#tables_write}
Запись файлов *в текстовом формате* можно осуществить посредством функций из пакета **readr**, таких как `write_delim()`, `write_csv()` и `write_tsv()`. Базовый синтаксис их предельно прост:
```{r}
write_csv(compare, "data/output/timber_compare.csv")
```
Для записи таблиц *Microsoft Excel* можно использовать возможности пакета [**writexl**](https://cran.r-project.org/web/packages/writexl/index.html):
```{r}
write_xlsx(compare, "data/output/timber_compare.xlsx")
```
Каждая из этих функций содержит ряд дополнительных параметров, позволяющих управлять внешним видом выгружаемых таблиц. Более подробно с ними вы можете ознакомиться, вызвав справку для соответствующей функции.
## Рекомендации по подготовке таблиц для чтения в R {#tables_rules}
Несмотря на то, что каких-то четких правил подготовки таблиц для программной обработки не существует, можно дать несколько полезных рекомендаций по данному поводу:
1. В первой строке таблицы должны располагаться названия столбцов.
2. Во второй строке таблицы должны начинаться данные. Не допускайте многострочных заголовков.
3. В названиях столбцов недопустимы объединенные ячейки, покрывающие несколько столбцов. Это может привести к неверному подсчету количества столбцов и, как следствие, некорректному чтению таблицы в целом.
4. Названия столбцов должны состоять из латинских букв и цифр, начинаться с буквы и не содержать пробелов. Сложносочиненные названия выделяйте прописными буквами. Плохое название столбца: `Валовый внутренний продукт за 2015 г.`. Хорошее название столбца: `GDP2015`.
5. Некоторые ошибки данных в таблицах (такие как неверные десятичные разделители), проще найти и исправить в табличном/текстовом редакторе, нежели после загрузки в **R**.
Следование этим правилам значительно облегчит работу с табличными данными в среде **R**.
## Краткий обзор {#tables_review}
Для просмотра презентации щелкните на ней один раз левой кнопкой мыши и листайте, используя кнопки на клавиатуре:
```{r, echo=FALSE}
knitr::include_url('https://tsamsonov.github.io/r-geo-course-slides/03_Tables.html#1', height = '390px')
```
> Презентацию можно открыть в отдельном окне или вкладке браузере. Для этого щелкните по ней правой кнопкой мыши и выберите соответствующую команду.
## Контрольные вопросы и упражнения {#tables_questions}
### Вопросы {#tables_questions_questions}
1. Какая функция позволяет установить новый пакет в R?
2. Как подключить пакет в текущую сессию R?
3. Как вызвать функцию из пакета, не подключая его в текущей сессии R?
4. Какие структуры данных используются для представления таблиц в среде R? Чем они отличаются друг от друга?
5. Какая функция позволяет создавать тибблы по строкам, а не столбцам?
6. Как загрузить в текущую сессию R набор данных из конкретного пакета? Под каким именем он будет доступен?
7. Каковы основные принципы создания текстовых файлов с разделителем для хранения таблиц? Какой аббревиатурой они обозначаются?
8. Чем отличаются файлы с фиксированной шириной столбца от файлов с разделителем? Какими спопообами можно указать границы колонок при чтении этих файлов?
9. Перечислите функции из пакета **readr**, которые можно использовать для чтения текстовых файлов с табличными данными?
10. Какой пакет позволяет читать данные в формате Microsoft Excel?
11. Если прочитанный вами файл содержит нечитаемые символы, что это означает? Как решить проблему?
12. Какие дополнительные параметры позволяют задавать функции чтения данных из пакетов **readr** и **readxl**, помимо имени файла?
13. Перечислите основные возможности для отображения табличных данных в среде R?
14. Можно ли заменять названия строк и столбцов таблицы? Если да, то какие функции для этого используются?
15. Назовите возможные способы извлечения столбца (столбцов) из фрейма данных/тиббла. Какие из них приводят к получения вектора, а какие возвращают фрейм данных/тиббл?
16. Как извлечь строки, удовлетворяющие некоторому критерию, используя стандартные средства **R**?
17. Перечислите основные типы манипуляций над таблицами.
18. Какой тип манипуляции всегда используется в сочетании с другими типами?
19. Нужно ли заключать названия переменных в кавычки при использовании функций пакета **dplyr**?
20. Какая функция **dplyr** позволяет выбирать нужные переменные? Как с помощью нее отобрать диапазон переменных? Можно ли пойти от обратного и исключить, а не выбрать переменные?
21. Какая функция **dplyr** позволяет осуществлять фильтрацию строк? Как реализовать фильтрацию строк по нескольким условиям одновременно?
22. Как найти строки, в которых встречаются заданные фрагменты текста? Какая функция позволяет это сделать?
23. Назовите функцию **dplyr**, отвечающую за выполнение сортировки таблицы. Как с помощью нее упорядочить данные по нескольким столбцам? Сделать порядок сортировки по убыванию?
24. Что такое мутирование и трансмутирование таблицы?
25. Какая функция **dplyr** отвечает за добавление новых переменных? Можно ли с помощью нее добавить несколько переменных сразу? Как оставить только вновь создаваемые переменные?
26. С помощью какой функции можно вычислить обобщающие статистики по всем строкам или группам строк?
27. Как с помощью **dplyr** получить количество элементов и количество уникальных элементов в каждой группе?
28. Как с помощью **dplyr** получить первый, последний и n-ный элемент в каждой группе?
29. Что такое конвейер манипуляций и для чего он используется в R?
30. Опишите приципы действия пайп-оператора. С помощью какого клавиатурного сочетания его можно быстро ввести в **RStudio**?
31. Опишите требования, которым должны отвечать аккуратные табличные данные. Какой пакет **R** позволяет преборазовывать данные в такую форму?
32. Какие две основных операции над таблицами позволяют добиться длинного и широкого вида таблицы? Можно ли говорить о том, что для всех задач удобна какая-то одна из этих двух форм?
33. Как работают функции `pivot_longer()` и `pivot_wider()`?
34. Что такое соединение таблиц? В каких случаях оно бывает необходимо? Какие разновидности соединения реализованы в **dplyr**?
35. Каким требованиям должны отвечать таблицы для того чтобы их соединение было возможным?
36. Что такое первичный и внешний ключ в операции соединения таблиц?
37. Какие возможности существуют для записи табличных данных в текстовые файлы? Перечислите пакеты и функции, такие возможности реализующие.
38. Опишите общие рекомендации для подготовки таблиц к чтению средствами R.
### Упражнения {#tables_questions_tasks}
1. Таблица *quakes* из пакета **datasets** содержит магнитуду землетрясений в поле `mag`. Используя функции **dplyr** и пайп-оператор, создайте на ее основе таблицу с частотой (количеством штук) землетрясений каждой магнитуды.
> **Подсказка:** при выполнении агрегирования используйте функцию `n()`, которая возвращает количество строк в группе.
2. Таблица *storms* из пакета **dplyr** содержит увлекательные данные трекинга тропических циклонов c 1975 по 2015 год. Используя функции **dplyr** и пайп-оператор, создайте таблицу в которой зафиксировано: название циклона, дата начала, дата окончания, продолжительность в днях, максимальная скорость ветра, минимальное давление. Отсортируйте циклоны сначала по максимальной скорости ветра (по убыванию), затем по давлению (по возрастанию). Оформите результат в виде одного конвейера манипуляций.
> **Подсказка:** перед выполнением агрегирования на основе существующих полей создайте новое поле, в котором хранится дата события, имеющая тип `Date`. Это позволит вам правильно вычислить продолжительность в днях. Чтобы создать строку для преобразования в дату, используйте функцию `paste()`, подставив в нее поля, составляющие даты, и укажите необходимый разделитель в параметре `sep()`. После агрегирования данных и перед сортировкой вызовите `ungroup()`, иначе вы будете сортировать внутри каждой группы, а вам нужно сортировать результаты группировки.
3. Загрузите [файл](https://raw.githubusercontent.com/tsamsonov/r-geo-course/master/data/wind_energy.txt) с данными по энергии ветра вдоль Черноморского побережья на высотах 50 и 100 метров, который был использован в этой лекции. Данный файл имеет формат фиксированной ширины столбца (см. параграф \@ref(tables-reading-text-fwf)). Произведите чтение данного файла и приведите его с помощью **tidyr** к аккуратному виду, разделив высоту и величину энергии на отдельные столбцы. Используя возможности **dplyr**, рассчитайте фрейм данных со средними значениями энергии на каждой высоте.
4. Загрузите [файл CSV](https://raw.githubusercontent.com/tsamsonov/r-geo-course/master/data/okruga.csv) с данными по населению федеральных округов России, который был использован в этой лекции. Прочтите данную таблицу, приведите ее с помощью **tidyr** к аккуратному виду (федеральный округ — год — население), а также вычислите в виде дополнительного столбца долю (в %), которую каждый округ занимает в общем населении России на каждый год (получится федеральный округ — год — население — доля). Оформите результат в виде одного конвейера манипуляций.
> **Подсказка:** используйте *группировку таблицы по году* и вычислите поле с долей путем деления поля населения на *сумму* по полю населения.
| |
|----|
| *Самсонов Т.Е.* **Визуализация и анализ географических данных на языке R.** М.: Географический факультет МГУ, `r lubridate::year(Sys.Date())`. DOI: [10.5281/zenodo.901911](https://doi.org/10.5281/zenodo.901911) |