class: center, middle, inverse, title-slide # Язык R и его применение в биоинформатике ### Анна Валяева ### 30.09.2022 --- # Столбчатая диаграмма <style> html { padding: unset;} body { padding: unset;} </style> .pull-left[ По умолчанию `position = "stack"` ```r ggplot(penguins, aes(x = species, fill = sex)) + geom_bar(color = "#F4F1BB", width = 0.8) + scale_fill_manual(values = c("#ED6A5A", "#9BC1BC")) + theme_minimal() ``` <img src="data:image/png;base64,#figs/geom_barplot_stack-1.png" width="100%" style="display: block; margin: auto;" /> ] .pull-right[ Можно поменять на `position = "dodge"` ```r ggplot(penguins, aes(x = species, fill = sex)) + geom_bar( color = "#F4F1BB", width = 0.8, * position = "dodge") + scale_fill_manual(values = c("#ED6A5A", "#9BC1BC")) + theme_minimal() ``` <img src="data:image/png;base64,#figs/geom_barplot_dodge-1.png" width="100%" style="display: block; margin: auto;" /> ] --- # Стековая столбчатая диаграмма `position = "fill"` преобразует каунты в доли/проценты ```r ggplot(penguins, aes(x = species, fill = island)) + geom_bar( color = "#F4F1BB", width = 0.8, * position = "fill") + * scale_y_continuous(labels = scales::percent) + scale_fill_manual(values = c("#ED6A5A", "#F4F1BB", "#9BC1BC")) + theme_minimal() ``` <img src="data:image/png;base64,#figs/geom_barplot_fill-1.png" width="70%" style="display: block; margin: auto;" /> --- # Столбчатая диаграмма Если уже есть столбец с числами, которые хотим изобразить, то нужно использовать `geom_bar(stat = "identity")` или `geom_col()`. ```r penguins %>% count(species, year) %>% ggplot(aes(x = species, y = n, fill = as_factor(year))) + geom_col(position = "dodge") + scale_fill_manual(values = c("#ED6A5A", "#F4F1BB", "#9BC1BC")) + theme_minimal() ``` <img src="data:image/png;base64,#figs/geom_col-1.png" width="70%" style="display: block; margin: auto;" /> --- # Добавление текста ```r penguins %>% count(species, year) %>% ggplot(aes(x = species, y = n, fill = as_factor(year))) + geom_col(position = "dodge") + * geom_text( * aes(label = n, y = n + 2, group = as_factor(year)), * position = position_dodge(width = .9)) + scale_fill_manual(values = c("#ED6A5A", "#F4F1BB", "#9BC1BC")) + theme_minimal() ``` <img src="data:image/png;base64,#figs/geom_text-1.png" width="80%" style="display: block; margin: auto;" /> --- # Столбчатая диаграмма, изображающая статистики ```r ggplot(penguins, aes(x = species, y = flipper_length_mm)) + * geom_bar(stat = "summary", fun = "mean") ``` <img src="data:image/png;base64,#figs/geom_bar_sum-1.png" width="80%" style="display: block; margin: auto;" /> --- # Добавление величины ошибки ```r penguins_stat <- penguins %>% * group_by(species) %>% * summarise( * avg_flipper_mm = mean(flipper_length_mm, na.rm = TRUE), * min_flipper_mm = avg_flipper_mm - sd(flipper_length_mm, na.rm = TRUE), * max_flipper_mm = avg_flipper_mm + sd(flipper_length_mm, na.rm = TRUE)) penguins_stat ``` ``` # A tibble: 3 x 4 species avg_flipper_mm min_flipper_mm max_flipper_mm <chr> <dbl> <dbl> <dbl> 1 Adelie 190. 183. 196. 2 Chinstrap 196. 189. 203. 3 Gentoo 217. 211. 224. ``` --- # Добавление величины ошибки ```r ggplot(penguins_stat) + geom_bar(aes(x = species, y = avg_flipper_mm, fill = species), * stat = "identity") + * geom_errorbar(aes(x = species, ymin = min_flipper_mm, ymax = max_flipper_mm), width = 0.2) ``` <img src="data:image/png;base64,#figs/geom_bar_error-1.png" width="80%" style="display: block; margin: auto;" /> --- ## Альтернатива - пакет ggpubr ```r library(ggpubr) ggbarplot(penguins, x = "species", y = "flipper_length_mm", fill = "species", add = "mean_sd") ``` <img src="data:image/png;base64,#figs/ggbarplot-1.png" width="80%" style="display: block; margin: auto;" /> --- # ggplot2 <br> <br> <img src="data:image/png;base64,#img/ggplot2/ggplot2_scheme.PNG" width="90%" style="display: block; margin: auto;" /> --- # Длинный и широкий формат - Из широкого в длинный - `pivot_longer` - Из длинного в широкий - `pivot_wider` <img src="data:image/png;base64,#img/tidyr/wide_long.png" width="80%" style="display: block; margin: auto;" /> --- # Широкий формат ```r relig_income ``` ``` # A tibble: 18 x 11 religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` `$75-100k` <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 Agnostic 27 34 60 81 76 137 122 2 Atheist 12 27 37 52 35 70 73 3 Buddhist 27 21 30 34 33 58 62 4 Catholic 418 617 732 670 638 1116 949 5 Don’t k~ 15 14 15 11 10 35 21 6 Evangel~ 575 869 1064 982 881 1486 949 7 Hindu 1 9 7 9 11 34 47 8 Histori~ 228 244 236 238 197 223 131 9 Jehovah~ 20 27 24 24 21 30 15 10 Jewish 19 19 25 25 30 95 69 11 Mainlin~ 289 495 619 655 651 1107 939 12 Mormon 29 40 48 51 56 112 85 13 Muslim 6 7 9 10 9 23 16 14 Orthodox 13 17 23 32 32 47 38 15 Other C~ 9 7 11 13 13 14 18 16 Other F~ 20 33 40 46 49 63 46 17 Other W~ 5 2 3 4 2 7 3 18 Unaffil~ 217 299 374 365 341 528 407 # ... with 3 more variables: $100-150k <dbl>, >150k <dbl>, # Don't know/refused <dbl> ``` --- # Pivot_longer Для построения графиков с **{ggplot2}**, работы с группами категорий. ```r relig_income %>% pivot_longer(!religion, names_to = "income", values_to = "count") ``` ``` # A tibble: 180 x 3 religion income count <chr> <chr> <dbl> 1 Agnostic <$10k 27 2 Agnostic $10-20k 34 3 Agnostic $20-30k 60 4 Agnostic $30-40k 81 5 Agnostic $40-50k 76 6 Agnostic $50-75k 137 7 Agnostic $75-100k 122 8 Agnostic $100-150k 109 9 Agnostic >150k 84 10 Agnostic Don't know/refused 96 # ... with 170 more rows ``` --- # Длинный формат ```r fish_encounters ``` ``` # A tibble: 114 x 3 fish station seen <fct> <fct> <int> 1 4842 Release 1 2 4842 I80_1 1 3 4842 Lisbon 1 4 4842 Rstr 1 5 4842 Base_TD 1 6 4842 BCE 1 7 4842 BCW 1 8 4842 BCE2 1 9 4842 BCW2 1 10 4842 MAE 1 # ... with 104 more rows ``` --- # Pivot_wider Для построения тепловых карт. ```r fish_encounters %>% pivot_wider(names_from = fish, values_from = seen) ``` ``` # A tibble: 11 x 20 station `4842` `4843` `4844` `4845` `4847` `4848` `4849` `4850` `4851` `4854` <fct> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int> 1 Release 1 1 1 1 1 1 1 1 1 1 2 I80_1 1 1 1 1 1 1 1 1 1 1 3 Lisbon 1 1 1 1 1 1 NA NA NA NA 4 Rstr 1 1 1 1 NA 1 NA 1 NA NA 5 Base_TD 1 1 1 1 NA NA NA 1 NA NA 6 BCE 1 1 1 NA NA NA NA 1 NA NA 7 BCW 1 1 1 NA NA NA NA 1 NA NA 8 BCE2 1 1 1 NA NA NA NA NA NA NA 9 BCW2 1 1 1 NA NA NA NA NA NA NA 10 MAE 1 1 1 NA NA NA NA NA NA NA 11 MAW 1 1 1 NA NA NA NA NA NA NA # ... with 9 more variables: 4855 <int>, 4857 <int>, 4858 <int>, 4859 <int>, # 4861 <int>, 4862 <int>, 4863 <int>, 4864 <int>, 4865 <int> ``` --- # 3 aes - 3 столбца ```r drop_na(penguins, sex) %>% ggplot() + geom_boxplot(aes(x = species, y = bill_length_mm, fill = sex)) ``` <img src="data:image/png;base64,#figs/long-format-1.png" width="80%" style="display: block; margin: auto;" /> --- # Длинный формат .pull-left[ ```r penguins %>% * pivot_longer(cols = c(bill_length_mm, bill_depth_mm)) %>% select(species, name, value) ``` ``` # A tibble: 688 x 3 species name value <chr> <chr> <dbl> 1 Adelie bill_length_mm 39.1 2 Adelie bill_depth_mm 18.7 3 Adelie bill_length_mm 39.5 4 Adelie bill_depth_mm 17.4 5 Adelie bill_length_mm 40.3 6 Adelie bill_depth_mm 18 7 Adelie bill_length_mm NA 8 Adelie bill_depth_mm NA 9 Adelie bill_length_mm 36.7 10 Adelie bill_depth_mm 19.3 # ... with 678 more rows ``` ] .pull-right[ ```r penguins %>% * pivot_longer(cols = c(bill_length_mm, bill_depth_mm)) %>% ggplot() + geom_boxplot(aes(x = species, y = value, fill = name)) ``` <img src="data:image/png;base64,#figs/long-format-2-pl-1.png" width="80%" style="display: block; margin: auto;" /> ] --- # Facets ```r drop_na(penguins, sex) %>% pivot_longer(cols = c(bill_length_mm, bill_depth_mm)) %>% ggplot() + geom_boxplot(aes(x = sex, y = value, fill = name)) + * facet_wrap(~ species) + * theme(legend.position = "none") ``` <img src="data:image/png;base64,#figs/facet_box-1.png" width="80%" style="display: block; margin: auto;" /> --- # Названия панелек ```r drop_na(penguins, sex) %>% pivot_longer(cols = c(bill_length_mm, bill_depth_mm)) %>% ggplot() + geom_boxplot(aes(x = sex, y = value, fill = name)) + facet_wrap(~ species, * labeller = as_labeller(c("Adelie" = "Адели", * "Chinstrap" = "Антарктический", * "Gentoo" = "Субантарктический"))) + theme(legend.position = "none") ``` <img src="data:image/png;base64,#figs/facet_box2-1.png" width="75%" style="display: block; margin: auto;" /> --- class: inverse, center, middle # Функции --- # Функции - Если вы заметили, что несколько раз используете один и тот же код, то запишите его в функцию. ```r df <- tibble( a = 1:10, b = rnorm(10), c = runif(10)) # возведем все столбцы в куб df$a <- df$a ** 3 df$b <- df$b * 3 df$c <- df$c **3 ``` --- # Функции - Если вы заметили, что несколько раз используете один и тот же код, то запишите его в функцию. ```r cube <- function(x) { x ** 3 } cube(x = 2) ``` ``` [1] 8 ``` ```r df$a <- cube(df$a) df$b <- cube(df$b) df$c <- cube(df$c) ``` --- # Функции Нужно придумать: - **имя** функции. Оно не должно совпадать с именами функций из базового R или пакетов, которые вы используете. В идеале оно отражает смысл вашей функции. ```r # не делайте так! mean <- function(x) { sum(x) } ``` - список **параметров**, которые функция принимает на вход. Например, `function(x, y, z)`. - сам код, выполняющий работу, который вы записываете в **тело** функции внутри `{...}`. ```r smart_name <- function(input1, input2, param3) { ... body ... } ``` --- # Функции - У функции может быть 0 или несколько параметров. - Функция может возвращать максимум 1 объект. ```r # ничего не требует say_hello <- function() { print("hello!") } # ничего не возвращает save_res <- function(df) { df = df[df$pval < 0.05, c(1,3:5)] write.csv(df, "path-to-file.csv") } ``` --- # Взглянуть на код функции И на свою функцию посмотреть: ```r cube ``` ``` function(x) { x ** 3 } <bytecode: 0x000000001666e198> ``` И на чужую: ```r xor ``` ``` function (x, y) { (x | y) & !(x & y) } <bytecode: 0x0000000024f05dd0> <environment: namespace:base> ``` --- # Return - Функция возвращает результат последнего выражения либо то, что указано как `return(...)`. .pull-left[ ```r cube_or_not <- function(x) { x ** 3 } cube_or_not(2) ``` ] .pull-right[ ```r cube_or_not <- function(x) { return(x * 3) x ** 3 } cube_or_not(2) ``` ] --- .pull-left[ ## Implicit return ```r check_sign_i <- function(x) { # check if x is positive if (x > 0) { "positive" } # check if x is negative else if (x < 0) { "negative" } # check if x is not positive nor negative else { "zero" } } check_sign_i(10) ``` ``` [1] "positive" ``` ] .pull-right[ ## Explicit return ```r check_sign_e <- function(x) { # check if x is positive if (x > 0) { return("positive") } # check if x is negative else if (x < 0) { return("negative") } # check if x is not positive nor negative else { return("zero") } } check_sign_e(10) ``` ``` [1] "positive" ``` ] --- # Выполнение кода по условию ```r if (condition) { # что делать, когда condition = TRUE } else { # что делать, когда condition = FALSE } ``` Логическое выражение `condition` должно возвращать либо TRUE, либо FALSE. --- # Выполнение кода по условию Логическое выражение `condition` должно возвращать либо TRUE, либо FALSE. - Если `condition` - это вектор, то будет предупреждение: ```r if (c(TRUE, FALSE)) {} ``` ``` Warning in if (c(TRUE, FALSE)) {: the condition has length > 1 and only the first element will be used ``` ``` NULL ``` - Если `condition` - это пропущенное значение, то будет ошибка: ```r if (NA) {} ``` ``` Error in if (NA) {: missing value where TRUE/FALSE needed ``` --- # Несколько условий ```r if (this) { # делай это } else if (that) { # делай что-то другое } else { # делай что-то третье } ``` Не путайте `else if () {...}` с `ifelse()`. --- # Несколько условий Если условий слишком много, то в них можно запутаться. Тогда лучше использовать другие подходы. Например, использовать `switch()`. ```r centre <- function(x, type) { switch(type, mean = mean(x), median = median(x), trimmed = mean(x, trim = .1), stop("Unknown central tendency!")) } ``` .pull-left[ ```r set.seed(123) x <- rnorm(10) centre(x, "mean") ``` ``` [1] 0.07462564 ``` ] .pull-right[ ```r centre(x, "median") ``` ``` [1] -0.07983455 ``` ```r centre(x, "trimmed") ``` ``` [1] 0.03703159 ``` ```r centre(x, "mode") ``` ``` Error in centre(x, "mode"): Unknown central tendency! ``` ] --- # Параметры Через параметры на вход функции передаются **данные** или какие-то **детали**. Обычно данные передаются первому параметру. В таком случае эту функцию будет легко использовать с ` %>% `. Для параметров можно задать значение по умолчанию: ```r centre <- function(x, type = "mean") { switch(type, mean = mean(x), median = median(x), trimmed = mean(x, trim = .1), stop("Unknown central tendency!")) } centre(rnorm(10)) ``` ``` [1] 0.208622 ``` Если при вызове функции вы заменяете значение по умолчанию, то указывайте название параметра (не надейтесь только на позицию). Так всем будет понятнее. --- # Названия параметров Идеи для названий параметров: - `x, y, z` - вектора, - `w` - вектор весов, - `df` - датафрейм, - `i, j` - индексы (строки и столбцы), - `n` - длина или число строк, - `p` - число столбцов. --- # Провека формата входных данных В каком случае нужно остановиться. .pull-left[ ## if + stop ```r cube <- function(x) { * if (!is.numeric(x)) { * stop("`x` must be numeric") * } x ** 3 } cube("twelve") ``` ``` Error in cube("twelve"): `x` must be numeric ``` ] .pull-left[ ## stopifnot ```r cube <- function(x) { * stopifnot(is.numeric(x)) x ** 3 } cube("twelve") ``` ``` Error in cube("twelve"): is.numeric(x) is not TRUE ``` ] --- # Multiple returns Чтобы функция возвращала несколько объектов, нужно эти объекты возвращать в виде списка. ```r return_two_and_four <- function(){ list(2, 4) } return_two_and_four() ``` ``` [[1]] [1] 2 [[2]] [1] 4 ``` --- # Локальные переменные `x` внутри функции (в ее среде) и вне функции (в глобальной среде) существуют независимо. ```r x <- 1000 add_ten <- function(x){ x + 10 } add_ten(32) ``` ``` [1] 42 ``` ```r x ``` ``` [1] 1000 ``` --- # Глобальные переменные Изнутри функции можно переписать глобальную переменную с помощью оператора ` <<- `. ```r x <- 1000 add_ten <- function(x){ x <<- 32 x + 10 } add_ten(32) ``` ``` [1] 42 ``` ```r x ``` ``` [1] 32 ``` --- # Глобальные переменные Если R не нашел переменную в среде функции, то он будет искать ее в глобальной среде. ```r y <- 1000 add_ten <- function(){ y + 10 } add_ten() ``` ``` [1] 1010 ``` --- class: inverse, center, middle # Работа с табличными данными --- # Работа с табличными данными Очень широкий датафрейм про лемуров из Duke Lemur Center. Как привести его к форме `name`-`weight_1`-`weight_2`-`weight_3`? ```r lemurs_weights_wide ``` ``` # A tibble: 3 x 52 weight_date Agatha Angelique `Annabel Lee` `Ardrey-A` Ardrey `Bellatrix-A` <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> 1 weight_1 1060 2920 944 98 3000 NA 2 weight_2 1860 2940 1180 98 2780 NA 3 weight_3 2000 209 1689 95 666 NA # ... with 45 more variables: Bellatrix-B <lgl>, Bellatrix-C <lgl>, # Bellatrix <dbl>, Blue Devil <dbl>, Caliban <dbl>, Claudia <dbl>, # Cruella <dbl>, Damien <lgl>, Elphaba <dbl>, Endora <dbl>, Goblin <dbl>, # Grendel <dbl>, Hitchcock <dbl>, Ichabod <dbl>, Imp <lgl>, Kali <dbl>, # Kambana <lgl>, Loki <lgl>, Lucrezia <dbl>, Medea <dbl>, Medusa <dbl>, # Mephistopheles <dbl>, Merlin <dbl>, Morticia <dbl>, Niffy <lgl>, # Norman Bates <dbl>, Nosferatu <dbl>, Ozma-A <dbl>, Ozma <dbl>, ... ``` --- # Работа с табличными данными Как привести его к форме `name`-`weight_1`-`weight_2`-`weight_3`? ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix-A NA NA NA 7 Bellatrix-B NA NA NA 8 Bellatrix-C NA NA NA 9 Bellatrix 585 2760 2460 10 Blue Devil 1330 1820 2460 # ... with 41 more rows ``` --- # Работа с табличными данными Как привести его к форме `name`-`weight_1`-`weight_2`-`weight_3`? ```r lemurs_weights <- lemurs_weights_wide %>% pivot_longer(-weight_date) %>% pivot_wider(names_from = weight_date, values_from = value) lemurs_weights ``` ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix-A NA NA NA 7 Bellatrix-B NA NA NA 8 Bellatrix-C NA NA NA 9 Bellatrix 585 2760 2460 10 Blue Devil 1330 1820 2460 # ... with 41 more rows ``` --- ## Как еще можно указать множество столбцов? - Использовать информацию о типе данных: `where(is.character)`, ... ```r lemurs_weights_wide %>% * pivot_longer(!where(is.character)) %>% pivot_wider(names_from = weight_date, values_from = value) lemurs_weights_wide %>% * pivot_longer(where(is.logical) | where(is.numeric)) %>% pivot_wider(names_from = weight_date, values_from = value) ``` --- ## Как еще можно указать множество столбцов? - `starts_with("pattern")` - начинается с "pattern" - `ends_with("pattern")` - заканчивается на "pattern" - `contains("pattern")` - содержит подслово "pattern" - `matches("pattern")` - находится по регулярному выражению "pattern" ```r lemurs_weights %>% select(starts_with("weight")) %>% head(1) ``` ``` # A tibble: 1 x 3 weight_1 weight_2 weight_3 <dbl> <dbl> <dbl> 1 1060 1860 2000 ``` ```r lemurs_weights %>% select(matches("*_[12]")) %>% head(1) ``` ``` # A tibble: 1 x 2 weight_1 weight_2 <dbl> <dbl> 1 1060 1860 ``` --- ## Как еще можно указать множество столбцов? - `num_range()` - поиск по общему префиксу среди столбцов с некой нумерацией ```r lemurs_weights %>% select(num_range("weight_", c(1,3))) %>% # prefix, numeric range head(1) ``` ``` # A tibble: 1 x 2 weight_1 weight_3 <dbl> <dbl> 1 1060 2000 ``` --- ## Как еще можно указать множество столбцов? - Использовать информацию о позиции столбца ```r lemurs_weights %>% select(1, num_range("weight_", c(1,3))) %>% head(1) ``` ``` # A tibble: 1 x 3 name weight_1 weight_3 <chr> <dbl> <dbl> 1 Agatha 1060 2000 ``` --- ## Как еще можно указать множество столбцов? - Добавить условие по значениям в столбцах ```r lemurs_weights %>% select(where(~ is.numeric(.) && max(., na.rm=TRUE) > 3000)) %>% head(1) ``` ``` # A tibble: 1 x 1 weight_3 <dbl> 1 2000 ``` Устаревшее: ```r lemurs_weights %>% select_if(~ is.numeric(.) && max(., na.rm=TRUE) > 3000) ``` --- ## Как еще можно указать множество столбцов? - Использовать вектор с названиями нужных столбцов и `all_of()` или `any_of()`. ```r weight_cols <- paste("weight", 1:4, sep = "_") ``` .pull-left[ ```r lemurs_weights %>% select(all_of(weight_cols)) ``` ``` Error: Can't subset columns that don't exist. x Column `weight_4` doesn't exist. ``` ] .pull-right[ ```r lemurs_weights %>% select(any_of(weight_cols)) %>% head(1) ``` ``` # A tibble: 1 x 3 weight_1 weight_2 weight_3 <dbl> <dbl> <dbl> 1 1060 1860 2000 ``` ] --- # Трансформация таблиц Задача: по 3 взвешиваниям посчитать средний вес каждого лемура. ```r lemurs_weights ``` ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix-A NA NA NA 7 Bellatrix-B NA NA NA 8 Bellatrix-C NA NA NA 9 Bellatrix 585 2760 2460 10 Blue Devil 1330 1820 2460 # ... with 41 more rows ``` --- # Подсчет по нескольким столбцам Задача: по 3 взвешиваниям посчитать средний вес каждого лемура. Получилось что-то странное... ```r lemurs_weights %>% mutate( avg_weight = mean(weight_1:weight_3)) ``` ``` # A tibble: 51 x 5 name weight_1 weight_2 weight_3 avg_weight <chr> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1530 2 Angelique 2920 2940 209 1530 3 Annabel Lee 944 1180 1689 1530 4 Ardrey-A 98 98 95 1530 5 Ardrey 3000 2780 666 1530 6 Bellatrix-A NA NA NA 1530 7 Bellatrix-B NA NA NA 1530 8 Bellatrix-C NA NA NA 1530 9 Bellatrix 585 2760 2460 1530 10 Blue Devil 1330 1820 2460 1530 # ... with 41 more rows ``` --- # Подсчет по нескольким столбцам Задача: по 3 взвешиваниям посчитать средний вес каждого лемура. Получилось верно, но как-то глупо считать среднее "вручную"... ```r lemurs_weights %>% mutate( avg_weight = (weight_1 + weight_2 + weight_3) / 3) ``` ``` # A tibble: 51 x 5 name weight_1 weight_2 weight_3 avg_weight <chr> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1640 2 Angelique 2920 2940 209 2023 3 Annabel Lee 944 1180 1689 1271 4 Ardrey-A 98 98 95 97 5 Ardrey 3000 2780 666 2149. 6 Bellatrix-A NA NA NA NA 7 Bellatrix-B NA NA NA NA 8 Bellatrix-C NA NA NA NA 9 Bellatrix 585 2760 2460 1935 10 Blue Devil 1330 1820 2460 1870 # ... with 41 more rows ``` --- # Подсчет по нескольким столбцам Задача: по 3 взвешиваниям посчитать средний вес каждого лемура. Группируем **построчно** и для каждой строки считаем среднее. Каждый лемур сам себе группа. ```r lemurs_weights %>% * rowwise() %>% mutate( avg_weight = mean(c(weight_1, weight_2, weight_3), na.rm = TRUE)) ``` ``` # A tibble: 51 x 5 # Rowwise: name weight_1 weight_2 weight_3 avg_weight <chr> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1640 2 Angelique 2920 2940 209 2023 3 Annabel Lee 944 1180 1689 1271 4 Ardrey-A 98 98 95 97 5 Ardrey 3000 2780 666 2149. 6 Bellatrix-A NA NA NA NaN 7 Bellatrix-B NA NA NA NaN 8 Bellatrix-C NA NA NA NaN 9 Bellatrix 585 2760 2460 1935 10 Blue Devil 1330 1820 2460 1870 # ... with 41 more rows ``` --- # Подсчет по нескольким столбцам Задача: по 3 взвешиваниям посчитать средний вес каждого лемура. `c_across()` позволяет отбирать столбцы по-умному (как срез, по типу данных, ...). ```r lemurs_weights %>% rowwise() %>% mutate( * avg_weight = mean(c_across(weight_1:weight_3), na.rm = TRUE)) ``` ``` # A tibble: 51 x 5 # Rowwise: name weight_1 weight_2 weight_3 avg_weight <chr> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1640 2 Angelique 2920 2940 209 2023 3 Annabel Lee 944 1180 1689 1271 4 Ardrey-A 98 98 95 97 5 Ardrey 3000 2780 666 2149. 6 Bellatrix-A NA NA NA NaN 7 Bellatrix-B NA NA NA NaN 8 Bellatrix-C NA NA NA NaN 9 Bellatrix 585 2760 2460 1935 10 Blue Devil 1330 1820 2460 1870 # ... with 41 more rows ``` --- # Подсчет по нескольким столбцам Задача: по 3 взвешиваниям посчитать средний вес каждого лемура. `c_across()` позволяет отбирать столбцы по-умному (как срез, по типу данных, ...). ```r lemurs_weights %>% rowwise() %>% mutate( * avg_weight = mean(c_across(where(is.numeric)), na.rm = TRUE)) ``` ``` # A tibble: 51 x 5 # Rowwise: name weight_1 weight_2 weight_3 avg_weight <chr> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1640 2 Angelique 2920 2940 209 2023 3 Annabel Lee 944 1180 1689 1271 4 Ardrey-A 98 98 95 97 5 Ardrey 3000 2780 666 2149. 6 Bellatrix-A NA NA NA NaN 7 Bellatrix-B NA NA NA NaN 8 Bellatrix-C NA NA NA NaN 9 Bellatrix 585 2760 2460 1935 10 Blue Devil 1330 1820 2460 1870 # ... with 41 more rows ``` --- # Подсчет по нескольким столбцам `rowwise()` создает группировку, которую умеет снимать `summarise()` или `ungroup()`. ```r lemurs_weights %>% rowwise() %>% mutate( avg_weight = mean(c_across(where(is.numeric)), na.rm = TRUE)) %>% * ungroup() ``` ``` # A tibble: 51 x 5 name weight_1 weight_2 weight_3 avg_weight <chr> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1640 2 Angelique 2920 2940 209 2023 3 Annabel Lee 944 1180 1689 1271 4 Ardrey-A 98 98 95 97 5 Ardrey 3000 2780 666 2149. 6 Bellatrix-A NA NA NA NaN 7 Bellatrix-B NA NA NA NaN 8 Bellatrix-C NA NA NA NaN 9 Bellatrix 585 2760 2460 1935 10 Blue Devil 1330 1820 2460 1870 # ... with 41 more rows ``` --- # Трансформировать сразу все столбцы Столбцы перезаписываются. ```r lemurs_weights %>% * mutate(across(everything(), toupper)) ``` ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <chr> <chr> <chr> 1 AGATHA 1060 1860 2000 2 ANGELIQUE 2920 2940 209 3 ANNABEL LEE 944 1180 1689 4 ARDREY-A 98 98 95 5 ARDREY 3000 2780 666 6 BELLATRIX-A <NA> <NA> <NA> 7 BELLATRIX-B <NA> <NA> <NA> 8 BELLATRIX-C <NA> <NA> <NA> 9 BELLATRIX 585 2760 2460 10 BLUE DEVIL 1330 1820 2460 # ... with 41 more rows ``` --- # Трансформировать несколько столбцов ```r lemurs_weights %>% * mutate(across(c("name"), toupper)) ``` ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 AGATHA 1060 1860 2000 2 ANGELIQUE 2920 2940 209 3 ANNABEL LEE 944 1180 1689 4 ARDREY-A 98 98 95 5 ARDREY 3000 2780 666 6 BELLATRIX-A NA NA NA 7 BELLATRIX-B NA NA NA 8 BELLATRIX-C NA NA NA 9 BELLATRIX 585 2760 2460 10 BLUE DEVIL 1330 1820 2460 # ... with 41 more rows ``` --- # Трансформировать несколько столбцов ```r lemurs_weights %>% * mutate(across(where(is.numeric), round)) ``` ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix-A NA NA NA 7 Bellatrix-B NA NA NA 8 Bellatrix-C NA NA NA 9 Bellatrix 585 2760 2460 10 Blue Devil 1330 1820 2460 # ... with 41 more rows ``` --- # Трансформировать несколько столбцов ```r lemurs_weights %>% * mutate(across(starts_with("weight"), ~ .x/1000)) ``` ``` # A tibble: 51 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1.06 1.86 2 2 Angelique 2.92 2.94 0.209 3 Annabel Lee 0.944 1.18 1.69 4 Ardrey-A 0.098 0.098 0.095 5 Ardrey 3 2.78 0.666 6 Bellatrix-A NA NA NA 7 Bellatrix-B NA NA NA 8 Bellatrix-C NA NA NA 9 Bellatrix 0.585 2.76 2.46 10 Blue Devil 1.33 1.82 2.46 # ... with 41 more rows ``` --- # Трансформировать несколько столбцов При использовании `list(...)` или при указании `.names = ...` создаются новые столбцы. ```r lemurs_weights %>% * mutate(across(starts_with("weight"), list(kg = ~ .x/1000))) ``` ``` # A tibble: 51 x 7 name weight_1 weight_2 weight_3 weight_1_kg weight_2_kg weight_3_kg <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1.06 1.86 2 2 Angelique 2920 2940 209 2.92 2.94 0.209 3 Annabel Lee 944 1180 1689 0.944 1.18 1.69 4 Ardrey-A 98 98 95 0.098 0.098 0.095 5 Ardrey 3000 2780 666 3 2.78 0.666 6 Bellatrix-A NA NA NA NA NA NA 7 Bellatrix-B NA NA NA NA NA NA 8 Bellatrix-C NA NA NA NA NA NA 9 Bellatrix 585 2760 2460 0.585 2.76 2.46 10 Blue Devil 1330 1820 2460 1.33 1.82 2.46 # ... with 41 more rows ``` --- # Переименовать несколько столбцов ```r lemurs_weights %>% mutate(across(starts_with("weight"), list(kg = ~ .x/1000))) %>% # как модифицировать названия столбцов, какие столбцы * rename_with(~ str_c("KG_", str_remove(., "_kg")), ends_with("kg")) ``` ``` # A tibble: 51 x 7 name weight_1 weight_2 weight_3 KG_weight_1 KG_weight_2 KG_weight_3 <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 1.06 1.86 2 2 Angelique 2920 2940 209 2.92 2.94 0.209 3 Annabel Lee 944 1180 1689 0.944 1.18 1.69 4 Ardrey-A 98 98 95 0.098 0.098 0.095 5 Ardrey 3000 2780 666 3 2.78 0.666 6 Bellatrix-A NA NA NA NA NA NA 7 Bellatrix-B NA NA NA NA NA NA 8 Bellatrix-C NA NA NA NA NA NA 9 Bellatrix 585 2760 2460 0.585 2.76 2.46 10 Blue Devil 1330 1820 2460 1.33 1.82 2.46 # ... with 41 more rows ``` --- # Трансформировать несколько столбцов `transmute` оставляет только перечисленные и новые столбцы. ```r lemurs_weights %>% transmute( name, * across(starts_with("weight"), list(kg = ~ .x/1000), .names = "KG_{.col}")) ``` ``` # A tibble: 51 x 4 name KG_weight_1 KG_weight_2 KG_weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1.06 1.86 2 2 Angelique 2.92 2.94 0.209 3 Annabel Lee 0.944 1.18 1.69 4 Ardrey-A 0.098 0.098 0.095 5 Ardrey 3 2.78 0.666 6 Bellatrix-A NA NA NA 7 Bellatrix-B NA NA NA 8 Bellatrix-C NA NA NA 9 Bellatrix 0.585 2.76 2.46 10 Blue Devil 1.33 1.82 2.46 # ... with 41 more rows ``` --- # summarise по нескольким столбцам с группировкой ```r lemurs ``` ``` # A tibble: 51 x 6 name sex weight_1 weight_2 weight_3 birth_type <chr> <chr> <dbl> <dbl> <dbl> <chr> 1 Nosferatu M 2860 2505 2930 wild 2 Poe M 2700 2610 2680 wild 3 Samantha F 2242 2360 2415 wild 4 Annabel Lee F 944 1180 1689 captive 5 Mephistopheles M 2760 2520 2620 wild 6 Endora F 2600 2360 2645 wild 7 Ozma F 2500 2440 2620 wild 8 Morticia F 2700 2550 2255 wild 9 Blue Devil M 1330 1820 2460 captive 10 Goblin M 1180 1460 1150 captive # ... with 41 more rows ``` --- # summarise по нескольким столбцам с группировкой ```r lemurs %>% * group_by(sex, birth_type) %>% * summarise(across(starts_with("weight"), mean, na.rm = TRUE)) ``` ``` # A tibble: 5 x 5 # Groups: sex [3] sex birth_type weight_1 weight_2 weight_3 <chr> <chr> <dbl> <dbl> <dbl> 1 F captive 1604. 1490. 1722. 2 F wild 2510. 2428. 2484. 3 M captive 1893. 1589. 1410. 4 M wild 2773. 2545 2743. 5 <NA> captive NaN NaN NaN ``` --- # summarise по нескольким столбцам с группировкой ```r lemurs %>% drop_na(sex) %>% group_by(sex, birth_type) %>% * summarise(across(starts_with("weight"), ~ mean(.x, na.rm = TRUE))) ``` ``` # A tibble: 4 x 5 # Groups: sex [2] sex birth_type weight_1 weight_2 weight_3 <chr> <chr> <dbl> <dbl> <dbl> 1 F captive 1604. 1490. 1722. 2 F wild 2510. 2428. 2484. 3 M captive 1893. 1589. 1410. 4 M wild 2773. 2545 2743. ``` --- # summarise по нескольким столбцам с группировкой... ```r lemurs %>% drop_na(sex) %>% * group_by(sex, birth_type) %>% summarise(across(starts_with("weight"), mean, na.rm = TRUE)) %>% * ungroup() %>% * rowwise() %>% mutate(avg_weight = mean(c_across(starts_with("weight")), na.rm = TRUE)) ``` ``` # A tibble: 4 x 6 # Rowwise: sex birth_type weight_1 weight_2 weight_3 avg_weight <chr> <chr> <dbl> <dbl> <dbl> <dbl> 1 F captive 1604. 1490. 1722. 1605. 2 F wild 2510. 2428. 2484. 2474. 3 M captive 1893. 1589. 1410. 1631. 4 M wild 2773. 2545 2743. 2687. ``` --- # Подсчет наблюдений - `tally()` - количество набюдений (строк) всего - `add_tally()` - добавляется отдельный столбец с общим количеством наблюдений .pull-left[ ```r lemurs %>% tally() ``` ``` # A tibble: 1 x 1 n <int> 1 51 ``` ] .pull-right[ ```r lemurs %>% select(1:2) %>% add_tally() ``` ``` # A tibble: 51 x 3 name sex n <chr> <chr> <int> 1 Nosferatu M 51 2 Poe M 51 3 Samantha F 51 4 Annabel Lee F 51 5 Mephistopheles M 51 6 Endora F 51 7 Ozma F 51 8 Morticia F 51 9 Blue Devil M 51 10 Goblin M 51 # ... with 41 more rows ``` ] --- # Подсчет наблюдений в группах - `count()` - количество набюдений (строк) в группе - `add_count()` - добавляется отдельный столбец с количеством наблюдений в группе .pull-left[ ```r lemurs %>% count(sex) ``` ``` # A tibble: 3 x 2 sex n <chr> <int> 1 F 26 2 M 24 3 <NA> 1 ``` ] .pull-right[ ```r lemurs %>% select(1:2) %>% add_count(sex) ``` ``` # A tibble: 51 x 3 name sex n <chr> <chr> <int> 1 Nosferatu M 24 2 Poe M 24 3 Samantha F 26 4 Annabel Lee F 26 5 Mephistopheles M 24 6 Endora F 26 7 Ozma F 26 8 Morticia F 26 9 Blue Devil M 24 10 Goblin M 24 # ... with 41 more rows ``` ] --- # Фильтрация данных - `between()` ```r lemurs %>% filter(between(weight_1, 900, 1100)) ``` - `near()` ```r lemurs %>% # от 900 до 1100 filter(near(weight_1, 1000, tol = 100)) ``` ``` # A tibble: 2 x 6 name sex weight_1 weight_2 weight_3 birth_type <chr> <chr> <dbl> <dbl> <dbl> <chr> 1 Annabel Lee F 944 1180 1689 captive 2 Agatha F 1060 1860 2000 captive ``` --- # Фильтрация данных - `near()` ```r lemurs %>% filter(near(weight_1, * mean(weight_1, na.rm = TRUE), * tol = sd(weight_1, na.rm = TRUE))) ``` ``` # A tibble: 24 x 6 name sex weight_1 weight_2 weight_3 birth_type <chr> <chr> <dbl> <dbl> <dbl> <chr> 1 Poe M 2700 2610 2680 wild 2 Samantha F 2242 2360 2415 wild 3 Annabel Lee F 944 1180 1689 captive 4 Mephistopheles M 2760 2520 2620 wild 5 Endora F 2600 2360 2645 wild 6 Ozma F 2500 2440 2620 wild 7 Morticia F 2700 2550 2255 wild 8 Blue Devil M 1330 1820 2460 captive 9 Goblin M 1180 1460 1150 captive 10 Cruella F 2050 1350 2340 captive # ... with 14 more rows ``` --- # Фильтрация по нескольким столбцам ```r lemurs %>% * filter(across(starts_with("weight"), ~ . > 2500)) ``` ``` # A tibble: 3 x 6 name sex weight_1 weight_2 weight_3 birth_type <chr> <chr> <dbl> <dbl> <dbl> <chr> 1 Nosferatu M 2860 2505 2930 wild 2 Poe M 2700 2610 2680 wild 3 Mephistopheles M 2760 2520 2620 wild ``` --- # Фильтрация по нескольким столбцам .pull-left[ `if_any` оставляет те строки, где хотя бы в одном из указанных столбцов условие выполняется. ```r lemurs_weights %>% * filter(if_any(starts_with("weight"), ~ . > 3000)) ``` ``` # A tibble: 1 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Lucrezia 2560 213 3070 ``` ] .pull-left[ `if_all` оставляет те строки, где во всех указанных столбцах условие выполняется. ```r lemurs_weights %>% * filter(if_all(starts_with("weight"), ~ . > 2500)) ``` ``` # A tibble: 3 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Mephistopheles 2760 2520 2620 2 Nosferatu 2860 2505 2930 3 Poe 2700 2610 2680 ``` ] --- # Фильтрация по нескольким столбцам Фильтрация пропущенных значений - все значения должны быть не `NA`. .pull-left[ ```r lemurs_weights %>% * drop_na(where(is.numeric)) ``` ``` # A tibble: 34 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix 585 2760 2460 7 Blue Devil 1330 1820 2460 8 Caliban 2080 296. 641 9 Claudia 740 2550 2390 10 Cruella 2050 1350 2340 # ... with 24 more rows ``` ] .pull-left[ ```r lemurs_weights %>% * filter(if_all(where(is.numeric), ~ !is.na(.x))) ``` ``` # A tibble: 34 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix 585 2760 2460 7 Blue Devil 1330 1820 2460 8 Caliban 2080 296. 641 9 Claudia 740 2550 2390 10 Cruella 2050 1350 2340 # ... with 24 more rows ``` ] --- # Фильтрация по нескольким столбцам Фильтрация пропущенных значений - хоть одно значение не `NA`. ```r lemurs_weights %>% * filter(if_any(where(is.numeric), ~ !is.na(.x))) ``` ``` # A tibble: 37 x 4 name weight_1 weight_2 weight_3 <chr> <dbl> <dbl> <dbl> 1 Agatha 1060 1860 2000 2 Angelique 2920 2940 209 3 Annabel Lee 944 1180 1689 4 Ardrey-A 98 98 95 5 Ardrey 3000 2780 666 6 Bellatrix 585 2760 2460 7 Blue Devil 1330 1820 2460 8 Caliban 2080 296. 641 9 Claudia 740 2550 2390 10 Cruella 2050 1350 2340 # ... with 27 more rows ``` --- # Что почитать про продвинутый dplyr - [dplyr cheatsheet](https://github.com/rstudio/cheatsheets/blob/master/data-transformation.pdf) - [Data Wrangling by Suzan Baert](https://suzanbaert.netlify.app/2018/01/dplyr-tutorial-1/) - [dplyr - Column-wise operations](https://dplyr.tidyverse.org/articles/colwise.html) - [dplyr - Row-wise operations](https://dplyr.tidyverse.org/articles/rowwise.html) - `?across` и прочие хелпы...