2 Маніпуляції з даними за допомогою dplyr і не тільки
В минулій главі ми розібрали основні принципи мови програмування R. І тут слід зазначити, що окрім базового синтаксису існують й інші. Загалом, можна виділити три основні діалекти в мові програмування R:
base: основний фундамент мови, який ми розібрали (але не повністю) раніше.
Tidyverse: окремий напрямок розвитку мови програмування R, що сконцентрований у напрямку науки про дані (data science).
data.table: альтернативний напрямок, який дозволяє оброблювати об’ємні масиви даних за рекордний час “Database-Like Ops Benchmark” (2022).
Їх можна сміло поєднувати в своїх проектах, що значним чином підвищує ефективність та швидкість роботи.
2.1 Tidy-всесвіт
Tidyverse — це екосистема, набір пакетів, які спеціально створені для науки про дані (data science). В ньому є ключові пакети (ядро tidyverse) та побічні — які додатково розширюють можливості мови програмування R.
Концепція охайних даних (tidy-data) передбачає приведення даних до формату, в якому:
Кожна змінна міститься в окремому стовпчику
Кожне спостереження міститься в окремому рядку
Кожне значення міститься в окремій комірці
Ядро tidyverse:
ggplot2, для візуалізації
dplyr, для маніпуляції з даними
tidyr, для отримання охайних даних (tidy data)
readr, для зчитування та записування файлів в R
purrr, для функціонального програмування
tibble, для роботи з тібблами (tibble), просунутий варіант дата фреймів
stringr, для роботи з текстовими даними
forcats, для роботи з факторами (factors)
Крім того є ще низка допоміжних пакетів, які не входять до ядра tidyverse але вважаються його частиною:
vroom, для швидкого завантаження даних
DBI, для роботи з базами даних
haven, для даних SPSS, Stata та SAS
httr, для роботи з API
readxl для завантаження .xls та .xlsx файлів
googlesheets4, для роботи з Google Sheet
googledrive, для роботи з Google Drive
rvest, для скрапінгу веб-сторінок
jsonlite, для роботи з JSON-файлами
xml2, для роботи з XML
lubridate, для роботи з датами
dbplyr, для перетворення коду
dplyr
в SQLdtplyr, для перетворення коду на
data.table
magrittr, для використання конвеєрів
%>%
(pipe)glue, для поєднання даних та тексту
tidymodels, для роботи з моделями машнинного навчання.
І це ще не повний список. Крім офіційних пакетів tidyverse є ще низка пакетів, які намагаються відповідати принципам tidyverse і доповнюють його.
Головним чином, для роботи з даними, я зосереджу свою увагу на роботі з пакетом dplyr
.
Для завантаження tidyverse необхідно виконати наступний код:
install.packages("tidyverse")
Для підключення:
Концепція “охайних” даних передбачає альтернативу класичним data.frame
у вигляді тібблів (tibble
). Давайте розеберемо основі відмінності.
В мові програмування R є вбудований популярний датасет iris
. Він зберігається в форматі дата фрейму.
# Переглянемо перші декілька значень
head(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
Давайте створемо його альтернативу у вигляді тібблу:
iris_tbl <- as_tibble(iris)
iris_tbl
## # A tibble: 150 x 5
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## <dbl> <dbl> <dbl> <dbl> <fct>
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
## 7 4.6 3.4 1.4 0.3 setosa
## 8 5 3.4 1.5 0.2 setosa
## 9 4.4 2.9 1.4 0.2 setosa
## 10 4.9 3.1 1.5 0.1 setosa
## # ... with 140 more rows
Вже на цьому етапі видні основні відмінності: тіббл відображає одразу перші 10 спостережень і всі ствопчики, які поміщаються на екрані. Крім того до кожного стовпчика вказується його тип. Це дуже зручно, особливо коли працюєте з великим об’ємом даних.
2.2 Завантаження даних
2.2.1 Завантаження .csv, .tsv файлів
Стандартною функцією завантаження даних типу .csv
є функція read.csv()
, але на досить великих масивах даних краще використовувати read_csv()
з пакету readr
. Синтаксис цих функцій схожий, але read_csv()
одразу приведе дані до формату tibble
.
Першим аргументом функції є шлях до файлу (із оберненим слешем /
). Також можна використовувати прямі URL-посилання на файл:
Аналогічно до read_csv()
можна використовувати функцію vroom
з однойменного пакету. Головною особливістю цього пакету є швидкість завантаження даних.
Для завантаження одночасно декількох файлів однакової структури корисно використовувати наступну конструкцію
2.2.2 Завантаження .xls, .xlsx файлів
Для завантаження файлів Excel використовується пакет readxl
та функція read_excel()
.
На початку можна отримати перелік листів файлу Excel за допомогою функції excel_sheets()
readxl::excel_sheets("docs/data/tourism.xlsx")
## [1] "Sheet1" "Sheet2" "Sheet3"
Після чого зчитати данні з потрібного листа
excel_file <- read_excel("docs/data/tourism.xlsx", sheet = "Sheet1")
В більшості випадків цього інструментарію має бути достатньо, але для завантаження специфічних файлів завжди можна знайти потрібний пакет. Не соромтеся використовувати google.
2.3 Маніпуляції з даними за допомогою пакету dplyr
dplyr - це граматика маніпуляції з даними, яка має низку функцій, які допоможуть легко та зручно маніпулювати даними, наприклад:
створювати нові змінні
сортувати дані
проводити фільтрацію даних
агрегування даних і багато іншого.
В якості прикладу роботи з пакетом dplyr пропоную використати датасет gapminder
з однойменного пакету. В ньому збережена інформація про ВВП, очікувану тривалість життя при народженні та населення для 142 країн світу з 1952 по 2007 роки.
# Підключаємо пакет (не забудьте його встановити) та подивимось на датасет
library(gapminder)
gapminder
## # A tibble: 1,704 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Видно, що змінні country
та continent
— це фактори, а всі інші — числові.
2.3.1 dplyr::glimpse()
Для перегляду структури тібблу використовується функція glimpse()
:
glimpse(gapminder)
## Rows: 1,704
## Columns: 6
## $ country <fct> "Afghanistan", "Afghanistan", "Afghanistan", "Afghanistan", "Afghan~
## $ continent <fct> Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, A~
## $ year <int> 1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997, 2002, 2~
## $ lifeExp <dbl> 28.801, 30.332, 31.997, 34.020, 36.088, 38.438, 39.854, 40.822, 41.~
## $ pop <int> 8425333, 9240934, 10267083, 11537966, 13079460, 14880372, 12881816,~
## $ gdpPercap <dbl> 779.4453, 820.8530, 853.1007, 836.1971, 739.9811, 786.1134, 978.011~
Це альтернатива базовій функції str()
для дата фреймів.
2.3.2 dplyr::filter()
Для фільтрації спостережень за певною умовою використовується функція filter()
. Для прикладу відфільтруємо дані для Ірландії:
filter(gapminder, country == "Ireland")
## # A tibble: 12 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Ireland Europe 1952 66.9 2952156 5210.
## 2 Ireland Europe 1957 68.9 2878220 5599.
## 3 Ireland Europe 1962 70.3 2830000 6632.
## 4 Ireland Europe 1967 71.1 2900100 7656.
## 5 Ireland Europe 1972 71.3 3024400 9531.
## 6 Ireland Europe 1977 72.0 3271900 11151.
## 7 Ireland Europe 1982 73.1 3480000 12618.
## 8 Ireland Europe 1987 74.4 3539900 13873.
## 9 Ireland Europe 1992 75.5 3557761 17559.
## 10 Ireland Europe 1997 76.1 3667233 24522.
## 11 Ireland Europe 2002 77.8 3879155 34077.
## 12 Ireland Europe 2007 78.9 4109086 40676.
І тут я одразу хочу познайомити вас з альтернативним варіантом запису коду в синтаксисі tidyverse. Справа в тому, що якщо послідовно використовувати низку різноманітних функцій, то читаймість такого коду стає дуже низькою. Покажу на прикладі:
До кожного значення вектора від 1 до 10 з кроком 1 розрахуємо сінус.
З отриманого на першому кроці візьмемо абсолютні значення.
З отриманого результату на 2 кроці візьмемо корінь квадратний
Відсортуємо результат.
В класичному вигляді все виглядає наступним чином:
sort(sqrt(abs(sin(1:10))))
## [1] 0.3756594 0.5285977 0.6419646 0.7375779 0.8105471 0.8699440 0.9173173 0.9535709
## [9] 0.9792468 0.9946649
Погодьтесь, що розібрати такий код досить складно. Тому в діалекті tidyverse використовуються пайпи, %>%
, (pipe) — вони передають результат попереднього розрахунку першим аргументов наступної функції. Таким чином попередній код можна переписати наступним чином:
1:10 %>%
sin() %>%
abs() %>%
sqrt() %>%
sort()
## [1] 0.3756594 0.5285977 0.6419646 0.7375779 0.8105471 0.8699440 0.9173173 0.9535709
## [9] 0.9792468 0.9946649
Погодьтесь, що такий код читається значно легше. Для виклику оператора %>%
в RStudio використовується комбінація клавіш CTRL + SHIFT + M для Windows і CMD + SHIFT + M для Mac.
Тож, якщо повернутися до фільтрації 2.3.2, код можна переписати:
# Попередній варіант
filter(gapminder, country == "Ireland")
## # A tibble: 12 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Ireland Europe 1952 66.9 2952156 5210.
## 2 Ireland Europe 1957 68.9 2878220 5599.
## 3 Ireland Europe 1962 70.3 2830000 6632.
## 4 Ireland Europe 1967 71.1 2900100 7656.
## 5 Ireland Europe 1972 71.3 3024400 9531.
## 6 Ireland Europe 1977 72.0 3271900 11151.
## 7 Ireland Europe 1982 73.1 3480000 12618.
## 8 Ireland Europe 1987 74.4 3539900 13873.
## 9 Ireland Europe 1992 75.5 3557761 17559.
## 10 Ireland Europe 1997 76.1 3667233 24522.
## 11 Ireland Europe 2002 77.8 3879155 34077.
## 12 Ireland Europe 2007 78.9 4109086 40676.
# З використанням %>%
gapminder %>%
filter(country == "Ireland")
## # A tibble: 12 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Ireland Europe 1952 66.9 2952156 5210.
## 2 Ireland Europe 1957 68.9 2878220 5599.
## 3 Ireland Europe 1962 70.3 2830000 6632.
## 4 Ireland Europe 1967 71.1 2900100 7656.
## 5 Ireland Europe 1972 71.3 3024400 9531.
## 6 Ireland Europe 1977 72.0 3271900 11151.
## 7 Ireland Europe 1982 73.1 3480000 12618.
## 8 Ireland Europe 1987 74.4 3539900 13873.
## 9 Ireland Europe 1992 75.5 3557761 17559.
## 10 Ireland Europe 1997 76.1 3667233 24522.
## 11 Ireland Europe 2002 77.8 3879155 34077.
## 12 Ireland Europe 2007 78.9 4109086 40676.
Тут і далі я буду часто використовувати пайпи.
Розберемо ще приклади фільтрації даних. Відберемо дані Ірландії та Іспанії за 2007 рік:
gapminder %>%
filter(country == "Ireland" | country == "Spain",
year == 2007)
## # A tibble: 2 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Ireland Europe 2007 78.9 4109086 40676.
## 2 Spain Europe 2007 80.9 40448191 28821.
А тепер припустимо, що нам потрібно відібрати інформацію не по двом, а по низці країн. Переліковувати їх всіх через country == "Назва_країни"
буде дуже довго і не зручно. В таких випадках зручно використовувати оператор %in%
. Давайте відберемо інформацію по Ірландії, Іспанії, Норвегії та Польщі за 2007 рік:
gapminder %>%
filter(country %in% c("Ireland", "Spain", "Norway", "Poland"),
year == 2007)
## # A tibble: 4 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Ireland Europe 2007 78.9 4109086 40676.
## 2 Norway Europe 2007 80.2 4627926 49357.
## 3 Poland Europe 2007 75.6 38518241 15390.
## 4 Spain Europe 2007 80.9 40448191 28821.
Всі потрібні умови фільтрації можна переліковувати всередині функції filter()
через кому.
2.3.3 dplyr::slice()
Для отримання зрізу даних, тобто тільки певних спостережень (перші, останні тощо) використовуються варіації функції slice()
.
Перші п’ять спостережень:
gapminder %>%
slice(1:5)
## # A tibble: 5 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
Перші п’ять та десяте спостереження:
gapminder %>%
slice(1:5, 10)
## # A tibble: 6 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1997 41.8 22227415 635.
Всі крім перших трьох:
gapminder %>%
slice(-(1:3))
## # A tibble: 1,701 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1967 34.0 11537966 836.
## 2 Afghanistan Asia 1972 36.1 13079460 740.
## 3 Afghanistan Asia 1977 38.4 14880372 786.
## 4 Afghanistan Asia 1982 39.9 12881816 978.
## 5 Afghanistan Asia 1987 40.8 13867957 852.
## 6 Afghanistan Asia 1992 41.7 16317921 649.
## 7 Afghanistan Asia 1997 41.8 22227415 635.
## 8 Afghanistan Asia 2002 42.1 25268405 727.
## 9 Afghanistan Asia 2007 43.8 31889923 975.
## 10 Albania Europe 1952 55.2 1282697 1601.
## # ... with 1,691 more rows
Перші 15 спостережень:
gapminder %>%
slice_head(n = 15)
## # A tibble: 15 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## 11 Afghanistan Asia 2002 42.1 25268405 727.
## 12 Afghanistan Asia 2007 43.8 31889923 975.
## 13 Albania Europe 1952 55.2 1282697 1601.
## 14 Albania Europe 1957 59.3 1476505 1942.
## 15 Albania Europe 1962 64.8 1728137 2313.
Останні 15 спостережень:
gapminder %>%
slice_tail(n = 15)
## # A tibble: 15 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Zambia Africa 1997 40.2 9417789 1071.
## 2 Zambia Africa 2002 39.2 10595811 1072.
## 3 Zambia Africa 2007 42.4 11746035 1271.
## 4 Zimbabwe Africa 1952 48.5 3080907 407.
## 5 Zimbabwe Africa 1957 50.5 3646340 519.
## 6 Zimbabwe Africa 1962 52.4 4277736 527.
## 7 Zimbabwe Africa 1967 54.0 4995432 570.
## 8 Zimbabwe Africa 1972 55.6 5861135 799.
## 9 Zimbabwe Africa 1977 57.7 6642107 686.
## 10 Zimbabwe Africa 1982 60.4 7636524 789.
## 11 Zimbabwe Africa 1987 62.4 9216418 706.
## 12 Zimbabwe Africa 1992 60.4 10704340 693.
## 13 Zimbabwe Africa 1997 46.8 11404948 792.
## 14 Zimbabwe Africa 2002 40.0 11926563 672.
## 15 Zimbabwe Africa 2007 43.5 12311143 470.
Топ-3 з найбільшим значенням очікуваної тривалості життя:
gapminder %>%
slice_max(lifeExp, n = 3)
## # A tibble: 3 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Japan Asia 2007 82.6 127467972 31656.
## 2 Hong Kong, China Asia 2007 82.2 6980412 39725.
## 3 Japan Asia 2002 82 127065841 28605.
Топ-3 з найменшим значенням очікуваної тривалості життя:
gapminder %>%
slice_min(lifeExp, n = 3)
## # A tibble: 3 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Rwanda Africa 1992 23.6 7290203 737.
## 2 Afghanistan Asia 1952 28.8 8425333 779.
## 3 Gambia Africa 1952 30 284320 485.
Проста випадкова вибірка без повернення з трьох країн:
gapminder %>%
slice_sample(n = 3)
## # A tibble: 3 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Canada Americas 1972 72.9 22284500 18971.
## 2 El Salvador Americas 1957 48.6 2355805 3422.
## 3 Chad Africa 1987 51.1 5498955 952.
Зверніть увагу, що при повторному виконанні попереднього коду ви будете отримувати кожного разу різні підвибірки. Для того щоб отримати відтворюваний результат, необхідно вказати початкове значення генератора випадкових чисел за допомогою функції set.seed()
. Аргументом цієї функції може бути будь-яке ціле число.
set.seed(2022)
gapminder %>%
slice_sample(n = 3)
## # A tibble: 3 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Cambodia Asia 2007 59.7 14131858 1714.
## 2 Swaziland Africa 1982 55.6 649901 3895.
## 3 Kenya Africa 1982 58.8 17661452 1348.
Для формування простої випадкової вибірки з поверненням з трьох країн, необхідно додати аргумент replace = TRUE
:
gapminder %>%
slice_sample(n = 3, replace = TRUE)
## # A tibble: 3 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 India Asia 2007 64.7 1110396331 2452.
## 2 Italy Europe 1977 73.5 56059245 14256.
## 3 India Asia 1982 56.6 708000000 856.
2.3.4 dplyr::arrange()
Для впорядкування даних використовується функція arrange()
. За замовчуванням сортування даних відбувається за зростанням.
Сортування по змінній ВВП на душу населення:
gapminder %>%
arrange(gdpPercap)
## # A tibble: 1,704 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Congo, Dem. Rep. Africa 2002 45.0 55379852 241.
## 2 Congo, Dem. Rep. Africa 2007 46.5 64606759 278.
## 3 Lesotho Africa 1952 42.1 748747 299.
## 4 Guinea-Bissau Africa 1952 32.5 580653 300.
## 5 Congo, Dem. Rep. Africa 1997 42.6 47798986 312.
## 6 Eritrea Africa 1952 35.9 1438760 329.
## 7 Myanmar Asia 1952 36.3 20092996 331
## 8 Lesotho Africa 1957 45.0 813338 336.
## 9 Burundi Africa 1952 39.0 2445618 339.
## 10 Eritrea Africa 1957 38.0 1542611 344.
## # ... with 1,694 more rows
Для того щоб сотувати дані за спаданням, використовується додаткова функція desc()
в середині arrange()
:
gapminder %>%
arrange(desc(gdpPercap))
## # A tibble: 1,704 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Kuwait Asia 1957 58.0 212846 113523.
## 2 Kuwait Asia 1972 67.7 841934 109348.
## 3 Kuwait Asia 1952 55.6 160000 108382.
## 4 Kuwait Asia 1962 60.5 358266 95458.
## 5 Kuwait Asia 1967 64.6 575003 80895.
## 6 Kuwait Asia 1977 69.3 1140357 59265.
## 7 Norway Europe 2007 80.2 4627926 49357.
## 8 Kuwait Asia 2007 77.6 2505559 47307.
## 9 Singapore Asia 2007 80.0 4553009 47143.
## 10 Norway Europe 2002 79.0 4535591 44684.
## # ... with 1,694 more rows
Якщо сортування за спаданням виконується по одній змінній, замість desc()
можна використати знак -
:
# Але працює це тільки у випадку використання однієї змінної!
gapminder %>%
arrange(-gdpPercap)
## # A tibble: 1,704 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Kuwait Asia 1957 58.0 212846 113523.
## 2 Kuwait Asia 1972 67.7 841934 109348.
## 3 Kuwait Asia 1952 55.6 160000 108382.
## 4 Kuwait Asia 1962 60.5 358266 95458.
## 5 Kuwait Asia 1967 64.6 575003 80895.
## 6 Kuwait Asia 1977 69.3 1140357 59265.
## 7 Norway Europe 2007 80.2 4627926 49357.
## 8 Kuwait Asia 2007 77.6 2505559 47307.
## 9 Singapore Asia 2007 80.0 4553009 47143.
## 10 Norway Europe 2002 79.0 4535591 44684.
## # ... with 1,694 more rows
2.3.5 dplyr::select()
У випадках, коли змінних у данних дуже багато, є сенс відібрати тільки ті, з якими ви будете працювати. В таких випадках нам допоможе функція select()
, хоча тільки такими задачами вона не обмежується.
У нашому випадку в наборі даних лише шість змінних, що в принципі небагато, але суть функції буде зрозуміла.
Відберемо, наприклад змінні по країнам та населенню:
gapminder %>%
select(country, pop)
## # A tibble: 1,704 x 2
## country pop
## <fct> <int>
## 1 Afghanistan 8425333
## 2 Afghanistan 9240934
## 3 Afghanistan 10267083
## 4 Afghanistan 11537966
## 5 Afghanistan 13079460
## 6 Afghanistan 14880372
## 7 Afghanistan 12881816
## 8 Afghanistan 13867957
## 9 Afghanistan 16317921
## 10 Afghanistan 22227415
## # ... with 1,694 more rows
Через двокрапку можна вказати послідовність з декількох стовпчиків для відбору:
gapminder %>%
select(country, lifeExp:gdpPercap)
## # A tibble: 1,704 x 4
## country lifeExp pop gdpPercap
## <fct> <dbl> <int> <dbl>
## 1 Afghanistan 28.8 8425333 779.
## 2 Afghanistan 30.3 9240934 821.
## 3 Afghanistan 32.0 10267083 853.
## 4 Afghanistan 34.0 11537966 836.
## 5 Afghanistan 36.1 13079460 740.
## 6 Afghanistan 38.4 14880372 786.
## 7 Afghanistan 39.9 12881816 978.
## 8 Afghanistan 40.8 13867957 852.
## 9 Afghanistan 41.7 16317921 649.
## 10 Afghanistan 41.8 22227415 635.
## # ... with 1,694 more rows
Замість назв можна використовувати порядковий номер стовпчика (нагадую, що індексація в R починається з 1):
gapminder %>%
select(1, 4:6)
## # A tibble: 1,704 x 4
## country lifeExp pop gdpPercap
## <fct> <dbl> <int> <dbl>
## 1 Afghanistan 28.8 8425333 779.
## 2 Afghanistan 30.3 9240934 821.
## 3 Afghanistan 32.0 10267083 853.
## 4 Afghanistan 34.0 11537966 836.
## 5 Afghanistan 36.1 13079460 740.
## 6 Afghanistan 38.4 14880372 786.
## 7 Afghanistan 39.9 12881816 978.
## 8 Afghanistan 40.8 13867957 852.
## 9 Afghanistan 41.7 16317921 649.
## 10 Afghanistan 41.8 22227415 635.
## # ... with 1,694 more rows
Коли змінних дійсно багато, простіше вказати змінні, які хочеться викинути. Для цього використовується знак -
(для відкидання однієї змінної) і !
(для декількох, які необхідно помістити у вектор). Відкинемо змінну континенту:
# Відкидаємо одну змінну за допомогою `-`
gapminder %>%
select(-continent)
## # A tibble: 1,704 x 5
## country year lifeExp pop gdpPercap
## <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan 1952 28.8 8425333 779.
## 2 Afghanistan 1957 30.3 9240934 821.
## 3 Afghanistan 1962 32.0 10267083 853.
## 4 Afghanistan 1967 34.0 11537966 836.
## 5 Afghanistan 1972 36.1 13079460 740.
## 6 Afghanistan 1977 38.4 14880372 786.
## 7 Afghanistan 1982 39.9 12881816 978.
## 8 Afghanistan 1987 40.8 13867957 852.
## 9 Afghanistan 1992 41.7 16317921 649.
## 10 Afghanistan 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Відкинемо змінну континенту та населення:
# Відкидаємо декілька змінних за допомогою `!`
gapminder %>%
select(!c(continent, pop))
## # A tibble: 1,704 x 4
## country year lifeExp gdpPercap
## <fct> <int> <dbl> <dbl>
## 1 Afghanistan 1952 28.8 779.
## 2 Afghanistan 1957 30.3 821.
## 3 Afghanistan 1962 32.0 853.
## 4 Afghanistan 1967 34.0 836.
## 5 Afghanistan 1972 36.1 740.
## 6 Afghanistan 1977 38.4 786.
## 7 Afghanistan 1982 39.9 978.
## 8 Afghanistan 1987 40.8 852.
## 9 Afghanistan 1992 41.7 649.
## 10 Afghanistan 1997 41.8 635.
## # ... with 1,694 more rows
Крім того, за допомогою функцій starts_with()
, ends_with()
, contains()
та matches()
ми можемо задавати специфічні умови відбору змінних.
Для відбору стовпчиків, які починаються на певний вираз використовується starts_with()
. Відберемо змінні, які починаються на co:
gapminder %>%
select(starts_with("c"))
## # A tibble: 1,704 x 2
## country continent
## <fct> <fct>
## 1 Afghanistan Asia
## 2 Afghanistan Asia
## 3 Afghanistan Asia
## 4 Afghanistan Asia
## 5 Afghanistan Asia
## 6 Afghanistan Asia
## 7 Afghanistan Asia
## 8 Afghanistan Asia
## 9 Afghanistan Asia
## 10 Afghanistan Asia
## # ... with 1,694 more rows
Змінні, які закінчуються на англійську літеру p:
gapminder %>%
select(ends_with("p"))
## # A tibble: 1,704 x 3
## lifeExp pop gdpPercap
## <dbl> <int> <dbl>
## 1 28.8 8425333 779.
## 2 30.3 9240934 821.
## 3 32.0 10267083 853.
## 4 34.0 11537966 836.
## 5 36.1 13079460 740.
## 6 38.4 14880372 786.
## 7 39.9 12881816 978.
## 8 40.8 13867957 852.
## 9 41.7 16317921 649.
## 10 41.8 22227415 635.
## # ... with 1,694 more rows
Змінні, які містять англійську літеру x:
gapminder %>%
select(contains("x"))
## # A tibble: 1,704 x 1
## lifeExp
## <dbl>
## 1 28.8
## 2 30.3
## 3 32.0
## 4 34.0
## 5 36.1
## 6 38.4
## 7 39.9
## 8 40.8
## 9 41.7
## 10 41.8
## # ... with 1,694 more rows
При роботі з даними часто використовуються регулярні вирази (regular expression). Це певні патерни тексту, які відповідають певній умові. Ми можемо використовувати їх при відборі змінних за допомогою функції matches()
.
Наприклад відберемо стовпчики, які в назві містять літери op або ap:
gapminder %>%
select(matches("[oa]p"))
## # A tibble: 1,704 x 2
## pop gdpPercap
## <int> <dbl>
## 1 8425333 779.
## 2 9240934 821.
## 3 10267083 853.
## 4 11537966 836.
## 5 13079460 740.
## 6 14880372 786.
## 7 12881816 978.
## 8 13867957 852.
## 9 16317921 649.
## 10 22227415 635.
## # ... with 1,694 more rows
Для більш детального ознайомлення з цією темою рекомендую роботу Friedl (2006).
За допомогою функції where()
можна обирати змінні за їх типом. Наприклад відберемо факторні змінні:
# Зверніть увагу, що умова "is.factor"
# вживається без дужок в середині where()
gapminder %>%
select(where(is.factor))
## # A tibble: 1,704 x 2
## country continent
## <fct> <fct>
## 1 Afghanistan Asia
## 2 Afghanistan Asia
## 3 Afghanistan Asia
## 4 Afghanistan Asia
## 5 Afghanistan Asia
## 6 Afghanistan Asia
## 7 Afghanistan Asia
## 8 Afghanistan Asia
## 9 Afghanistan Asia
## 10 Afghanistan Asia
## # ... with 1,694 more rows
Або залишимо тільки числові змінні:
gapminder %>%
select(where(is.numeric))
## # A tibble: 1,704 x 4
## year lifeExp pop gdpPercap
## <int> <dbl> <int> <dbl>
## 1 1952 28.8 8425333 779.
## 2 1957 30.3 9240934 821.
## 3 1962 32.0 10267083 853.
## 4 1967 34.0 11537966 836.
## 5 1972 36.1 13079460 740.
## 6 1977 38.4 14880372 786.
## 7 1982 39.9 12881816 978.
## 8 1987 40.8 13867957 852.
## 9 1992 41.7 16317921 649.
## 10 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Але і це ще не все: за допомогою функції select()
та everything()
можна змінювати позиції змінних у наборі даних. Спочатку ми вказуємо змінні, які хочемо помістити першими, після чого пишемо everything()
. Наприклад, помістимо всі числові змінні на початку:
gapminder %>%
select(where(is.numeric), everything())
## # A tibble: 1,704 x 6
## year lifeExp pop gdpPercap country continent
## <int> <dbl> <int> <dbl> <fct> <fct>
## 1 1952 28.8 8425333 779. Afghanistan Asia
## 2 1957 30.3 9240934 821. Afghanistan Asia
## 3 1962 32.0 10267083 853. Afghanistan Asia
## 4 1967 34.0 11537966 836. Afghanistan Asia
## 5 1972 36.1 13079460 740. Afghanistan Asia
## 6 1977 38.4 14880372 786. Afghanistan Asia
## 7 1982 39.9 12881816 978. Afghanistan Asia
## 8 1987 40.8 13867957 852. Afghanistan Asia
## 9 1992 41.7 16317921 649. Afghanistan Asia
## 10 1997 41.8 22227415 635. Afghanistan Asia
## # ... with 1,694 more rows
2.3.6 dplyr::relocate()
Насправді в dplyr є окрема функція для гнучкої зміни позиції — relocate()
. За замовчуванням вона поміщає вказані змінні на перші позиції:
gapminder %>%
relocate(pop)
## # A tibble: 1,704 x 6
## pop country continent year lifeExp gdpPercap
## <int> <fct> <fct> <int> <dbl> <dbl>
## 1 8425333 Afghanistan Asia 1952 28.8 779.
## 2 9240934 Afghanistan Asia 1957 30.3 821.
## 3 10267083 Afghanistan Asia 1962 32.0 853.
## 4 11537966 Afghanistan Asia 1967 34.0 836.
## 5 13079460 Afghanistan Asia 1972 36.1 740.
## 6 14880372 Afghanistan Asia 1977 38.4 786.
## 7 12881816 Afghanistan Asia 1982 39.9 978.
## 8 13867957 Afghanistan Asia 1987 40.8 852.
## 9 16317921 Afghanistan Asia 1992 41.7 649.
## 10 22227415 Afghanistan Asia 1997 41.8 635.
## # ... with 1,694 more rows
У функції relocate()
є два додаткових аргументи: .before()
та .after()
. Вони використовуються для вказування місця зміни позиції стовпчика:
# Помістимо стовпчик continent після year
gapminder %>%
relocate(continent, .after = year)
## # A tibble: 1,704 x 6
## country year continent lifeExp pop gdpPercap
## <fct> <int> <fct> <dbl> <int> <dbl>
## 1 Afghanistan 1952 Asia 28.8 8425333 779.
## 2 Afghanistan 1957 Asia 30.3 9240934 821.
## 3 Afghanistan 1962 Asia 32.0 10267083 853.
## 4 Afghanistan 1967 Asia 34.0 11537966 836.
## 5 Afghanistan 1972 Asia 36.1 13079460 740.
## 6 Afghanistan 1977 Asia 38.4 14880372 786.
## 7 Afghanistan 1982 Asia 39.9 12881816 978.
## 8 Afghanistan 1987 Asia 40.8 13867957 852.
## 9 Afghanistan 1992 Asia 41.7 16317921 649.
## 10 Afghanistan 1997 Asia 41.8 22227415 635.
## # ... with 1,694 more rows
# Помістимо стовпчик continent після year
gapminder %>%
relocate(lifeExp, .before = gdpPercap)
## # A tibble: 1,704 x 6
## country continent year pop lifeExp gdpPercap
## <fct> <fct> <int> <int> <dbl> <dbl>
## 1 Afghanistan Asia 1952 8425333 28.8 779.
## 2 Afghanistan Asia 1957 9240934 30.3 821.
## 3 Afghanistan Asia 1962 10267083 32.0 853.
## 4 Afghanistan Asia 1967 11537966 34.0 836.
## 5 Afghanistan Asia 1972 13079460 36.1 740.
## 6 Afghanistan Asia 1977 14880372 38.4 786.
## 7 Afghanistan Asia 1982 12881816 39.9 978.
## 8 Afghanistan Asia 1987 13867957 40.8 852.
## 9 Afghanistan Asia 1992 16317921 41.7 649.
## 10 Afghanistan Asia 1997 22227415 41.8 635.
## # ... with 1,694 more rows
Можна переміщати й одразу цілі групи:
# Помістимо всі цілочислові змінні після факторних
gapminder %>%
relocate(where(is.integer), .after = where(is.factor))
## # A tibble: 1,704 x 6
## country continent year pop lifeExp gdpPercap
## <fct> <fct> <int> <int> <dbl> <dbl>
## 1 Afghanistan Asia 1952 8425333 28.8 779.
## 2 Afghanistan Asia 1957 9240934 30.3 821.
## 3 Afghanistan Asia 1962 10267083 32.0 853.
## 4 Afghanistan Asia 1967 11537966 34.0 836.
## 5 Afghanistan Asia 1972 13079460 36.1 740.
## 6 Afghanistan Asia 1977 14880372 38.4 786.
## 7 Afghanistan Asia 1982 12881816 39.9 978.
## 8 Afghanistan Asia 1987 13867957 40.8 852.
## 9 Afghanistan Asia 1992 16317921 41.7 649.
## 10 Afghanistan Asia 1997 22227415 41.8 635.
## # ... with 1,694 more rows
# Помістимо всі факторні змінні після числових
gapminder %>%
relocate(where(is.numeric), .before = where(is.factor))
## # A tibble: 1,704 x 6
## year lifeExp pop gdpPercap country continent
## <int> <dbl> <int> <dbl> <fct> <fct>
## 1 1952 28.8 8425333 779. Afghanistan Asia
## 2 1957 30.3 9240934 821. Afghanistan Asia
## 3 1962 32.0 10267083 853. Afghanistan Asia
## 4 1967 34.0 11537966 836. Afghanistan Asia
## 5 1972 36.1 13079460 740. Afghanistan Asia
## 6 1977 38.4 14880372 786. Afghanistan Asia
## 7 1982 39.9 12881816 978. Afghanistan Asia
## 8 1987 40.8 13867957 852. Afghanistan Asia
## 9 1992 41.7 16317921 649. Afghanistan Asia
## 10 1997 41.8 22227415 635. Afghanistan Asia
## # ... with 1,694 more rows
2.3.7 dplyr::rename()
Настав час ознайомитися, як змінювати назви стовпчиків. І в цьому нам допоможе функція rename()
.
Змінємо назву стовпчика pop
на population
:
# Спочатку вказуємо нову назву, а після стару
gapminder %>%
rename(population = pop)
## # A tibble: 1,704 x 6
## country continent year lifeExp population gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Можна змінювати регістр назв за допомогою функцій rename_with()
та toupper()
для запису великими літерами або tolower()
для запису маленькими.
Запишемо всі назви великими:
gapminder %>%
rename_with(toupper)
## # A tibble: 1,704 x 6
## COUNTRY CONTINENT YEAR LIFEEXP POP GDPPERCAP
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Згадуючи попередні розділи, можемо враховувати специфічні патерни:
gapminder %>%
rename_with(toupper, ends_with("p"))
## # A tibble: 1,704 x 6
## country continent year LIFEEXP POP GDPPERCAP
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
В роботі досить часто постає питання заміни певної частини назви. Наприклад, прибрати пробіли або замінити їх певний символ. Оскільки в нашому прикладі з назвами все більш-менш нормально, я спеціально вставлю пробіли перед великими літерами в назвах стовпчиків. В цьому випадку нам знову приходять на допомогу регулярні вирази. Результат я збережу у змінній gapminder_rename
:
gapminder_rename <- gapminder %>%
rename_with(~ gsub("([a-z])([A-Z])","\\1 \\2", .x))
gapminder_rename
## # A tibble: 1,704 x 6
## country continent year `life Exp` pop `gdp Percap`
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Використання стовпчиків в назвах яких зустрічаються пробіли незручно. Тому давайте виправимо цю ситуацію. Покажу декілька варіантів. Перший варіант з використанням базової функції gsub()
, де першим аргументом ми вказуємо спочатку що змінюємо, а другим - на що змінюємо.
Замінимо пробіли в назвах стовпчиків на знак підкреслення ("_"):
# Знак "тільда" (~) в R використовуються в якості формули,
# ми познайомимось з її використанням в наступних темах
gapminder_rename %>%
rename_with(~ gsub(" ", "_", .x))
## # A tibble: 1,704 x 6
## country continent year life_Exp pop gdp_Percap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Другий варіант варіант ближчий до синтаксису tidyverse і використовує функції з пакету stringr
, який створений для роботи з текстом:
gapminder_rename %>%
set_names(names(.) %>% str_replace(" ", "_") %>% str_to_title())
## # A tibble: 1,704 x 6
## Country Continent Year Life_exp Pop Gdp_percap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
2.3.8 dplyr::mutate()
Майже жодна маніпуляція з даними не обходиться без створення нових стовпчиків. Для цього використовується функція mutate()
. За замовчуванням нова змінна записується в кінці набору даних.
Давайте розрахуємо загальний ВВП кожної країни в певний момент часу. Для цього створимо нову змінну gdp_billion
в якій перемножимо змінні “кількість населення” на “ВВП на душу населення” та поділимо на один мільярд:
gapminder %>%
mutate(gdp_billion = pop * gdpPercap / 10^9)
## # A tibble: 1,704 x 7
## country continent year lifeExp pop gdpPercap gdp_billion
## <fct> <fct> <int> <dbl> <int> <dbl> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779. 6.57
## 2 Afghanistan Asia 1957 30.3 9240934 821. 7.59
## 3 Afghanistan Asia 1962 32.0 10267083 853. 8.76
## 4 Afghanistan Asia 1967 34.0 11537966 836. 9.65
## 5 Afghanistan Asia 1972 36.1 13079460 740. 9.68
## 6 Afghanistan Asia 1977 38.4 14880372 786. 11.7
## 7 Afghanistan Asia 1982 39.9 12881816 978. 12.6
## 8 Afghanistan Asia 1987 40.8 13867957 852. 11.8
## 9 Afghanistan Asia 1992 41.7 16317921 649. 10.6
## 10 Afghanistan Asia 1997 41.8 22227415 635. 14.1
## # ... with 1,694 more rows
За допомогою аргументів .before
та .after
можна змінювати й позицію запису нової змінної:
gapminder %>%
mutate(gdp_billion = pop * gdpPercap / 10^9,
.after = year)
## # A tibble: 1,704 x 7
## country continent year gdp_billion lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 6.57 28.8 8425333 779.
## 2 Afghanistan Asia 1957 7.59 30.3 9240934 821.
## 3 Afghanistan Asia 1962 8.76 32.0 10267083 853.
## 4 Afghanistan Asia 1967 9.65 34.0 11537966 836.
## 5 Afghanistan Asia 1972 9.68 36.1 13079460 740.
## 6 Afghanistan Asia 1977 11.7 38.4 14880372 786.
## 7 Afghanistan Asia 1982 12.6 39.9 12881816 978.
## 8 Afghanistan Asia 1987 11.8 40.8 13867957 852.
## 9 Afghanistan Asia 1992 10.6 41.7 16317921 649.
## 10 Afghanistan Asia 1997 14.1 41.8 22227415 635.
## # ... with 1,694 more rows
Інколи необхідно свторити стовпчик індексів: порядковий номер спостереження. Для цього можна використати функцію row_number()
:
gapminder %>%
mutate(record = row_number(),
.before = country)
## # A tibble: 1,704 x 7
## record country continent year lifeExp pop gdpPercap
## <int> <fct> <fct> <int> <dbl> <int> <dbl>
## 1 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Або використати базовий синтаксис R:
gapminder %>%
mutate(record = seq(1:n()),
.before = country)
## # A tibble: 1,704 x 7
## record country continent year lifeExp pop gdpPercap
## <int> <fct> <fct> <int> <dbl> <int> <dbl>
## 1 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Якщо ж хочется залишити лише розрахунковий стовпчик — використовується функція transmute()
. При цьому вона дає можливість залишити й певні стовпчики з оригінального набору даних.
Залишимо змінну country
та розрахуємо загальний ВВП:
gapminder %>%
transmute(country,
gdp_billion = pop * gdpPercap / 10^9)
## # A tibble: 1,704 x 2
## country gdp_billion
## <fct> <dbl>
## 1 Afghanistan 6.57
## 2 Afghanistan 7.59
## 3 Afghanistan 8.76
## 4 Afghanistan 9.65
## 5 Afghanistan 9.68
## 6 Afghanistan 11.7
## 7 Afghanistan 12.6
## 8 Afghanistan 11.8
## 9 Afghanistan 10.6
## 10 Afghanistan 14.1
## # ... with 1,694 more rows
2.3.9 dplyr::group_by() та summarise()
Ще однією популярною задачею є групування та агрегація даних: розрахунок певних статистик чи значень для окремих груп.
Для цього використовується функції group_by()
та summarise()
, які, як правило, використовуються разом.
Для прикладу, розрахуємо середню (mean()
) очікувану тривалість життя для кожного континенту. Для цього спочатку групуємо дані, а потім розраховуємо статистичний показник, який назвемо mean_life_exp
:
gapminder %>%
group_by(continent) %>%
summarise(mean_life_exp = mean(lifeExp))
## # A tibble: 5 x 2
## continent mean_life_exp
## <fct> <dbl>
## 1 Africa 48.9
## 2 Americas 64.7
## 3 Asia 60.1
## 4 Europe 71.9
## 5 Oceania 74.3
Аналогічно, ми можемо розраховувати одночасно декілька статистик. Додамо до розрахунку медіану (median()
) та стандартне відхилення (sd()
) очікуваної тривалості життя:
gapminder %>%
group_by(continent) %>%
summarise(mean_life_exp = mean(lifeExp),
md_life_exp = median(lifeExp),
sd_life_exp = sd(lifeExp))
## # A tibble: 5 x 4
## continent mean_life_exp md_life_exp sd_life_exp
## <fct> <dbl> <dbl> <dbl>
## 1 Africa 48.9 47.8 9.15
## 2 Americas 64.7 67.0 9.35
## 3 Asia 60.1 61.8 11.9
## 4 Europe 71.9 72.2 5.43
## 5 Oceania 74.3 73.7 3.80
Слід звернути увагу, що всі зазначені вище статистики в R чутливі до пропущених значень: у разі їх наявності, функції будуть повертати NA
(not available). Для того, щоб при розрахунку статистик не враховувались пропущені значення необхідно додати аргумент na.rm = TRUE
.
2.3.10 dplyr::across()
Але що, якщо нам необхідно розрахувати, наприклад, середні значення для всіх числових стовпчиків? Звичайно можна перерахувати їх всіх вручну:
gapminder %>%
group_by(continent) %>%
summarise(mean_life_exp = mean(lifeExp),
mean_pop = mean(pop),
mean_gdpPercap = mean(gdpPercap))
## # A tibble: 5 x 4
## continent mean_life_exp mean_pop mean_gdpPercap
## <fct> <dbl> <dbl> <dbl>
## 1 Africa 48.9 9916003. 2194.
## 2 Americas 64.7 24504795. 7136.
## 3 Asia 60.1 77038722. 7902.
## 4 Europe 71.9 17169765. 14469.
## 5 Oceania 74.3 8874672. 18622.
Але якщо числових змінних багато — такий підхід буде неоптимальним.
В таких випадках краще скористатися функцією across()
. Продемонструю декілька варіантів.
Для початку розрахуємо, як в попередньому прикладі, середні значення для числових змінних:
gapminder %>%
select(-year) %>% # прибираємо змінну року
group_by(continent) %>%
summarise(across(where(is.numeric), mean))
## # A tibble: 5 x 4
## continent lifeExp pop gdpPercap
## <fct> <dbl> <dbl> <dbl>
## 1 Africa 48.9 9916003. 2194.
## 2 Americas 64.7 24504795. 7136.
## 3 Asia 60.1 77038722. 7902.
## 4 Europe 71.9 17169765. 14469.
## 5 Oceania 74.3 8874672. 18622.
Або можна перелічити потрібні змінні у векторі:
gapminder %>%
group_by(continent) %>%
summarise(across(lifeExp:gdpPercap, mean))
## # A tibble: 5 x 4
## continent lifeExp pop gdpPercap
## <fct> <dbl> <dbl> <dbl>
## 1 Africa 48.9 9916003. 2194.
## 2 Americas 64.7 24504795. 7136.
## 3 Asia 60.1 77038722. 7902.
## 4 Europe 71.9 17169765. 14469.
## 5 Oceania 74.3 8874672. 18622.
Якщо уважно придивитися до результатів, то видно, що результати розрахунку повертаються з тими ж названими, як в оригінальному наборі даних. Якщо показати такий розрахунок третій стороні - можуть виникнути проблеми сприйняття. Як бути? Насправді функція across()
досить гнучка і розробники передбачили такий варіант. Для цього нам знадобиться аргумент .names
, де ми вкажемо правило запису назв результуючих змінних:
gapminder %>%
group_by(continent) %>%
summarise(across(lifeExp:gdpPercap,
mean,
.names = "mean_{.col}"))
## # A tibble: 5 x 4
## continent mean_lifeExp mean_pop mean_gdpPercap
## <fct> <dbl> <dbl> <dbl>
## 1 Africa 48.9 9916003. 2194.
## 2 Americas 64.7 24504795. 7136.
## 3 Asia 60.1 77038722. 7902.
## 4 Europe 71.9 17169765. 14469.
## 5 Oceania 74.3 8874672. 18622.
І навіть розрахунки декількох різних статистик теж можливий. Для цього їх перелік необхідно записати списоком в аргументів функції across()
:
gapminder %>%
group_by(continent) %>%
summarise(across(lifeExp:gdpPercap,
list(avg = mean, stdev = sd, md = median),
.names = "{.fn}_{.col}"))
## # A tibble: 5 x 10
## continent avg_lifeExp stdev_lifeExp md_lifeExp avg_pop stdev_pop md_pop avg_gdpPercap
## <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Africa 48.9 9.15 47.8 9.92e6 1.55e7 4.58e6 2194.
## 2 Americas 64.7 9.35 67.0 2.45e7 5.10e7 6.23e6 7136.
## 3 Asia 60.1 11.9 61.8 7.70e7 2.07e8 1.45e7 7902.
## 4 Europe 71.9 5.43 72.2 1.72e7 2.05e7 8.55e6 14469.
## 5 Oceania 74.3 3.80 73.7 8.87e6 6.51e6 6.40e6 18622.
## # ... with 2 more variables: stdev_gdpPercap <dbl>, md_gdpPercap <dbl>
2.3.11 dplyr::count()
Якщо нам необхідно порахувати кількість значень в групі, то це можна зробити за допомогою group_by()
та summarise()
. Підрахуємо кількість країн на кожному континенті у наборі даних. Оскільки для кожної країни є 12 спостережень (з 1952 по 2007 рік кожні 5 років), необхідно враховувати тільки унікальні значення - для таких випадків є функція distinct()
:
gapminder %>%
group_by(continent) %>%
distinct(country) %>% # унікальні значення
summarise(n = n())
## # A tibble: 5 x 2
## continent n
## <fct> <int>
## 1 Africa 52
## 2 Americas 25
## 3 Asia 33
## 4 Europe 30
## 5 Oceania 2
Але є простіший варіант - через функцію count()
:
2.3.12 dplyr::case_when()
Яка маніпуляція з даними обходиться без конструкції “if - else?” В базовому синтаксисі R є функція ifelse()
але я пропоную користуватися векторизованим варіантом з dplyr case_when()
.
Для прикладу давайте створимо нову номінативну змінну, яка буде оцінювати середню очікувану тривалість життя в форматі “низька-середня-висока”:
gapminder %>%
group_by(country) %>%
summarise(mean_life_exp = mean(lifeExp)) %>%
mutate(
bin_life = case_when(
mean_life_exp > 70 ~ "hight", # умова 1
mean_life_exp > 50 & mean_life_exp <= 70 ~ "medium", # умова 2
TRUE ~ "low" # для всіх інших випадків
)
)
## # A tibble: 142 x 3
## country mean_life_exp bin_life
## <fct> <dbl> <chr>
## 1 Afghanistan 37.5 low
## 2 Albania 68.4 medium
## 3 Algeria 59.0 medium
## 4 Angola 37.9 low
## 5 Argentina 69.1 medium
## 6 Australia 74.7 hight
## 7 Austria 73.1 hight
## 8 Bahrain 65.6 medium
## 9 Bangladesh 49.8 low
## 10 Belgium 73.6 hight
## # ... with 132 more rows
2.3.13 Обертання даних: pivot()
Бувають випадки, коли дані приходять в “неохайному,” незручному форматі:
одна змінна розташована у декількох стовпчиках.
одне спостереження може бути розкидане по кільком рядкам.
Типовим прикладом таких даних можуть бути дані Всесвітнього банку. Давайте завантажимо їх і подивимось, як привести їх до “охайних.” В таких випадках нам допоможуть функції pivot_longer()
та pivot_wider()
з пакету tidyr
(частина tidyverse).
В якості прикладу завантажимо інформацію по показнику Прямі іноземні інвестиції (Foreign direct investment, net (BoP, current US$)).
Дані можна завантажити з мого репозитарію GitHub: прямі іноземні інвестиції, дані Всісвітного банку. Давайте переглянемо їх 2.1.На що звертаємо увагу:
Перші 4 рядочки неінформативні.
Дані зберігаються у “широкому форматі”: змінна року розташована горизонтально, що може ускладнити аналіз.
Завантажимо дані і одразу пропустимо перші 4 рядки за допомогою аргументу skip = 4
:
fdi <- read_csv("https://raw.githubusercontent.com/Aranaur/datasets/main/datasets/world_bank/Foreign_direct_investment/FDI.csv",
skip = 4)
fdi
## # A tibble: 266 x 66
## `Country Name` `Country Code` `Indicator Name` `Indicator Code` `1960` `1961` `1962`
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 Aruba ABW Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 2 Africa Easter~ AFE Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 3 Afghanistan AFG Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 4 Africa Wester~ AFW Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 5 Angola AGO Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 6 Albania ALB Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 7 Andorra AND Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 8 Arab World ARB Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 9 United Arab E~ ARE Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## 10 Argentina ARG Foreign direct ~ BN.KLT.DINV.CD NA NA NA
## # ... with 256 more rows, and 59 more variables: `1963` <dbl>, `1964` <dbl>,
## # `1965` <dbl>, `1966` <dbl>, `1967` <dbl>, `1968` <dbl>, `1969` <dbl>,
## # `1970` <dbl>, `1971` <dbl>, `1972` <dbl>, `1973` <dbl>, `1974` <dbl>,
## # `1975` <dbl>, `1976` <dbl>, `1977` <dbl>, `1978` <dbl>, `1979` <dbl>,
## # `1980` <dbl>, `1981` <dbl>, `1982` <dbl>, `1983` <dbl>, `1984` <dbl>,
## # `1985` <dbl>, `1986` <dbl>, `1987` <dbl>, `1988` <dbl>, `1989` <dbl>,
## # `1990` <dbl>, `1991` <dbl>, `1992` <dbl>, `1993` <dbl>, `1994` <dbl>, ...
Використаємо функцію pivot_longer()
для того щоб змінна року була окремим стовпчиком:
fdi_longer <- fdi %>%
# змінимо формат назв стовпчиків
set_names(names(.) %>% str_to_lower() %>% str_replace(" ", "_")) %>%
# приберемо зайві
select(-c(indicator_name, indicator_code)) %>%
# обертаємо дані
pivot_longer(cols = -c(country_name, country_code), # використовуємо всі
# крім country_name, country_code
names_to = "year", # назва нового стовпчика
# (роки з початкового набору даних)
values_to = "fdi") %>% # назва нового стовпчика
# (значення ПІІ до кожного року)
drop_na() # приберемо пропущені значення
fdi_longer
## # A tibble: 6,473 x 4
## country_name country_code year fdi
## <chr> <chr> <chr> <dbl>
## 1 Aruba ABW 1990 -130502793.
## 2 Aruba ABW 1991 -184748603.
## 3 Aruba ABW 1992 36983240.
## 4 Aruba ABW 1993 17932961.
## 5 Aruba ABW 1994 73184358.
## 6 Aruba ABW 1995 5530726.
## 7 Aruba ABW 1996 -84134078.
## 8 Aruba ABW 1997 -197597765.
## 9 Aruba ABW 1998 -82178771.
## 10 Aruba ABW 1999 -469329609.
## # ... with 6,463 more rows
З таким набором даних буде працювати значно зручніше.
Для прикладу роботи функції pivot_wider()
повернемося до датасету gapminder
:
gapminder
## # A tibble: 1,704 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
## 7 Afghanistan Asia 1982 39.9 12881816 978.
## 8 Afghanistan Asia 1987 40.8 13867957 852.
## 9 Afghanistan Asia 1992 41.7 16317921 649.
## 10 Afghanistan Asia 1997 41.8 22227415 635.
## # ... with 1,694 more rows
Перевернемо його у широкий формат:
gapminder_wide <- gapminder %>%
pivot_wider(names_from = year,
values_from = c(lifeExp:gdpPercap))
gapminder_wide
## # A tibble: 142 x 38
## country continent lifeExp_1952 lifeExp_1957 lifeExp_1962 lifeExp_1967 lifeExp_1972
## <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Afghanis~ Asia 28.8 30.3 32.0 34.0 36.1
## 2 Albania Europe 55.2 59.3 64.8 66.2 67.7
## 3 Algeria Africa 43.1 45.7 48.3 51.4 54.5
## 4 Angola Africa 30.0 32.0 34 36.0 37.9
## 5 Argentina Americas 62.5 64.4 65.1 65.6 67.1
## 6 Australia Oceania 69.1 70.3 70.9 71.1 71.9
## 7 Austria Europe 66.8 67.5 69.5 70.1 70.6
## 8 Bahrain Asia 50.9 53.8 56.9 59.9 63.3
## 9 Banglade~ Asia 37.5 39.3 41.2 43.5 45.3
## 10 Belgium Europe 68 69.2 70.2 70.9 71.4
## # ... with 132 more rows, and 31 more variables: lifeExp_1977 <dbl>,
## # lifeExp_1982 <dbl>, lifeExp_1987 <dbl>, lifeExp_1992 <dbl>, lifeExp_1997 <dbl>,
## # lifeExp_2002 <dbl>, lifeExp_2007 <dbl>, pop_1952 <int>, pop_1957 <int>,
## # pop_1962 <int>, pop_1967 <int>, pop_1972 <int>, pop_1977 <int>, pop_1982 <int>,
## # pop_1987 <int>, pop_1992 <int>, pop_1997 <int>, pop_2002 <int>, pop_2007 <int>,
## # gdpPercap_1952 <dbl>, gdpPercap_1957 <dbl>, gdpPercap_1962 <dbl>,
## # gdpPercap_1967 <dbl>, gdpPercap_1972 <dbl>, gdpPercap_1977 <dbl>, ...
В переважній більшості випадків вам знадобиться довгий формат даних. Але при потребі зберігання великих файлів - краще зберігати у широкому форматі, оскільки це збереже місце на диску.
Збережемо результати широкого і довго формату набору даних gapminder
та перевіремо їх розмір:
write_csv(gapminder, "docs/data/gapminder.csv")
write_csv(gapminder_wide, "docs/data/gapminder_wide.csv")
fs::file_size("docs/data/gapminder.csv")
## 80.2K
fs::file_size("docs/data/gapminder_wide.csv")
## 47.3K
Широкий формат в 1.7 рази менший.