class: center, middle, inverse, title-slide .title[ # Функции ] .subtitle[ ## Визуализация и анализ географических данных на языке R ] .author[ ### Тимофей Самсонов ] .institute[ ### МГУ имени Ломоносова, Географический факультет ] .date[ ### 2024-09-24 ] --- # Функции Функции в R можно использовать для структурирования кода на логически завершенные, автономные фрагменты кода, каждый из которых выполняет конкретную задачу. Синтаксис функции выглядит следующим образом: ``` r functionName = function(parameter1, parameter2, ...){ ... return(result) ... last_value = ... } ``` - Если не указано иное, то будет возвращен результат вычисления последнего выражения, выполненного внутри функции (`last_value` в примере выше) <br><br> - Результат можно вернуть принудительно в любом месте функции, передав его в выражение `return()`. --- # Функции Пример — вычисление расстояния по координатам: ``` r x = 10 * rnorm(4) y = 10 * rnorm(4) distance = function(x1, y1, x2, y2) { sqrt((x1 - x2) ^ 2 + (y1 - y2) ^ 2) } d12 = distance(x[1], y[1], x[2], y[2]) d23 = distance(x[2], y[2], x[3], y[3]) d31 = distance(x[3], y[3], x[4], y[4]) cat(d12, d23, d31) ## 19.31289 19.85864 27.42993 ``` --- # Функции Расстояние для матрицы координат: ``` r distances = function(xy) { n = nrow(xy) * distance(xy[1:(n-1), 'x'], xy[1:(n-1), 'y'], * xy[2:n, 'x'], xy[2:n, 'y']) } (coords = cbind(x, y)) ## x y ## [1,] -4.663445 -4.580360 ## [2,] 11.478914 6.022085 ## [3,] -6.501490 14.452428 ## [4,] -6.587513 -12.977363 distances(coords) ## [1] 19.31289 19.85864 27.42993 ``` --- # Функции Длина линии: ``` r line_length = function(xy) { * sum(distances(xy)) } line_length(coords) ## [1] 66.60145 ``` Полученные функции образуют иерархию вызовов: <br><br> `line_length() -> distances() -> distance()` --- # Функции Несколько объектов можно вернуть через список: ``` r line_params = function(xy) { n = nrow(xy) l = line_length(xy) s = l / distance(xy[1, 'x'], xy[1, 'y'], xy[n, 'x'], xy[n, 'y']) * list(length = l, sinuosity = s) } result = line_params(coords) result$length ## [1] 66.60145 result$sinuosity ## x ## 7.731211 ``` --- # Функционалы __Функционал__ — это функция, которая принимает на вход другую функцию и возвращает вектор. Функционалы используются в качестве замены цикла для быстрой обработки последовательности элементов. <br><br> Функция | Назначение ------------|-------------------------------------------------------------- `apply()` | применить функцию ко всем строкам или столбцам матрицы `lapply()` | применить функцию к каждому компоненту вектора или списка и получить результат также в виде списка (_l — list_) `sapply()` | применить функцию к каждому компоненту вектора или списка и получить результат в виде вектора (_s — simplify_) `vapply()` | аналогична vapply, но требует явного задания типа данных возвращаемого вектора, за счет чего работает быстрее (_v — velocity_) `mapply()` | позволяет итерироваться по множеству структур данных одновременно --- # Функционалы Базовая функция `apply(X, MARGIN, FUN, ...)` имеет следующие аргументы: - `X` — массив любой размерности (включая вектор) - `MARGIN` — измерения по которым необходимо вести вычисления. Для матрицы `1` означает строку, `2` означает столбец, `c(1, 2)` будет означать, что вычисления производятся по всем комбинациям строк и столбцов - `FUN` — функция, которая будет применяться к каждому элементу указанных измерений - `...` — список аргументов, которые надо передать в функцию `FUN` (в этом случае массив должен передаваться обязательно в первый аргумент) --- # Функционалы ``` r library(readxl) library(dplyr) ## ## Attaching package: 'dplyr' ## The following objects are masked from 'package:stats': ## ## filter, lag ## The following objects are masked from 'package:base': ## ## intersect, setdiff, setequal, union (df = read_excel("../r-geo-course/data/sevzap.xlsx", col_types = c('text', rep('numeric', 17)))) ## # A tibble: 10 × 18 ## city pop birth death labor salary livspace doctors hosp assets business ## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 Петр… 277. 13 12.3 72.3 36268. 24.4 75 17 2.47e5 15856 ## 2 Сыкт… 259. 14.5 10.4 84.1 39790 22.8 79.5 17 2.17e5 10068 ## 3 Арха… 358. 11.8 11.6 97.9 40303. 22.7 82.3 19 2.79e5 13016 ## 4 Нарь… 24.5 18.1 7.6 12.7 69884. 23.6 65.2 2 1.05e5 704 ## 5 Воло… 313. 16.2 11.4 91 31483 24.7 60.9 18 8.27e5 21931 ## 6 Кали… 460. 13.3 13.4 126. 34142 27.7 69.8 25 3.27e5 38013 ## 7 Мурм… 302. 12.4 11.9 96.9 53240. 23.8 68.8 15 7.24e5 16041 ## 8 Вели… 222. 13.8 13.8 74.1 32377. 24.1 78.9 14 1.59e5 9583 ## 9 Псков 208. 13.2 13.7 59.6 27405. 25 59 13 1.31e5 8752 ## 10 Санк… 5226. 13.6 11.9 2055. 44187 23.6 73.8 112 4.10e6 374999 ## # ℹ 7 more variables: minerals <dbl>, manufact <dbl>, engaswat <dbl>, ## # construct <dbl>, apart <dbl>, retail <dbl>, invest <dbl> ``` --- # Функционалы Максимальное значение каждой переменной: ``` r apply(df[-1], 2, max) ## pop birth death labor salary livspace doctors hosp ## 5225.7 18.1 13.8 2055.3 69883.5 27.7 82.3 112.0 ## assets business minerals manufact engaswat construct apart retail ## 4102243.8 374999.0 NA 1978634.0 173292.0 397229.0 3031.0 1144607.0 ## invest ## 521293.0 ``` Что равносильно вызову sapply: ``` r sapply(df[-1], max) ## pop birth death labor salary livspace doctors hosp ## 5225.7 18.1 13.8 2055.3 69883.5 27.7 82.3 112.0 ## assets business minerals manufact engaswat construct apart retail ## 4102243.8 374999.0 NA 1978634.0 173292.0 397229.0 3031.0 1144607.0 ## invest ## 521293.0 ``` --- # Функционалы .pull-left[ Нормализация переменных: ``` r lapply(df[-1], function(X) { round(X/mean(X,na.rm = TRUE),2) }) |> as_tibble() ## # A tibble: 10 × 17 ## pop birth death labor salary livspace doctors hosp assets business ## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 0.36 0.93 1.04 0.26 0.89 1.01 1.05 0.67 0.35 0.31 ## 2 0.34 1.04 0.88 0.3 0.97 0.94 1.11 0.67 0.3 0.2 ## 3 0.47 0.84 0.98 0.35 0.99 0.94 1.15 0.75 0.39 0.26 ## 4 0.03 1.29 0.64 0.05 1.71 0.97 0.91 0.08 0.15 0.01 ## 5 0.41 1.16 0.97 0.33 0.77 1.02 0.85 0.71 1.16 0.43 ## 6 0.6 0.95 1.14 0.46 0.83 1.14 0.98 0.99 0.46 0.75 ## 7 0.39 0.89 1.01 0.35 1.3 0.98 0.96 0.6 1.02 0.32 ## 8 0.29 0.99 1.17 0.27 0.79 0.99 1.11 0.56 0.22 0.19 ## 9 0.27 0.94 1.16 0.22 0.67 1.03 0.83 0.52 0.18 0.17 ## 10 6.83 0.97 1.01 7.42 1.08 0.97 1.03 4.44 5.76 7.37 ## # ℹ 7 more variables: minerals <dbl>, manufact <dbl>, engaswat <dbl>, ## # construct <dbl>, apart <dbl>, retail <dbl>, invest <dbl> ``` ] .pull-right[ Начиная с R 4.1 можно анонимные функции можно создавать через синтаксис `\(...){...}` ``` r *lapply(df[-1], \(X) { round(X/mean(X,na.rm = TRUE),2) }) |> as_tibble() ## # A tibble: 10 × 17 ## pop birth death labor salary livspace doctors hosp assets business ## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 0.36 0.93 1.04 0.26 0.89 1.01 1.05 0.67 0.35 0.31 ## 2 0.34 1.04 0.88 0.3 0.97 0.94 1.11 0.67 0.3 0.2 ## 3 0.47 0.84 0.98 0.35 0.99 0.94 1.15 0.75 0.39 0.26 ## 4 0.03 1.29 0.64 0.05 1.71 0.97 0.91 0.08 0.15 0.01 ## 5 0.41 1.16 0.97 0.33 0.77 1.02 0.85 0.71 1.16 0.43 ## 6 0.6 0.95 1.14 0.46 0.83 1.14 0.98 0.99 0.46 0.75 ## 7 0.39 0.89 1.01 0.35 1.3 0.98 0.96 0.6 1.02 0.32 ## 8 0.29 0.99 1.17 0.27 0.79 0.99 1.11 0.56 0.22 0.19 ## 9 0.27 0.94 1.16 0.22 0.67 1.03 0.83 0.52 0.18 0.17 ## 10 6.83 0.97 1.01 7.42 1.08 0.97 1.03 4.44 5.76 7.37 ## # ℹ 7 more variables: minerals <dbl>, manufact <dbl>, engaswat <dbl>, ## # construct <dbl>, apart <dbl>, retail <dbl>, invest <dbl> ``` ] --- # Функционалы Если функция большая, то ее целесообразно создать заранее: ``` r normalize = function(X) { if (is.numeric(X)) round(X / mean(X, na.rm = TRUE), 2) else X } df |> lapply(normalize) |> as_tibble() ## # A tibble: 10 × 18 ## city pop birth death labor salary livspace doctors hosp assets business ## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 Петроз… 0.36 0.93 1.04 0.26 0.89 1.01 1.05 0.67 0.35 0.31 ## 2 Сыктыв… 0.34 1.04 0.88 0.3 0.97 0.94 1.11 0.67 0.3 0.2 ## 3 Арханг… 0.47 0.84 0.98 0.35 0.99 0.94 1.15 0.75 0.39 0.26 ## 4 Нарьян… 0.03 1.29 0.64 0.05 1.71 0.97 0.91 0.08 0.15 0.01 ## 5 Вологда 0.41 1.16 0.97 0.33 0.77 1.02 0.85 0.71 1.16 0.43 ## 6 Калини… 0.6 0.95 1.14 0.46 0.83 1.14 0.98 0.99 0.46 0.75 ## 7 Мурман… 0.39 0.89 1.01 0.35 1.3 0.98 0.96 0.6 1.02 0.32 ## 8 Велики… 0.29 0.99 1.17 0.27 0.79 0.99 1.11 0.56 0.22 0.19 ## 9 Псков 0.27 0.94 1.16 0.22 0.67 1.03 0.83 0.52 0.18 0.17 ## 10 Санкт-… 6.83 0.97 1.01 7.42 1.08 0.97 1.03 4.44 5.76 7.37 ## # ℹ 7 more variables: minerals <dbl>, manufact <dbl>, engaswat <dbl>, ## # construct <dbl>, apart <dbl>, retail <dbl>, invest <dbl> ``` --- # Функционалы Итерации по множеству структур данных: ``` r # генерация случайных глубин в заданных интервалах set.seed(2020) zmin = -10:-6 zmax = -5:-1 depths <- mapply( runif, min = zmin, max = zmax, MoreArgs = list(n = 7), SIMPLIFY = FALSE ) str(depths) ## List of 5 ## $ : num [1:7] -6.77 -8.03 -6.91 -7.62 -9.32 ... ## $ : num [1:7] -7.03 -8.99 -5.9 -5.18 -5.28 ... ## $ : num [1:7] -5.95 -5.3 -3.2 -4.73 -5.27 ... ## $ : num [1:7] -6.61 -2.91 -2.29 -2.58 -6.17 ... ## $ : num [1:7] -3.75 -3.22 -1.18 -5.64 -1.22 ... ``` --- # Функционалы `purrr` Пакет __purrr__ содержит более широкий спектр функционалов. Есть и аналоги базовых функционалов: - `map()` возвращает список. - `map_lgl()` возвращает вектор логических значений. - `map_int()` возвращает вектор целочисленных значений. - `map_dbl()` возвращает вектор чисел с плавающей точкой. - `map_chr()` возвращает вектор строк. ``` r library(purrr) map_dbl(df[-1], max) ## pop birth death labor salary livspace doctors hosp ## 5225.7 18.1 13.8 2055.3 69883.5 27.7 82.3 112.0 ## assets business minerals manufact engaswat construct apart retail ## 4102243.8 374999.0 NA 1978634.0 173292.0 397229.0 3031.0 1144607.0 ## invest ## 521293.0 ``` --- # Функционалы `purrr` Основные отличия от базовых функций следующие: - явное указание типа возвращаемого значения (`apply` могут быть непредсказуемы); - данные всегда идут первым аргументом (это условие не выполняется для `mapply`). - поддерживается большое разнообразие сочетаний входных и выходных параметров, в том числе итерации по нескольким векторам одновременно. --- # Функционалы `purrr` Итерации с условием: ``` r df |> map_if(is.numeric, \(X) round(X / mean(X, na.rm = TRUE), 2)) |> as_tibble() ## # A tibble: 10 × 18 ## city pop birth death labor salary livspace doctors hosp assets business ## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 Петроз… 0.36 0.93 1.04 0.26 0.89 1.01 1.05 0.67 0.35 0.31 ## 2 Сыктыв… 0.34 1.04 0.88 0.3 0.97 0.94 1.11 0.67 0.3 0.2 ## 3 Арханг… 0.47 0.84 0.98 0.35 0.99 0.94 1.15 0.75 0.39 0.26 ## 4 Нарьян… 0.03 1.29 0.64 0.05 1.71 0.97 0.91 0.08 0.15 0.01 ## 5 Вологда 0.41 1.16 0.97 0.33 0.77 1.02 0.85 0.71 1.16 0.43 ## 6 Калини… 0.6 0.95 1.14 0.46 0.83 1.14 0.98 0.99 0.46 0.75 ## 7 Мурман… 0.39 0.89 1.01 0.35 1.3 0.98 0.96 0.6 1.02 0.32 ## 8 Велики… 0.29 0.99 1.17 0.27 0.79 0.99 1.11 0.56 0.22 0.19 ## 9 Псков 0.27 0.94 1.16 0.22 0.67 1.03 0.83 0.52 0.18 0.17 ## 10 Санкт-… 6.83 0.97 1.01 7.42 1.08 0.97 1.03 4.44 5.76 7.37 ## # ℹ 7 more variables: minerals <dbl>, manufact <dbl>, engaswat <dbl>, ## # construct <dbl>, apart <dbl>, retail <dbl>, invest <dbl> ``` --- # Функционалы `purrr` Итерации по двум аргументам: ``` r depths <- map2(zmin, zmax, runif, n = 7) str(depths) ## List of 5 ## $ : num [1:7] -8.16 -9.94 -5.33 -7.38 -8.89 ... ## $ : num [1:7] -4.68 -4.58 -5.88 -6.14 -6.65 ... ## $ : num [1:7] -7.17 -5.11 -6.56 -3.49 -5.39 ... ## $ : num [1:7] -4.79 -5.88 -4.28 -4.57 -5.96 ... ## $ : num [1:7] -3.88 -1.68 -2.93 -1.04 -3.12 ... ``` --- # Функционалы `purrr` Итерации по множеству аргументов: ``` r nelem = sample(1:10, 5) depths <- pmap(list(min = zmin, max = zmax, n = nelem), runif) str(depths) ## List of 5 ## $ : num [1:5] -9.71 -6.64 -6.41 -5.91 -9.51 ## $ : num [1:6] -7.47 -7.03 -7.09 -8.14 -7.83 ... ## $ : num [1:2] -7.41 -6.88 ## $ : num -4 ## $ : num [1:3] -2.14 -4.22 -2.01 ``` --- # Функционалы `purrr` Посещение элементов с индексами и без возвращаемого значения: ``` r iwalk(depths, \(z, i) cat('Sample ', i, ': ', z, '\n', sep = '')) ## Sample 1: -9.714993-6.636848-6.413566-5.909034-9.511208 ## Sample 2: -7.466208-7.030675-7.091471-8.139645-7.830458-7.188147 ## Sample 3: -7.405076-6.881571 ## Sample 4: -4.000526 ## Sample 5: -2.143847-4.222869-2.013891 ```