class: center, middle .linea-superior[] .linea-inferior[] <img src="imagenes/logo_portada2.png" width="200" /> ## Capacitación en R y herramientas de productividad - nivel intermedio ## Proyecto Ciencia de Datos ## Funciones --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Estructura del taller **Contenidos de la clase** - `rowwise`: operaciones por fila -- - "Loops" en `dplyr` -- - Repaso de funciones en `R` **Objetivos de la clase:** - Revisar algunos contenidos de `dplyr` no tan conocidos - Sentar las bases para los contenidos que vendrán en las próximas sesiones --- class: inverse, center, middle # I. operaciones rowwise --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # rowwise ### En `dplyr` estamos acostumbrados a operar de manera vectorizada (verticalmente) ``` r mtcars %>% group_by(gear) %>% summarise(media = mean(hp)) ``` ``` ## # A tibble: 3 × 2 ## gear media ## <dbl> <dbl> ## 1 3 176. ## 2 4 89.5 ## 3 5 196. ``` -- ### Queremos una variable que sea la suma de muchas otras ``` r mtcars2 <- mtcars %>% rowwise() %>% mutate(suma = sum(cyl, disp, hp,drat)) %>% ungroup() ``` ### rowwise es una herramienta muy útil y un poco subutilizada --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # rowwise ### Podemos usar `rowwise` con algunos *helpers* ``` r casen2 <- casen %>% rowwise() %>% mutate(suma = sum(c_across(starts_with("yta")))) # funciona extremadamente lento mtcars2 <- mtcars %>% rowwise() %>% mutate(suma = sum(c_across(where(is.numeric)))) ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # rowwise como "loop" ### Tenemos un dataframe que contiene vectores ``` r df <- tibble( x = list(1, 2:3, 4:6) ) ``` -- ### ¿Cuál es el largo de cada vector? -- ``` r df %>% mutate(largo = length(x)) ``` ``` ## # A tibble: 3 × 2 ## x largo ## <list> <int> ## 1 <dbl [1]> 3 ## 2 <int [2]> 3 ## 3 <int [3]> 3 ``` .center[ <img src="https://media.giphy.com/media/a5viI92PAF89q/giphy.gif" width="200" /> ] --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # rowwise como "loop" ### `mutate` no nos es muy útil ¿Otras ideas? -- ``` r df %>% mutate(largo = map_int(x, length)) df %>% mutate(largo = sapply(x, length)) ``` -- ### Es deseable no salir del *framework* de `dplyr` ``` r df %>% rowwise() %>% mutate(largo = length(x)) ``` ``` ## # A tibble: 3 × 2 ## # Rowwise: ## x largo ## <list> <int> ## 1 <dbl [1]> 1 ## 2 <int [2]> 2 ## 3 <int [3]> 3 ``` -- ### En este caso, `rowwise` opera como un *loop* --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Otro ejemplo ### Queremos simular conjuntos de datos a partir de los siguientes parámetros ``` r df <- tribble( ~ n, ~ min, ~ max, 4, 0, 1, 2, 10, 100, 3, 100, 1000, ) ``` -- ``` r df2 <- df %>% mutate(data = runif(n, min, max)) df2 ``` ``` ## # A tibble: 3 × 4 ## n min max data ## <dbl> <dbl> <dbl> <dbl> ## 1 4 0 1 0.0752 ## 2 2 10 100 27.5 ## 3 3 100 1000 605. ``` ### No funcionó 😞 --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Otro ejemplo (continuación) ### Pero podemos usar rowwise ``` r df2 <- df %>% rowwise() %>% mutate(data = list(runif(n, min, max))) df2$data[1] ``` ``` ## [[1]] ## [1] 0.2102071 0.6588732 0.1742258 0.4066744 ``` -- ### `list()` le dice a `mutate` que el resultado de cada operación será un listado de elementos --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Desventajas de rowwise ### Para tablas con muchas filas, puede volverse un poco lento ``` r tic() casen2 <- casen %>% rowwise() %>% mutate(suma = sum(yautcorh, ytrabajocor, ymonecorh, yaimcorh, ytotcorh, yoprcor)) toc() ``` ``` ## 1.47 sec elapsed ``` -- ### Si la eficiencia es algo importante, es recomendable usar funciones como `rowSums` o `rowMeans` ``` r tic() casen2 <- casen %>% mutate(sum = rowSums(select(., yautcorh, ytrabajocor, ymonecorh, yaimcorh, ytotcorh, yoprcor))) toc() ``` ``` ## 0.068 sec elapsed ``` -- ### El problema es que debemos recurrir a una sintaxis poco elegante --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Ejercicio con rowwise Este ejercicio requiere la utilización de CASEN. Tenemos las siguientes fuentes de ingreso a nivel de persona: - *y2803c* (jubilación) - *ytro* (ingresos ocasionales) - *y0101c* (ingreso trabajo dependiente) - *y0701c* (ingreso trabajo independiente) Queremos crear una columna que contenga el mayor de estos valores (variables de más arriba) a nivel de hogar. La idea es usar `rowwise`. --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solución ejercicio con rowwise ``` r casen2 <- casen %>% rowwise() %>% mutate(max = max(y2803c, ytro, y0101c, y0701c, na.rm = T)) %>% group_by(folio) %>% mutate(maximo = max(max)) %>% ungroup() casen2 %>% select(folio, max, maximo, y2803c, ytro, y0101c, y0701c) ``` ``` ## # A tibble: 185,437 × 7 ## folio max maximo y2803c ytro y0101c y0701c ## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> ## 1 110110010101 150000 150000 0 0 0 150000 ## 2 110110010101 0 150000 0 0 0 0 ## 3 110110010201 372000 372000 0 0 372000 0 ## 4 110110010201 0 372000 0 0 0 0 ## 5 110110010201 0 372000 0 0 0 0 ## 6 110110010301 0 3000000 0 0 0 0 ## 7 110110010301 3000000 3000000 0 0 3000000 0 ## 8 110110010301 0 3000000 0 0 0 0 ## 9 110110010401 0 0 0 0 0 0 ## 10 110110010401 0 0 0 0 0 0 ## # ℹ 185,427 more rows ``` --- class: inverse, center, middle # II. "Loops" en dplyr --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # "Loops" en dplyr ### Necesito normalizar todas las columnas de mi dataset $$ norm = \frac{x - min(x)}{max(x) - min(x)} $$ -- ``` r mtcars2 <- mtcars for (var in names(mtcars)) { mtcars2[[var]] <- (mtcars[[var]] - min(mtcars[[var]])) / (max(mtcars[[var]]) - min(mtcars[[var]])) } ``` ``` ## mpg cyl disp ## Volvo 142E 0.4680851 0 0.1244699 ``` -- ### Estilo `dplyr` ``` r mtcars2 <- mtcars %>% mutate(across(.fns = ~ (. - min(.)) / (max(.) - min(.)) ) ) ``` ``` ## mpg cyl disp ## Volvo 142E 0.4680851 0 0.1244699 ``` **Nota:** Esto se solía hacer con `mutate_all` 👀 --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solo para algunas variables ### Solo me interesa `mpg` y `disp` ``` r mtcars2 <- mtcars %>% mutate(across(c(mpg, disp), ~ (. - min(.)) / (max(.) - min(.)) )) ``` -- ### Quiero crear variables nuevas, en lugar de sobrescribr ``` r mtcars2 <- mtcars %>% mutate(across(c(mpg, disp), list(norm = ~ (. - min(.)) / (max(.) - min(.)) ))) ``` ``` r names(mtcars2) ``` ``` ## [1] "mpg" "cyl" "disp" "hp" "drat" "wt" ## [7] "qsec" "vs" "am" "gear" "carb" "mpg_norm" ## [13] "disp_norm" ``` **Nota:** Esto se solía hacer con `mutate_at` 👀 --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solo para algunas variables ### Podemos adecuar los nombres a nuestro gusto ``` r mtcars2 <- mtcars %>% mutate(across(c("mpg", "disp") , .fns = list(norm = ~(. - min(.)) / (max(.) - min(.)) ), .names = "{.fn}_{.col}")) names(mtcars2) ``` ``` ## [1] "mpg" "cyl" "disp" "hp" "drat" "wt" ## [7] "qsec" "vs" "am" "gear" "carb" "norm_mpg" ## [13] "norm_disp" ``` -- ### Operar sobre todas las variables numéricas ``` r mtcars2 <- mtcars %>% mutate(across(is.numeric, list(norm = ~(. - min(.)) / (max(.) - min(.)) ))) ``` **Nota:** Esto se solía hacer con `mutate_if` 👀 --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Ejercicio mutate-across Este ejercicio requiere los datos de CASEN. Genere una variable que contenga la suma a nivel de hogar para las siguientes variables de CASEN: *ytotcor*, *yautcor*, *ytrabajocor*, *yotp*. Las nuevas variables deben tener el sufijo *_hog* --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solución ejercicio mutate-across ``` r casen2 <- casen %>% group_by(folio) %>% mutate(across(c("ytotcor", "yautcor", "ytrabajocor", "yotp"), .fns = list(hog = ~sum(.)) , .names = "{.col}_{.fn}")) %>% ungroup() casen2 %>% select(folio, ytotcor, ytotcor_hog) %>% slice(1:4) ``` ``` ## # A tibble: 4 × 3 ## folio ytotcor ytotcor_hog ## <dbl> <dbl> <dbl> ## 1 110110010101 310833 390833 ## 2 110110010101 80000 390833 ## 3 110110010201 889500 947583 ## 4 110110010201 31000 947583 ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Operaciones complejas ### ¿Qué pasa si necesito hacer una operación compleja sobre los datos? .pull-left[ - Normalizar - Sumar algo - Dividir por algo ] .pull-right[ <img src="https://media.giphy.com/media/a5viI92PAF89q/giphy.gif" width="200" /> ] -- ### Opción "carretera" ``` r mtcars2 <- mtcars %>% mutate(across(c(mpg, disp), list(norm = ~(. - min(.)) / (max(.) - min(.)) ))) %>% mutate(across(c(mpg_norm, disp_norm), ~(. + mean(.)))) %>% mutate(across(c(mpg_norm, disp_norm), ~(. / median(.)))) ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Operaciones complejas ### Podemos construir una función y luego usarla dentro de `mutate` ``` r do_silly_stuff <- function(x) { normalizar <- (x - min(x)) / (max(x) - min(x)) norm_media <- normalizar + mean(normalizar) norm_mediana <- norm_media / median(norm_media) return(norm_mediana) } mtcars2 <- mtcars %>% mutate(across(c("mpg", "disp"), .fns = list(norm = ~do_silly_stuff(.)) )) ``` .center[ ### Nota: ¡En las próximas láminas haremos un repaso de funciones! ] -- ### Alternativamente ``` r mtcars2 <- mtcars %>% mutate(across(c("mpg", "disp"), .fns = list(norm = do_silly_stuff ))) ``` --- class: inverse, center, middle # III. Repaso ~~breve~~ de funciones --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones Las funciones tienen 3 componentes: - `body` - `formals` (argumentos) - `environment` ``` r sumar_xy <- function(x, y) { x + y } ``` ``` r body(sumar_xy) ``` ``` ## { ## x + y ## } ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ``` r formals(sumar_xy) ``` ``` ## $x ## ## ## $y ``` -- ``` r environment(sumar_xy) ``` ``` ## <environment: R_GlobalEnv> ``` -- ``` r wrapper <- function() { sumar_xy <- function(x, y) { x + y } return(environment(sumar_xy)) } wrapper() ``` ``` ## <environment: 0x559ec9a30430> ``` ### Vemos que el ambiente ya no es R_GlobalEnv 🤓 --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ### *Scope* de una función ``` r z <- 3 crear_z <- function() { z <- 100 return(z) } crear_z() ``` ``` ## [1] 100 ``` ``` r print(z) ``` ``` ## [1] 3 ``` -- ### La variable z vive en el *scope* de `crear_z` y no afecta a z inicial --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ### Las funciones buscan variables "hacia arriba" ``` r sumar_xy <- function(x, y) { x + y + z } sumar_xy(1, 2) ``` ``` ## [1] 6 ``` -- ### ¿Qué devuelve la función sumar_xy? ``` r z <- 1 sumar_xy <- function(x, y) { z <- 100 x + y + z } sumar_xy(1, 2) ``` -- ``` ## [1] 103 ``` ### `z` asume el primer valor encontrado --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ### ¿Qué devuelve la función sumar_xy? ``` r z <- 1 sumar_xy <- function(x, y) { z <- 100 interna <- function(){ c(x + y + z) } interna() } sumar_xy(1, 2) ``` -- ``` ## [1] 103 ``` ### La función `interna` encontró a `x = 1`, `y = 2` y `z = 100`, --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ### ¿Qué devuelve `sumar_xy`? ``` r z <- 1 externa <- function(){ c(x + y + z) } sumar_xy <- function(x, y) { z <- 100 externa() } *sumar_xy(1, 2) ``` -- ``` ## Error in externa(): object 'x' not found ``` .pull-left[ <img src="https://media.giphy.com/media/a5viI92PAF89q/giphy.gif" width="250" /> ] -- .pull-right[ ### Las funciones buscan primero en el ambiente en el que fueron creadas ### `externa` fue creada en el mismo ambiente que `z = 1` ] --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ### Reparemos el script anterior ``` r z <- 1 externa <- function(x, y){ c(x + y + z) } sumar_xy <- function(x, y) { z <- 100 * externa(x, y) } sumar_xy(1, 2) ``` -- ``` ## [1] 4 ``` -- .center[ <img src="https://media.giphy.com/media/iIHBIgCIBlE4IoKm7w/giphy.gif" width="200" /> ] --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones .pull-left[ ### Evaluación *lazy* ] .pull-right[ <img src="https://www.inventorsdigest.com/wp-content/uploads/2018/01/lazy-sloth-1080x640.gif" width="200" /> ] ``` r sumar_xyz <- function(x, y, z) { x + y } sumar_xyz(1, 2) ``` ``` ## [1] 3 ``` -- ### La variable `z` no se evalúa hasta que realmente se necesita ``` r sumar_xyz <- function(x, y, z) { x + y + z } sumar_xyz(1, 2) ``` ``` ## Error in sumar_xyz(1, 2): argument "z" is missing, with no default ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Repaso de funciones ### En otros lenguajes, la evaluación no es *lazy* ``` python def sumar_xyz(x, y, z): return(x + y) print(sumar_xyz(1, 2)) ``` ``` ## TypeError: sumar_xyz() missing 1 required positional argument: 'z' ``` ### `python` utiliza *call by value* (CBV) --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Argumentos por defecto ### En algunos casos vale la pena incluir argumentos por defecto ``` r sumar_xyz <- function(x, y, z = 5) { x + y + z } sumar_xyz(1, 2) ``` ``` ## [1] 8 ``` -- ### Si explicitamos un valor para *z*, la función se evalúa en dicho valor ``` r sumar_xyz(1, 2, 0.4) ``` ``` ## [1] 3.4 ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Ejercicio funciones 1 Tenemos una tabla con características de los personajes de *Demon Slayer*. Crea una función llamada `get_imc` que calcule el IMC. Luego, usa tu función dentro de `mutate` para crear una columna llamada imc que contenga el IMC de cada uno de los personajes. La fórmula del IMC es: `\(imc = \frac{kg}{m^2}\)` **Recuerda que la altura en el dataset está en centímetros** --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solución ejercicio funciones 1 ``` r demon <- read_csv("data/demon_slayer.csv") %>% janitor::clean_names() get_imc <- function(weight, height) { imc <- weight /(height / 100) **2 return(imc) } demon <- demon %>% mutate(imc = get_imc(weight = weight_kg, height = height_cm)) ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Ejercicio funciones 1.2 La tabla categorias_imc permite clasificar en tramos los puntajes de imc. ``` r categorias <- read_csv("data/categorias_imc.csv") categorias ``` ``` ## # A tibble: 8 × 3 ## Categoría `IMC (kg/m²)` `Peso corporal` ## <chr> <chr> <chr> ## 1 Delgadez severa <16,0 Bajo peso ## 2 Delgadez moderada 16,0 - 17,0 Bajo peso ## 3 Delgadez leve 17,0 - 18,5 Bajo peso ## 4 Peso normal 18,5 - 24,9 Peso normal ## 5 Pre-obeso 25,0 - 29,9 Sobrepeso ## 6 Obesidad tipo I 30,0 - 34,9 Obesidad ## 7 Obesidad tipo II 35,0 - 39,9 Obesidad ## 8 Obesidad tipo III ≥ 40 Obesidad ``` Por simplicidad, usaremos solo las categorías de peso corporal (bajo peso, peso normal, sobrepeso y obesidad). Crearemos una función llamada `get_label` que reciba un valor de imc y retorne una categoría. ``` r get_label(23) ``` ``` ## [1] "peso normal" ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solución ejercicio funciones 1.2 ``` r get_label <- function(imc) { if (imc <= 18.5) { label <- "bajo peso" } else if (imc <= 24.9) { label <- "peso normal" } else if (imc <= 29.9) { label <- "sobrepeso" } else if (imc > 29.9) { label <- "obesidad" } return(label) } ``` -- ### ¿Podríamos usar esto con mutate? -- ``` r demon <- demon %>% mutate(imc = get_imc(weight = weight_kg, height = height_cm)) %>% * mutate(label = get_label(imc = imc) ) ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solución ejercicio funciones 1.2 ### ¿Alguna idea? -- ``` r demon <- demon %>% mutate(imc = get_imc(weight = weight_kg, height = height_cm)) %>% * rowwise() %>% mutate(label = get_label(imc = imc) ) %>% ungroup() ``` -- ``` r get_label <- function(imc) { * generate_label <- function(imc) { if (imc <= 18.5) { label <- "bajo peso" } else if (imc <= 24.9) { label <- "peso normal" } else if (imc <= 29.9) { label <- "sobrepeso" } else if (imc > 29.9) { label <- "obesidad" } return(label) } * return(map_chr(imc, generate_label)) } ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Ejercicio funciones 2 Crea una función que reciba un vector numérico y devuelva la desviación estándar de dicho vector `\(sd = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n} (x_i - \bar{x})^2 }\)` La idea es no usar `sd` Puedes probar tu función con el siguiente vector `rnorm(10000)` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Solución ejercicio funciones 2 ``` r get_sd <- function(x) { cuadrados <- (x - mean(x))**2 # distancias respecto a la media suma_cuadrados <- sum(cuadrados) # suma de cuadrados n <- length(x) # n sqrt(suma_cuadrados / (n - 1)) # salida } set.seed(123) vector <- rnorm(n = 10000) get_sd(vector) ``` ``` ## [1] 0.9986366 ``` ``` r sd(vector) ``` ``` ## [1] 0.9986366 ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Funciones de orden superior Son funciones que reciben como argumento una función -- ``` r vectorized_function <- function(x, func) { new_x <- do_silly_stuff(x) out <- func(new_x) return(out) } vectorized_function(c(rnorm(10)), mean) ``` ``` ## [1] 1.081107 ``` -- Se utilizan para hacer abstracciones -- Sumemos números enteros ``` r sum_integers <- function(n) { total <- 0 for (i in 1:n) { total <- total + i } return(total) } ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Funciones de orden superior Función para sumar log10 ``` r sum_log10 <- function(n) { total <- 0 for (i in 1:n) { total <- total + log10(i) } return(total) } ``` -- Función para sumar cuadrados ``` r sum_power <- function(n) { total <- 0 for (i in 1:n) { total <- total + i**2 } return(total) } ``` ### Todas las funciones tienen algo en común --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Abstracciones con funciones Función genérica para sumar cosas ``` r sum_something <- function(n, func) { total <- 0 for (i in 1:n) { total <- total + func(i) } return(total) } ``` -- Ahora creamos las funciones básicas ``` r power2 <- function(x) {x**2} identity <- function(x) {x} ``` -- Utilizamos la abstracción ``` r sum_something(10, log10) ``` ``` ## [1] 6.559763 ``` ``` r sum_something(10, power2) ``` ``` ## [1] 385 ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Fábricas de funciones Este contenido está fuera del curso, pero aconsejo revisarlo -- Las fábricas de funciones son funciones que retornan funciones -- **Motivación:** En R base no existe una función para calcular raíz de manera general Existe `sqrt` para el caso particular de raíz cuadrada -- ``` r factory_root <- function(power_input) { new_power <- function(x) { x**(1/power_input) } return(new_power) } root2 <- factory_root(2) root3 <- factory_root(3) root4 <- factory_root(4) ``` `factory_root` nos permite crear funciones para cualquier raíz --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Fábricas de funciones ¿Por qué no mejor crear simplemente una función que haga lo mismo? ``` r general_root <- function(x, power) {x**(1/power)} ``` -- Queremos una función que agregue "un print" a cualquier función -- ``` r add_print <- function(func) { string_function <- rlang::as_string(rlang::ensym(func)) * new_function <- function(x) { * result <- func(x) * print(paste0("El resultado de ", string_function, ": ", result )) * return(result) } return(new_function) } mean_print <- add_print(mean) mean_print(c(1,2,3)) ``` ``` ## [1] "El resultado de mean: 2" ``` ``` ## [1] 2 ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Comentario al margen sobre funciones infix --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Comentario al margen sobre funciones infix ### En `R` los operadores suma, resta, multiplicación, etc. son un tipo especial de función llamada *infix* ``` r 3 + 2 3 * 2 ``` -- ### Las podemos escribir en su forma *prefix* ``` r `+`(3, 2) ``` ``` ## [1] 5 ``` ``` r `*`(3, 2) ``` ``` ## [1] 6 ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Comentario al margen sobre funciones infix ### En algunos lenguajes de programación, como python, estos operadores tienen características muy útiles ``` python "perro" + " gato" ``` ``` ## 'perro gato' ``` ``` python 3 * " perro" ``` ``` ## ' perro perro perro' ``` -- ### Podemos cambiar el comportamiento de estas funciones a nuestra conveniencia --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Comentario al margen sobre funciones infix ### Vamos a definir que "+" concatene strings -- ``` r `+` <- function(x, y) { paste(x , y) } "perro" + "gato" ``` ``` ## [1] "perro gato" ``` ### Ahora "+" concatena strings 😀😀 --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Comentario al margen sobre funciones infix ### El problema es que hemos perdido la funcionalidad más importante de "+" 😞 ``` r 3 + 2 ``` ``` ## [1] "3 2" ``` -- ### ¿Y qué tal si restringimos el ámbito de acción de +? .center[ <img src="https://media.giphy.com/media/UP9ItQNj52DsM3e29m/giphy.gif" width="200" /> ] --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Comentario al margen sobre funciones infix ### En general, no es buena idea redefinir este tipo de operadores en el ambiente global ### Pero podemos crear ambientes nuevos en los que este comportamiento sea útil -- ``` r ambiente_esoterico <- env() ambiente_esoterico$`+` <- function(x, y) { paste(x , y) } eval_tidy(expr("un" + "saludo"), env = ambiente_esoterico ) ``` ``` ## [1] "un saludo" ``` -- ### Fuera de nuestro ambiente, el operador sigue funcionando normalmente ``` r "un" + "saludo" ``` ``` ## Error in "un" + "saludo": non-numeric argument to binary operator ``` -- ### Esto es una herramienta poderosa para desarrollar paquetes o aplicaciones --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Contenidos próxima clase - Estudiaremos como programar con `dplyr` mediante evaluaciones no estándar (NSE) - Revisaremos contenidos de programación funcional ### Es importante asimilar los contenidos de esta clase sobre funciones --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Tarea para la 🏠 **Ejercicio 1** Construye una función llamada `get_cv` que calcule el coeficiente de variación de un vector. `get_cv` debe recibir 2 parámetros: - un vector de tamaño `n` - un valor `boolean` que indique si los valores NA deben ser removidos. Por defecto, la función **no** debe remover dichos valores. En la construcción de la función no está permitido utilizar la función `cv`, pero sí `mean` y `sd` La fórmula para calcular el coeficiente de variación es `\(cv = \frac{sd}{\bar{X}}\)` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Tarea para la 🏠 **Ejercicio 2** Crea una función llamada `build_address` que construya una dirección en base a algunos *inputs*, siguiendo un formato establecido. `build_address` recibe 2 parámetros obligatorios y uno opcional. Parámetros obligatorios: - *street*: string que contiene el nombre de la calle - *number*: string que contiene el número de la dirección El parámetro opcional es *apartment* y contiene el número del departamento. A continuación se muestra un ejemplo del resultado esperado ``` r street <- "calle Los Alerces" number <- "número 123" build_address(street, number) ``` ``` ## [1] "los alerces 123" ``` --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Tarea para la 🏠 Si la vivienda se encuentra en un departamento, el formato esperado es **"los alerces 123, depto. 34"** Considere que no existe un formato preestablecido para registrar calle y número, lo que implica que la función debe procesar cosas como: "numero 123", "123", "num 123", "número 123", "n 123". Análogamente, la calle podría estar descrita de diferentes modos: "avenida Los Alerces", "Los Alerces", "av. Los Alerces", "calle los alerces", "pasaje Los Alerces", "los alerces", etc. **Independiente de lo que reciba la función, el formato siempre debe ser: NOMBRE CALLE, NÚMERO, DEPTO.** En la construcción de `build_address` puede usar cualquier función que sea de utilidad para lograr el objetivo. No es necesario que la función sea capaz de procesar todos los casos posibles. **Pista: En la construcción de la función pueden ser útiles algunas expresiones regulares** --- background-image: url("imagenes/fondo2.PNG") background-size: contain; background-position: 100% 0% # Tarea para la 🏠 **Ejercicio 3** Utilice la función creada sobre el siguiente `dataframe`, generando una nueva columna llamada dirección. ``` r df <- tribble(~calle, ~numero, ~depto, "calle Hemingway", "num 345", "depto. 345", "av. Albert Camus", "número 123", "123", "Manuel Rojas", "234", "departamento 231", "Nicanor Parra", "678", NULL ) df ``` ``` ## # A tibble: 4 × 3 ## calle numero depto ## <chr> <chr> <list> ## 1 calle Hemingway num 345 <chr [1]> ## 2 av. Albert Camus número 123 <chr [1]> ## 3 Manuel Rojas 234 <chr [1]> ## 4 Nicanor Parra 678 <NULL> ``` --- class: center, middle .linea-superior[] .linea-inferior[] <img src="imagenes/logo_portada2.png" width="200" /> ## Capacitación en R y herramientas de productividad - nivel intermedio ## Proyecto Ciencia de Datos ## Funciones