require(CursoR)
require(learnr)
require(tidyverse)
require(curl)
require(gradethis)
require(kableExtra)
require(palmerpenguins)
library(gapminder)
require(tibble)
require(timevis)
require(ggplot2)
require(patchwork)
require(fontawesome)
#PARA COMENTAR AL FINALIZAR
require(jpeg)
require(grid)
require(gridGraphics)
require(showtext)
font_add_google('Gochi Hand', 'gochi')
#PARA COMENTAR AL FINALIZAR
tutorial_options(exercise.timelimit = 60, exercise.checker = gradethis::grade_learnr)
knitr::opts_chunk$set(echo = F,
                      warning = F,
                      error = F,
                      message = F)

¡Bienvenido!

knitr::include_graphics("images/imagen_curso.png")  

Descripción

Este curso pretende ser una introducción básica del R. Su objetivo consiste en enseñar habilidades en la limpieza, manipulación y visualización de datos mediante el uso del R. Tenga en cuenta que este curso no pretende ser un sustituto de un curso completo de programación o de estadística.

knitr::include_graphics("images/1ra_imagen.png")  

A lo largo del curso podrá encontrar fragmentos de código para realizar ejemplos y ejercicios de codificación. Abajo encontrara como podrán ser usados:

:::example

Fragmentos de código para realizar ejemplos

Los ejemplos contienen código previamente escrito para que pueda explorarlo y ejecutarlo presionando el botón amarillo "Run Code" (ejecutar) en la esquina superior derecha. Puede actualizar el código en cualquiera de los ejemplos presionando "Start Over" (iniciar de nuevo) en la esquina superior izquierda y el código se restablecerá automáticamente al valor predeterminado. :::


:::exercise

Fragmentos de código para realizar ejercicios de codificación

Aquí se indican ejercicios prácticos de codificación donde se le pedirá que escriba el código manualmente. Si no da con la solución, puede buscar ayuda dando clic en los botones "Solution" (solución) o "Hints" (sugerencias). En la mayoría de los casos el código podrá ser evaluado con el botón "Submit Answer" (Enviar Respuesta), indicándole cuando la respuesta sea acertada. :::


#Ejercicio_código

También se encontrara con el siguiente bloque informativo:

:::caution

Bloque informativo de aclaración

En ocasiones se dara alguna recomendación sobre un tema. Este bloque se usará para llevar a cabo esto. :::


Acerca de nosotros

__Jorge Leonardo López Martínez__ es zootecnista con interés en el uso de la programación y la bioestadística aplicada al campo de la genética animal.
[leo4luffy.github.io](https://leo4luffy.github.io/)
jollopezma@unal.edu.co
[Leo4Luffy](https://github.com/Leo4Luffy)
__Duvan Ariel Nieves Ruiz__ es estudiante de ingeniería ambiental apasionado por el software libre con interés en la ecología y la programación.
[duvancho321.github.io](https://duvancho321.github.io/)
dnieves@unal.edu.co
[Duvancho321](https://github.com/Duvancho321)


No dude en comunicarse con nosotros por correo electrónico si tiene preguntas acerca del curso y su contenido.

Tenga en cuenta que este curso está bajo la licencia Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Por tanto el contenido del curso debe acreditarse a sus autores.

Creative Commons License

1. Una breve introducción al R

1.1 ¿Qué es el R?

R es un lenguaje de programación creado por Robert Gentleman y Ross Ihaka en el año de 1992. Ambos creadores le dieron el nombre de R al lenguaje implementado por las iniciales de sus nombres (a modo de broma).

knitr::include_graphics("images/Creadores.jpg")

Se trata de un lenguaje interpretado o de script, con tipado dinámico, multiplataforma y orientado a objetos:

:::example

Ejemplo 1.1.1

:::

a <- 12

b <- c(4, 6, 8, 12)

c <- matrix(data = 1:4,
            nrow = 2,
            ncol = 2)

:::caution Para crear nuevos objetos en R, se debe hacer la asignación del objeto. La asignación de objetos es la forma de almacenar información. Para hacer una asignación, se suele emplear el simbolo <-. Por ejemplo la notación a <- ..., asigna ... (lo que se quiere almacenar) al objeto a. :::

:::example

Ejemplo 1.1.2

:::

a <- 'Hola'
typeof(x = a)

b <- 1L
typeof(x = b)

c <- 12.3
typeof(x = c)

d <- 1 + 2i
typeof(x = d)

e <- c(FALSE, TRUE)
typeof(x = e)

1.2 ¿Por qué usar R?

El lenguaje de programación R se puede obtener y distribuir de forma gratuita, debido a que se encuentra bajo Licencia Pública General del proyecto GNU. Por lo tanto es un programa de código abierto y gratis.

`R` cuenta con una __comunidad__ de usuarios alrededor del mundo. Al contar con una comunidad es posible dar con la solución de algún problema de programación, así como contar con el desarrollo de paquetes utilizados en temas específicos, desarrolados por especialistas en alguna parte del mundo.
wzxhzdk:9
Encuentro en el [Rday 2019](https://rdaymedellin.github.io/).

Finalmente, el uso del R garantiza que otro investigador pueda repetir el experimento, comprobar los resultados obtenidos y estar en la condición de ampliar o refutar las interpretaciones del estudio realizado.

knitr::include_graphics("images/al_principio_pero_ahora.jpg")
Ilustracíon hecha por \@allison_horst.

¡No se desanime! Los lenguajes de programación como el R no son sencillos de aprender, pero con trabajo y motivación vera como en poco tiempo se lograrán grandes avances.

1.3 Instalación de R base y RStudio

R base es el software básico que contiene el lenguaje de programación R. RStudio es un software que facilita la programación en R.

1.3.1 R base:

La instalación de R base tanto en Windows como en Linux se realiza a través de la CRAN (Comprehensive R Archive Network).

Ejemplo de consola del R

knitr::include_graphics("images/consola_R_base.jpg")


1.3.2 RStudio:

Se puede instalar directamente desde la [página de RStudio](https://rstudio.com/products/rstudio/). Hay se encuentran los archivos de instalación tanto en Windows como en Linux. Cuando se abre RStudio se pueden ver cuatro paneles:
wzxhzdk:12
Ilustracíon hecha por \@allison_horst.
knitr::include_graphics("images/source_RStudio.jpg")

:::caution Un texto insertado en el código el cual es omitido en la ejecución, se denomina como comentario. En R, un comentario es un texto que comienza con el símbolo # y se extiende hasta el final de la línea. Este permite que R no intente interpretarlo como parte del código. :::

knitr::include_graphics("images/console.jpg")

Ejemplo de ambiente del R

knitr::include_graphics("images/environment.jpg")

Ejemplo de historial del R

knitr::include_graphics("images/history.jpg")


Ejemplo de archivos del R

knitr::include_graphics("images/files.jpg")

Ejemplo de gráficos del R

knitr::include_graphics("images/plots.jpg")

Ejemplo de ayuda del R

knitr::include_graphics("images/help.jpg")

Ejemplo de paqutes del R

knitr::include_graphics("images/packages.jpg")


1.4 Los paquetes en R

Los paquetes en `R` son colecciones de funciones y conjunto de datos desarrollados por la comunidad. Un paquete de `R` incluye código, documentación para su uso y conjuntos de datos. Existen dos formas de instalar nuevos paquetes en `R`: 1) Descargarlos de la [CRAN](https://cran.r-project.org/mirrors.html) (Comprehensive R Archive Network) por medio de la función `install.packages()`. :::example #### Ejemplo 1.4.1 :::
wzxhzdk:21
Ilustracíon hecha por \@spren9er.
install.packages('tidyr', 'dplyr', 'ggplot2')

2) Mediante el uso del paquete devtools, el cual permite instalar paquetes alojados en distintos servidores:

:::example

Ejemplo 1.4.2

:::

install.packages('devtools')
devtools::install_github('tidyverse/dplyr')

Una vez haya instalado un paquete, estará en la computadora. Si se desea usar una función o un conjunto de datos del paquete instalado, debe cargar el paquete en la sesión de R. Para esto, existen dos formas:

1) Con la notación nombre_paquete::nombre_función() para hacer un uso temporal de la función o conjunto de datos.

:::example

Ejemplo 1.4.3

:::

dplyr::starwars

2) Cargándolo en la memoria del computador mediante el uso de la función library().

:::example

Ejemplo 1.4.4

:::

library(dplyr)
Datos_starwars <- starwars

Para acceder a la descripción de un paquete desde R se puede emplear las funciones packageDescription() y help().

:::example

Ejemplo 1.4.5

:::

packageDescription('dplyr')
#help(package = 'dplyr')

Otras funciones que permiten la gestión de los paquetes instalados en el computador:

installed.packages() # Para ver que paquetes se tienen instalados.
remove.packages('dplyr') # Si se desease eliminar un paquete (en este caso el paquete dplyr).
old.packages() # Para comprobar que paquetes necesitan ser actualizados.
update.packages() # Para actualizar todo los paquetes instalados.

1.5 Trabajando con proyectos

Un directorio de trabajo es el lugar en la computadora en el que se encuentran los archivos con los que se esta trabajando. Es el lugar donde el programa R buscara los archivos para importarlos y al que serán exportados.

knitr::include_graphics("images/59va_imagen.png")  

Con la función getwd() se puede encontrar el directorio en el que se esta trabajando.

:::example

Ejemplo 1.5.1

:::

getwd() # Da como resultado la ruta "/home/leo/Escritorio/github/Un_curso_amigable_sobre_R".

Para cambiar el directorio de trabajo se puede emplear la función setwd(), dando como argumento la ruta donde se encuentra el nuevo directorio de trabajo.

:::example

Ejemplo 1.5.2

:::

setwd(dir = "/home/leo/Escritorio/Curso_estadística") # Se cambia el directorio de trabajo a la carpeta Curso_estadistica.

Sin embargo como señala Jenny Bryan, con la función setwd() es practicamente imposible para cualquier otra persona que no sea el autor original del código R, en su computadora, hacer que las rutas de archivo funcionen. La solución consistiría en trabajar con proyectos.

Un proyecto es un directorio de trabajo designado como un archivo __.Rproj__. Cuando se abre un proyecto, el directorio de trabajo se establecerá automáticamente en la carpeta en el que se encuentra el archivo __.Rproj__. El archivo __.Rproj__ se puede crear yendo a "File" (archivo) → "New Project..." (nuevo proyecto) en RStudio, que luego se asocia con la carpeta o directorio especificado. Configurar un directorio de trabajo correctamente también ayuda a desarrollar buenos hábitos que conducen a un __análisis reproducible__.
wzxhzdk:31
Ilustracíon hecha por \@allison_horst.

La tabla a continuación presenta algunas funciones útiles para administrar el directorio de trabajo:

knitr::include_graphics("images/60va_imagen.png")  

1.5.1 Estructurando el directorio de trabajo

Además de usar proyectos, también es una buena práctica estructurar el directorio de una manera que ayude a cualquier persona con la que se esta colaborando, o una versión futura de usted intente reproducir algunos análisis.

knitr::include_graphics("images/2da_imagen.png")  

2. Lo básico en el uso del R

knitr::include_graphics("images/38va_imagen.png")  

2.1 Escalares

Los escalares son el tipo de objeto más simple en R. Un objeto escalar es un objeto de un único valor, sin embargo estos se pueden clasificar en cinco clases (clases atómicas):

:::example

Ejemplo 2.1.1

:::

a <- 'Hola'
typeof(x =a)

b <- 1L
typeof(x = b)

b_2 <- 1
typeof(x = b_2)

c <- 12.3
typeof(x = c)

d <- 1 + 2i
typeof(x = d)

e <- c(FALSE, TRUE)
typeof(x = e)

:::caution En R existen las llamadas palabras clave o palabras reservadas. Estas no se deben usar como nombres ni para crear objetos, ni para crear funciones. :::

2.2 Vectores

Un vector consiste en una colección de escalares de la misma clase atómica, ya sean estos números, caracteres o lógicos, pero nunca podrán ser de dos o más clases diferentes.

Hay muchas formas de crear vectores en R. A continuación podrá encontrar una tabla con ejemplos de estas formas:

knitr::include_graphics("images/39va_imagen.png")  

:::example

Ejemplo 2.2.1

:::

num <- c(1, 2, 3, 4, 5)
num

let <- c('a', 'b', 'c', 'd', 'e', 'f')
let

num_2 <- 1:10
num_2

num_3 <- 10:1
num_3

num_4 <- seq(from = 0, to = 100, by = 10)
num_4

num_5 <- rep(x = 4, times = 10)
num_5

num_6 <- rep(x = c(1, 2), each = 2)
num_6

:::caution Una función en R es un procedimiento que generalmente toma un objeto como argumento, hace algo con ese objeto y luego devuelve un nuevo objeto. :::

knitr::include_graphics("images/40va_imagen.png")  

Lista de funciones útiles

knitr::include_graphics("images/57va_imagen.png")  

:::caution Los argumentos de una función son una serie de valores (objetos o incluso el resultado de otra función) que se pasan dentro de la función para que esta realice un proceso. :::

:::example

Ejemplo 2.2.2

:::

args(round)
#?round
#help(round)

2.2.1 Operaciones sencillas con vectores

Las operaciones aritméticas más comunes están definidas para vectores. A continuación podrá encontrar una tabla con los operadores básicos:

knitr::include_graphics("images/41va_imagen.png")  

:::example

Ejemplo 2.2.1.1

:::

vec_1 <- c(1:8)
vec_2 <- 2
sum_vec_1_vec_2 <- vec_1 + vec_2
sum_vec_1_vec_2

:::example

Ejemplo 2.2.1.2

:::

vec_1 <- seq(from = 1, to = 4, by = 1)
vec_2 <- rep(x = 2, times = 4)
div_vec_1_vec_2 <- vec_1 / vec_2
div_vec_1_vec_2

2.2.2 Resumen estadístico en vectores

Debido a que el R es un lenguaje creado para estadísticas, el mismo contiene muchas funciones básicas que permiten calcular estadísticas descriptivas a partir de un vector de datos. A continuación podrá encontrar una tabla de estas funciones:

knitr::include_graphics("images/42va_imagen.png")  

:::example

Ejemplo 2.2.2.1

:::

Pesos_rn <- rnorm(n = 12, mean = 3768, sd = 572)
Pesos_rn

min(x = Pesos_rn)

mean(x = Pesos_rn)

sd(x = Pesos_rn)

quantile(x = Pesos_rn)

summary(object = Pesos_rn)

:::caution En el R los datos faltantes se identifican como NA (not available). La mayoría de las funciones estadísticas en R en presencia de valores NA pueden generar resultados inconsistentes.
:::

:::example

Ejemplo 2.2.2.2

:::

val_NA <- c(1, 5, NA, 2, 10)
mean(val_NA)
#mean(val_NA, na.rm = TRUE)

2.2.3 Indexación de vectores

Dentro de los múltiples objetos en R, como los vectores en este caso, se debe tener claro como manejar o acceder a la información contenida en los mismos. Para estos casos se utiliza la indexación.

La indexación consiste en la selección de subconjuntos de datos por medio de operadores de selección, que en el caso de los vectores corresponde al operador [ ]. Existen dos tipos de indexación:

:::caution La posición de los valores de un vector se pueden identificar mediante su índice. En el caso del lenguaje de programación R, el tipo de indexación es en base-uno ([1]). :::

:::example

Ejemplo 2.2.3.1

:::

seq(from = 1, to = 100, by = 1)

La indexación numérica permite acceder a los valores de un vector de acuerdo a su posición especifica dentro del vector. Para esto, se emplea la forma a[index], donde a es el nombre del vector e index es es el índice del vector.

:::example

Ejemplo 2.2.3.2

:::

indice <- seq(from = 1, to = 100, by = 1)
indice[44]
indice[2:10]
indice[c(4, 4, 4)]

seleccion <- 2:8
indice[seleccion]

:::example

Ejemplo 2.2.3.3

:::

fechas <- c('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic')
fechas[c(1, 6, 11)]
fechas[-c(2:5, 7:10, 12)]

:::caution En el lenguaje de programación R existen los denominados operadores lógicos. Estos pueden ser usados para hacer comparaciones u operaciones lógicas cuyo resultado del mismo será un vector lógico (TRUE y FALSE). :::

knitr::include_graphics("images/44va_imagen.png")  

:::example

Ejemplo 2.2.3.4

:::

numeros <- seq(from = 1, to = 40, by = 1)
numeros >= 34

La segunda forma de indexar vectores es mediante el uso de vectores lógicos (vector que solo contiene valores TRUE y FALSE como el ejemplo anterior).

:::example

Ejemplo 2.2.3.5

:::

knitr::include_graphics("images/43va_imagen.png")  
ind_logica <- c(4, 8, -1, 9, -3)
ind_logica[ind_logica > 0]
ind_logica[!(ind_logica < 0)]

:::example

Ejemplo 2.2.3.6

:::

meses <- c('Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic')
meses[meses == 'Nov']
meses[meses != 'Nov']

:::example

Ejemplo 2.2.3.7

:::

numeros <- seq(from = -10, to = 10, by = 1)
numeros[numeros >= 0 & numeros != 4]

2.3 Matrices y conjuntos de datos

knitr::include_graphics("images/46va_imagen.png")  

Las matrices y los conjuntos de datos son dos tipos de objetos que representan estructuras de datos grandes. Si bien las matrices y los conjuntos de datos se ven muy similares, dada su estructura tabular, no son exactamente lo mismo: las matrices pueden contener solo una clase de datos (ya sea numérico o carácter), mientras que los conjuntos de datos pueden contener columnas tanto numéricas como de caracteres.

Hay muchas formas de crear matrices y conjuntos de datos en R. A continuación podrá encontrar una tabla de estas formas:

knitr::include_graphics("images/45va_imagen.png")  

:::example

Ejemplo 2.3.1

:::

x <- 1:5
y <- 6:10
z <- 11:15

x_y_z <- cbind(x, y, z)
x_y_z

x_y_z <- rbind(x, y, z)
x_y_z

:::example

Ejemplo 2.3.2

:::

x <- c(1, 2, 3, 4, 5)
y <- c('a', 'b', 'c', 'd', 'e')

x_y <- cbind(x, y)
x_y

:::caution Recuerden, las matrices pueden contener números o caracteres, ¡no ambos!. Si intenta crear una matriz con números y caracteres, el R convertirá todos los números en caracteres (coerción implicita). :::

knitr::include_graphics("images/58va_imagen.png")  

:::example

Ejemplo 2.3.3

:::

matrices_1 <- matrix(
  data = 1:15,
  nrow = 5,
  ncol = 3
  )
matrices_1

matrices_2 <- matrix(
  data = 1:15,
  nrow = 5,
  ncol = 3,
  byrow = TRUE
  )
matrices_2

:::example

Ejemplo 2.3.4

:::

con_dtos <- data.frame(
  'Identificación' = 1:5,
  'Sexo' = c('Mujer', 'Mujer', 'Hombre', 'Mujer', 'Hombre'),
  'Edad' = c(99, 34, 43, 50, 88)
  )
con_dtos

2.3.1 Conjuntos de datos preinstalados en R y disponibes en paquetes de R

El lenguaje de programación R cuenta con conjuntos de datos preinstalados en un paquete llamado datasets, el cual no requiere ser instalado una vez el mismo viene incluido en el software R base. Una lista completa de estos conjuntos de datos incluidos en el paquete datasets se puede ver al ejecutar el código library(help = 'datasets').

A continuación podrá encontrar una tabla del nombre de algunos de estos conjuntos de datos acompañado de una pequeña descripción:

knitr::include_graphics("images/47va_imagen.png")  

:::example

Ejemplo 2.3.1.1

:::

data('iris')

También pueden existir conjuntos de datos disponibles dentro de paquetes del R. En la tabla a continuación podrá encontrar el nombre de algunos de estos conjuntos de datos acompañado del nombre del paquete donde se encuentra alojado y una pequeña descripción:

knitr::include_graphics("images/48va_imagen.png")  

:::example

Ejemplo 2.3.1.2

:::

data(package = 'dplyr')
#library(dplyr)
#data('starwars')

2.3.2 Operaciones sencillas con matrices y conjuntos de datos

El lenguaje de programación R proporciona operadores o funciones útiles para trabajar con matrices. En la tabla a continuación podrá encontrar un ejemplo del uso de estas funciones:

knitr::include_graphics("images/49va_imagen.png")  

:::example

Ejemplo 2.3.2.1

:::

A <- matrix(data = 1:9, nrow = 3, ncol = 3, byrow = TRUE)

a <- c(4, 5, 4)
b <- c(3, 4, 4)
d <- c(8, 7, 7)
B <- rbind(a, b, d)

M_1 <- A + B
M_1

M_2 <- A %*% B
M_2

M_3 <- 4 * A
M_3

:::example

Ejemplo 2.3.2.2

:::

Juan <- c(4.4, 3.8, 4.2, 3.4)
Andres <- c(3.2, 3.6, 4.4, 4.6)
Laura <- c(4.1, 3.4, 3.2, 5.0)
Notas <- rbind(Juan, Andres, Laura)

Prom_persona <- rowMeans(x = Notas)
Prom_persona

El lenguaje de programación R contiene también funciones para ver matrices y conjuntos de datos y proporcionar información sobre ambos tipos de objetos. En la tabla a continuación podrá encontrar el nombre de estas funciones acompañada de una pequeña descripción:

knitr::include_graphics("images/50va_imagen.png")  

Para explicar tanto las funciones anteriores como las que se veran posteriormente en lo que resta de este capitulo, se empleara el conjunto de datos Starwars:

data("starwars")
Starwars <- starwars %>% 
  select(-c(12:14))
Starwars <- as.data.frame(Starwars)
save(Starwars, file="data_pkg/Starwars.rda", compress='xz')
#library(CursoR)
#data('Starwars')
knitr::include_graphics("images/51va_imagen.png")  

:::example

Ejemplo 2.3.2.3

:::

head(x = Starwars)

tail(x = Starwars)

#View(x = Starwars)

Ejemplo de lo que se observa con View()

knitr::include_graphics("images/View_star_wars.png")  

:::example

Ejemplo 2.3.2.4

:::

nrow(x = Starwars)

ncol(x = Starwars)

dim(x = Starwars)

:::example

Ejemplo 2.3.2.5

:::

summary(object = Starwars)

2.3.3 Indexación de matrices y conjuntos de datos

Al igual que con los vectores, tanto en matrices como en conjunto de datos el lenguaje de programación R permite usar el operador [ ] para acceder a valores específicos dentro de los mismos, y al igual que con los vectores, la indexación de matrices y conjuntos de datos pueden ser de dos tipos:

En matrices y conjuntos de datos, y a diferencia de lo visto con vectores, el operador [ ] requiere de dos argumentos para acceder a los valores de acuerdo a su posición: el primer argumento el cual se aplica a las filas y el segundo a las columnas.

:::example

Ejemplo 2.3.3.1

:::

A <- matrix(data = 1:9, nrow = 3, ncol = 3)
A
A[3, ]
A[, 3]

:::caution Al usar una coma (,) dentro del operador [ ] se esta dando la instrucción al R de buscar los valores solicitados en más de una dimensión. Así, el número antes de la coma será buscado en la primera dimensión del objeto, y el número después de la coma en la segunda dimensión. :::

:::example

Ejemplo 2.3.3.2

:::

a <- c(3, 2, 4, 6, 5, 1)
a[2, 4]
#a[c(2, 4)]

:::exercise

Ejercicio 2.3.3.1

Por favor, haciendo uso del conjunto de datos Starwars intente seleccionar las primeras 5 columnas de este conjunto de datos, luego seleccionar las primeras 20 filas de la primera columna y por último seleccionar de forma conjunta las columnas 2 y 3, y las filas 20 a la 40. :::

# Primeras 5 columnas ----
Starwars[]
# Primeras 20 filas de la columna 1 ----
Starwars[]
# Filas de la 20 a la 40 de las columnas 2 y 3 ----
Starwars[]
# Primeras 5 columnas ----
Starwars[, 1:5]
# Primeras 20 filas de la columna 1 ----
Starwars[1:20, 1]
# Filas de la 20 a la 40 de las columnas 2 y 3 ----
Starwars[20:40, c(2, 3)]
grade_code("¡Excelente lo lograste, sigamos adelante!")

:::example

Ejemplo 2.3.3.3

:::

names(Starwars)

# Primeras 5 columnas ----
Starwars[, c('name', 'height', 'mass', 'hair_color', 'skin_color')]
# Primeras 20 filas de la columna 1 ----
Starwars[1:20, 'name']
# Filas de la 20 a la 40 de las columnas 2 y 3 ----
Starwars[20:40, c('height', 'mass')]

:::example

Ejemplo 2.3.3.4

:::

M <- matrix(seq(from = 18, to = 2, length.out = 16), ncol = 4, byrow = TRUE)
M

t_col <- colSums(m)
t_col

porc_M <- cbind(
  m[, 1] / t_col[1],
  m[, 2] / t_col[2],
  m[, 3] / t_col[3],
  m[, 4] / t_col[4]
  )
porc_M <- round(porc_M * 100, 2)
porc_M

diag(porc_M) # Vector con los resultados de la diagonal (1ra forma)

c(porc_M[1, 1], porc_M[2, 2], porc_M[3, 3], porc_M[4, 4]) # Vector con los resultados de la diagonal (2da forma)

Indexar matrices y conjuntos de datos mediante el uso de vectores lógicos es muy similar a lo visto con vectores:

1- Se crea un vector lógico que contiene solo valores TRUE y FALSE.

2- Usando el vector lógico se indexa la matriz o conjunto de datos, devolviendo solo aquellos valores para los que dicho vector lógico es igual a TRUE.

:::example

Ejemplo 2.3.3.5

:::

Starwars[, 'species'] == 'Human'
#Starwars[Starwars[, 'species'] == 'Human', ]

El operador $ permite acceder a una columna especifica en una matriz o conjuntos de datos mediante el nombre de las columnas de la forma df$name, donde df es el nombre del objeto y name es el nombre de la columna. De esta forma, este operador establece una forma de tener acceso a los valores de las matrices y conjuntos de datos mediante los nombres de las variables o columnas.

:::example

Ejemplo 2.3.3.6

:::

#Starwars[Starwars[, 'species'] == 'Human', ]
Starwars[Starwars$species == 'Human', ]

El operador $ no solo permite acceder a los valores de la matriz y conjunto de datos, sino que también permite crear nuevas columnas o variables.

:::example

Ejemplo 2.3.3.7

:::

Starwars$numero <- 1:nrow(Starwars)
Starwars

La función subset() es una de las funciones de manejo de datos más útiles en R. Esta función permite extraer subconjuntos de datos como se haría con el operador [ ] mediante los diferentes tipos de indexación ya vistos, pero el código es mucho más fácil de escribir.

La tabla a continuación muestra los argumentos principales de esta función:

knitr::include_graphics("images/52va_imagen.png")  

:::example

Ejemplo 2.3.3.8

:::

#Starwars[Starwars[, 'species'] == 'Human', ]
#Starwars[Starwars$species == 'Human', ]
subset(
  x = Starwars,
  subset = species == 'Human'
  )

:::example

Ejemplo 2.3.3.9

:::

subconjunto <- subset(
  x = Starwars,
  subset = species == 'Human' & homeworld != 'Tatooine'
  )
subconjunto

:::example

Ejemplo 2.3.3.10

:::

prom_peso <- subset(
  x = Starwars,
  subset = species == 'Human' & homeworld == 'Tatooine'
  )
mean(x = prom_peso$mass, na.rm = TRUE)

2.3.4 Lectura y exportación de conjuntos de datos

Frecuentemente, los conjuntos de datos que seran leidos en el programa R se almacenan en dos formatos: archivos de Excel o similares con extensión .csv o archivos de texto plano con extensión .txt.

Ejemplo de almacenamiento en .csv

wzxhzdk:93
wzxhzdk:94

Ejemplo de almacenamiento en .txt

wzxhzdk:95
wzxhzdk:96


Independientemente del tipo de formato con que se guarde el conjunto de datos, se puede emplear una de las funciones incluidas en el software R base para llevar a cabo la lectura de dicho conjunto de datos: la función read.table().

La tabla a continuación muestra los argumentos principales de esta función:

knitr::include_graphics("images/54va_imagen.png")  

:::example

Ejemplo 2.3.4.1

:::

#read.table(file = 'datos_csv.csv', header = TRUE, sep = ',')
#read.table(file = 'datos_txt.txt', header = TRUE, sep = '\t')

datos_csv <- data.frame(
  'Edad' = c(34, 25, 28, 19),
  'Deporte' = c(TRUE, TRUE, FALSE, FALSE),
  'Fuma' = c(FALSE, TRUE, FALSE, TRUE),
  'Ciudad' = c('Medellin', 'Bogota', 'Cerete', 'Pasto') 
)

str(datos_csv)

Ejemplo de lectura de datos

wzxhzdk:99
wzxhzdk:100


2.3.5 Crear y convertir conjuntos de datos en tibbles

Los __tibbles__ son una reinvención de la forma en como se creaban e importaban los conjuntos de datos a partir de las funciones del sistema base del `R` anteriormente descritas. Los tibbles al heredarse de los conjuntos de datos usando el sistema base del `R`, funcionan también con las funciones disponibles para estos últimos. Adicionalmente, los tibbles proporcionan un mejor comportamiento al momento de crear conjuntos de datos. Cabe mencionar que este nueva forma de crear conjuntos de datos no hace parte de las funciones básicas del `R`, por lo que es necesario instalar y cargar previamente un paquete para trabajar con ellos:
wzxhzdk:106
install.packages('tibble')
library(tibble)

Las ventajas de los tibbles son:

:::example

Ejemplo 2.3.5.1

:::

datos_csv <- data.frame(
  'Edad' = c(34, 25, 28, 19),
  'Deporte' = c(TRUE, TRUE, FALSE, FALSE),
  'Fuma' = c(FALSE, TRUE, FALSE, TRUE),
  'Ciudad' = c('Medellin', 'Bogota', 'Cerete', 'Pasto') 
)

datos_tibble <- tribble(
  ~Edad, ~Deporte, ~Fuma,    ~Ciudad,
     34,     TRUE, FALSE, 'Medellin',
     25,     TRUE,  TRUE,   'Bogota',
     28,    FALSE, FALSE,   'Cerete',
     19,    FALSE,  TRUE,    'Pasto'
)

:::example

Ejemplo 2.3.5.2

:::

datos_csv <- data.frame(
  'Edad' = c(34, 25, 28, 19),
  'Deporte' = c(TRUE, TRUE, FALSE, FALSE),
  'Fuma' = c(FALSE, TRUE, FALSE, TRUE),
  'Ciudad' = c('Medellin', 'Bogota', 'Cerete', 'Pasto') 
)
datos_csv

datos_tibble <- tribble(
  ~Edad, ~Deporte, ~Fuma,    ~Ciudad,
     34,     TRUE, FALSE, 'Medellin',
     25,     TRUE,  TRUE,   'Bogota',
     28,    FALSE, FALSE,   'Cerete',
     19,    FALSE,  TRUE,    'Pasto'
)
datos_tibble

Ejemplo observado en la consola

wzxhzdk:110
wzxhzdk:111


:::example

Ejemplo 2.3.5.3

:::

datos_csv <- data.frame(
  'Edad' = c(34, 25, 28, 19),
  'Deporte' = c(TRUE, TRUE, FALSE, FALSE),
  'Fuma' = c(FALSE, TRUE, FALSE, TRUE),
  'Ciudad' = c('Medellin', 'Bogota', 'Cerete', 'Pasto') 
)
str(datos_csv)

datos_tibble <- tribble(
  ~Edad, ~Deporte, ~Fuma,    ~Ciudad,
     34,     TRUE, FALSE, 'Medellin',
     25,     TRUE,  TRUE,   'Bogota',
     28,    FALSE, FALSE,   'Cerete',
     19,    FALSE,  TRUE,    'Pasto'
)
str(datos_tibble)

:::caution La coerción es una característica del lenguaje de programación R que permite, implícita o explícitamente, convertir un elemento de una clase de datos en otra. Para ello se cuenta con funciones del tipo as.* :::

knitr::include_graphics("images/56va_imagen.png")  

:::example

Ejemplo 2.3.5.4

:::

datos_csv <- as_tibble(
  data.frame(
    'Edad' = c(34, 25, 28, 19),
    'Deporte' = c(TRUE, TRUE, FALSE, FALSE),
    'Fuma' = c(FALSE, TRUE, FALSE, TRUE),
    'Ciudad' = c('Medellin', 'Bogota', 'Cerete', 'Pasto') 
    )
  )
str(datos_csv)

:::example

Ejemplo 2.3.5.5

:::

amigos <- tribble(
   ~nombre,                               ~fisico,                      ~sentimental,
  'Andres', c(edad = 23, altura = 181, masa = 98),  c(soltero = TRUE, hijos = FALSE),
  'Julian', c(edad = 24, altura = 179, masa = 75),  c(soltero = FALSE, hijos = TRUE),
   'Maria', c(edad = 19, altura = 167, masa = 71), c(soltero = TRUE , hijos = FALSE),
  'sandra', c(edad = 21, altura = 174, masa = 68),  c(soltero = TRUE, hijos = FALSE),
   'Karol', c(edad = 26, altura = 171, masa = 70),  c(soltero = FALSE, hijos = TRUE),
  'Javier', c(edad = 18, altura = 164), masa = 64,  c(soltero = TRUE, hijos = FALSE)
)
amigos

2.4 Listas y arreglos

knitr::include_graphics("images/53va_imagen.png")  

Las listas son como los vectores debido a que almacenan datos en una estructura unidimensional. Sin embargo, las listas a diferencia de los vectores no almacenan valores individuales sino que almacenan objetos, como escalares, conjuntos de datos, funciones e inclusive otras listas. Para crear una lista se emplea la función list().

:::example

Ejemplo 2.4.1

:::

lista_1 <- list(
  L_1 = 40:80, 
  L_2 = 'Hola', 
  L_3 = list(VERDADERO = TRUE, FALSO = FALSE)
  )
lista_1

Si se desea extraer los elementos almacenados en una lista, se pueden usar los operadores $, [[ ]] o [ ].

:::example

Ejemplo 2.4.2

:::

lista_1 <- list(
  L_1 = 40:80, 
  L_2 = 'Hola', 
  L_3 = list(VERDADERO = TRUE, FALSO = FALSE)
  )

lista_1$L_2 # Si queremos extraer el vector con el nombre "L_2" dentro del objeto "lista_1".

lista_1[[2]] # Si queremos extraer el vector con el nombre "L_2", pero esta vez no por su nombre sino por su posición dentro del objeto "lista_1".

lista_1$L_3[2]

:::example

Ejemplo 2.4.3

:::

amigos <- tribble(
   ~nombre,                               ~fisico,                      ~sentimental,
  'Andres', c(edad = 23, altura = 181, masa = 98),  c(soltero = TRUE, hijos = FALSE),
  'Julian', c(edad = 24, altura = 179, masa = 75),  c(soltero = FALSE, hijos = TRUE),
   'Maria', c(edad = 19, altura = 167, masa = 71), c(soltero = TRUE,  hijos = FALSE),
  'sandra', c(edad = 21, altura = 174, masa = 68),  c(soltero = TRUE, hijos = FALSE),
   'Karol', c(edad = 26, altura = 171, masa = 70),  c(soltero = FALSE, hijos = TRUE),
  'Javier', c(edad = 18, altura = 164, masa = 64),  c(soltero = TRUE, hijos = FALSE)
  )

# Selección de los elementos del anterior tibble ----
amigos$fisico[[3]][1] # Edad de Maria.
amigos$fisico[[2]][1] # Edad de Julian.
amigos$sentimental[[6]][2] # Hijos de Javier. 

:::exercise

Ejercicio 2.4.1

Sabiendo que el índice de masa corporal de una persona se calcula con base en la expresión ($IMC = \frac{Masa\ (kg)}{Altura(m)^2}$), intente por favor calcular este párametro para Karol y Javier por medio de indexación. Aquí, la altura esta expresada en centímetros, por lo cual requerirá cambiarse a metros. :::

amigos <- tribble(
   ~nombre,                               ~fisico,                      ~sentimental,
  'Andres', c(edad = 23, altura = 181, masa = 98),  c(soltero = TRUE, hijos = FALSE),
  'Julian', c(edad = 24, altura = 179, masa = 75),  c(soltero = FALSE, hijos = TRUE),
   'Maria', c(edad = 19, altura = 167, masa = 71), c(soltero = TRUE,  hijos = FALSE),
  'sandra', c(edad = 21, altura = 174, masa = 68),  c(soltero = TRUE, hijos = FALSE),
   'Karol', c(edad = 26, altura = 171, masa = 70),  c(soltero = FALSE, hijos = TRUE),
  'Javier', c(edad = 18, altura = 164, masa = 64),  c(soltero = TRUE, hijos = FALSE)
  )

# Calculo del IMC del anterior tibble ----
amigos$fisico[[5]][3] / (amigos$fisico[[5]][2] / 100)^2 # IMC de Karol
amigos$fisico[[6]][3] / (amigos$fisico[[6]][2] / 100)^2 # IMC de Javier

Un arreglo es una matriz n-dimensional. Por lo tanto los arreglos, al igual que en una matriz, se pueden almacenar datos de una sola clase atómica. Para construir una arreglo se usa la función array().

:::example

Ejemplo 2.4.4

:::

arreglo_1 <- array(
  data = c(11:14, 21:24, 31:34),
  dim = c(2, 2, 3)
)
arreglo_1

Si se desea extraer los elementos almacenados en un arreglo, se usa el operador [ ] al igual que en una matriz.

:::example

Ejemplo 2.4.5

:::

arreglo_1 <- array(
  data = c(11:14, 21:24, 31:34),
  dim = c(2, 2, 3)
)

arreglo_1[2, 1, 3] # Si se quiere extraer el número almacenado en la fila 2 y la columna 1 de la tercera matriz. 
arreglo_1[, , 2] # Si se quiere extraer la segunda matriz completa.  
arreglo_1[, 2, ] # Si se quiere extraer la columna 2 de todas las matrices.
arreglo_1[1, , ] # Si se quiere extraer la fila 1 de todas las matrices.

3. Condiciones, bucles y funciones

En esta sección aprenderemos a crear y utilizar estructuras de control como funciones de usuario, bucles y condicionales que nos permitan automatizar nuestra manipulación y análisis de datos. Además esta sección nos permitirá entender cómo trabajan algunas de las funciones que conocemos, dejándonos entender y manipular mejor el R como lenguaje de programación. Cuando utilizamos programación para resolver algún problema académico, laboral u de otra índole es común encontrarnos con particularidades que corresponde únicamente a nuestro problema, por ello en muchos casos funciones generales o procesos generales no aplican para nuestra situación, es allí donde entran las famosas funciones de usuario que conoceremos a lo largo de la sección. La sección cuenta con cuatro subsecciones 1) Condicionales, 2) Bucles, 3) Funciones y 4) Familia apply.

3.1 Condicionales

Son conocidos como construcciones y se articulan con los operadores lógicos, de conjunción y de disyunción. Utilizan expresiones lógicas para denotar diferentes alternativas, es decir, funcionan como una condición a partir de la expresión lógica. La primera función que utilizaremos será if(){} acompañada de print(). Para ello recordemos con un ejemplo expresiones lógicas que ya conocemos y agreguemos la sintaxis de una condición para utilizar estas nuevas funciones.

:::example

Ejemplo 3.1.1

:::

#Expresiones logicas
35 > 20
35 != 35.4
#Condiciones
if(35>20) {print("Es mayor a 20")}
if(35 != 35.4) {print("Son diferentes")}

Como vimos con el ejemplo la condición evalúa la expresión lógica correspondiente, en el primer caso, si 35 es mayor a 20 imprime “Es mayor”, en el segundo si 35 es diferente a 35.4 imprime “Son diferentes”. Esta sintaxis aplica para cualquier condición si la expresión lógica se cumple realice determinada acción, en nuestro caso la acción es imprimir, pero podemos ejecutar cualquier acción que necesitemos.

Veamos qué acciones podemos ejecutar si nuestra expresión lógica no se cumple, para esto nos valdremos del argumento else{}. Utilicemos uno de los ejemplos anteriores para visualizarlo.

:::example

Ejemplo 3.1.2

:::

#Se cumple la expresion logica
if(35>20) {
  print("Es mayor a 20")
} else {
  print("Es menor a 20")
}
#No se cumple la expresion logica
if(10>20) {
  print("Es mayor a 20")
} else {
  print("Es menor a 20")
}

La sintaxis para este caso tendría una modificación, siendo expresada por si expresión lógica se cumple realice determinada acción, de lo contrario realice otra acción, en este caso en especifico seria, si determinado numero es mayor a 20 imprima es mayor de lo contrario imprima es menor. Podríamos además agregar una condición extra en la que verifiquemos si el número es igual a 20, para ello podemos valernos del argumento else if{}. Para evidenciar su funcionamiento te invito a que cambies el valor de x por distintos números incluyendo el 20.

#para cada modificacion de x ejecute "Run code"
x = 18
if(x>20) {
  print("Es mayor a 20")
} else if (x==20){
  print("Es igual a 20")
}else {
  print("Es menor a 20")
}

A continuación trabajaremos con acciones diferentes a print() y utilizaremos conceptos de operaciones básicas que manejamos anteriormente, usando además una función nueva denominada paste(). Para este ejemplo x es el número de rosas compradas en una floristería, en la cual se tiene una oferta, si se compran más de 10 rosas se regalan 3 rosas, si se compran exactamente 10 se regala una rosa, de lo contrario el cliente solo se lleva las rosas que compro. Para evidenciar su funcionamiento te invito a que cambies el valor de x por distintos números.

:::example

Ejemplo 3.1.3

:::

#para cada modificacion de x ejecute "Run code"
x = -13
if(x<=0) {
  paste(x,"no es un número de compra valido")
} else if (x==10){
  print("Llevarás una rosa extra")
  paste("Total de Rosas:",x+1)
} else if(x>10){
  print("Llevarás tres rosas extra")
  paste("Total de Rosas:",x+3)
}else {
  print("Llevarás las rosas que compraste")
  paste("Total de Rosas:",x)
}

Trabajemos ahora un ejercicio similar, en el cual hay una oferta del 50% en bolsas de comida de perro y un descuento del 10% adicional si se compran por lo menos 10 bolsas de comida. El precio normal de una bolsa de comida son 6800 pesos, x corresponde al número de bolsas de comida. Si se ejecuta el código con x=0 se debe imprimir en pantalla "No es un número de compra válido" , con x=8 "Total a pagar: 27200" y con x=20 "Total a pagar: 54400".

:::exercise

Ejercicio 3.1.1

:::

#para cada modificacion de x ejecute "Run code"
x <- 0
precio = 6800
#para cada modificacion de x ejecute "Run code"
x <- 0
precio = 6800
if(x<= 0){
  paste("No es un número de compra válido")
} else if (x<10){
  cobro = x*.5*precio
  paste("Total a pagar:",cobro)
} else{
  cobro = x*.4*precio
  paste("Total a pagar:",cobro)
}
grade_code("Excelente lo lograste, sigamos adelante")

Ya vimos como trabajar con condiciones numéricas y algunos operadores lógicos, veamos cómo trabajar con coincidencias exactas con caracteres. Retomemos el ejercicio anterior, añadiendo que los descuentos solo aplican en dias especificos, para diferenciar cuando se aplica o no el descuento se creo la variable des que puede ser “Hay descuento” o “No hay descuento”, veamos como funcionaria con esta nueva condición al variar los valores de x y cambiar los valores de des por las dos opciones válidas.

:::example

Ejemplo 3.1.4

:::

#para cada modificacion de x y/o des ejecute "Run code"
x <- 10
precio = 6800
des = "Hay descuento"
#Cuando no hay descuento
if(des == "No hay descuento"){
  if(x<= 0){
  paste("No es un número de compra válido")
  } else{
  cobro = x * precio
  paste("Total a pagar:",cobro)  
  }
#Cuando hay descuento
} else if(des == "Hay descuento"){
  if(x<= 0){
  paste("No es un número de compra válido")
} else if (x<10){
  cobro = x*.5*precio
  paste("Total a pagar:",cobro)
} else{
  cobro = x*.4*precio
  paste("Total a pagar:",cobro)
}
#Opcion de clasificación no valida
} else {
  paste(des,"No es un opción válida de clasificación de descuento")
}

Este ejemplo contiene una condición principal, que es si hay descuento o no; dependiendo de ella se despliega el cobro con o sin descuento con condiciones para cada caso, esta forma de incluir condiciones dentro de otras condiciones se conoce como anidar y aplica también para funciones, procesos y bucles. Para solucionar problemas con condicionales podemos apoyarnos de los diagramas de flujo como el siguiente, que simplifica el proceso cuando las entradas son correctas.

knitr::include_graphics("images/3ra_imagen.png")  

Vamos con un ejercicio similar, en el que una heladería ofrece descuentos del 25% sobre el total de la compra los días lunes, martes y miércoles, adicionalmente si la compra supera el valor de 50.000 ofrece un 10% adicional de descuento, los otros días de la semana ofrece un 15% de descuento sobre el total de la compra si esta supera el valor de 60.000. el programa debe arrojar el total, el descuento y el total con descuento en cualquiera de los casos. Si se hacen las siguientes compras el programa debe dar las salidas enunciadas:

  • Domingo total de la compra 69500:
  • "Total: 69500"
  • "Descuento: 10425"
  • "Total con descuento: 59075"

  • Lunes total de la compra 71350

  • "Total: 71350"
  • "Descuento: 24972.5"
  • "Total con descuento: 46377.5"

  • Miercoles total de la compra 33700

  • "Total: 33700"
  • "Descuento: 8425"
  • "Total con descuento: 25275"

:::exercise

Ejercicio 3.1.2

:::

#para cada modificacion del dia y del valor total de la compra ejecute "Run code"
#para cada modificacion del dia y del valor total de la compra ejecute "Run code"
#Recuerda esta es solo una posible solucion 
dia = "Miercoles"
total_compra = 33700
if(dia =="Lunes" | dia =="Martes" | dia =="Miercoles" ){
  if(total_compra<=0){
    print("No es un valor de compra válido")
  } else if(total_compra>50000){
    descuento = total_compra * .35
    nuevo_total = total_compra - descuento
    print(paste("Total:", total_compra))
    print(paste("Descuento:", descuento))
    print(paste("Total con descuento:",nuevo_total))
  } else{
    print(paste("Total:", total_compra))
    print(paste("Descuento:", total_compra * .25))
    print(paste("Total con descuento:",total_compra*.75))
  }
} else if(dia =="Jueves" | dia =="Viernes" | dia =="Sabado" | dia =="Domingo"){
  if(total_compra<=0){
    print("No es un valor de compra válido")
  } else if (total_compra>60000){
    print(paste("Total:", total_compra))
    print(paste("Descuento:", total_compra * .15))
    print(paste("Total con descuento:",total_compra*.85)) 
  } else{
    print(paste("Total:", total_compra))
    print(paste("Descuento:", total_compra * 0))
    print(paste("Total con descuento:",total_compra))     
  }
} else{
  print("No es una dia de la semana valido")
}
grade_code("Excelente lo lograste, sigamos adelante")

En los anteriores ejemplos debíamos cambiar los valores para cada caso, ahora veamos cómo utilizar el código ágilmente sobre uno o varios vectores para obtener resultados en simultaneo para cada caso. Retomemos entonces el ejemplo 3.1.2 para utilizar la función ifelse() y comparar varios números al mismo tiempo.

:::example

Ejemplo 3.1.5

:::

#vector de prueba
numeros <- c(35,10,18,70,80,5,18,34,19)
#comparacion
ifelse(test = numeros>20,yes = paste("Es mayor a 20"),no = paste("Es menor a 20"))

Veamos la adaptación para el ejemplo con la condición de igualdad con el numero 20.

#vector de prueba
numeros <- c(35,10,18,20,70,80,5,18,34,19,20)
#comparacion
ifelse(test = numeros==20,yes = paste("Es igual a 20"),no = 
         ifelse(numeros>20,paste("Es mayor a 20"),paste("Es menor a 20")))

En ejemplos simples como este es fácil adaptar la función ifelse() para usarla con vectores, sin embargo ya hemos trabajado ejemplos un poco más elaborados en los que modificamos las variables y obtenemos unas nuevas, para estos últimos esta función es difícil de adaptar y tenemos que valernos de los bucles que aprenderemos en la siguiente subsección.

Para seguir con los condicionales realiza el siguiente ejercicio usando ifelse(), en él debes encontrar los números pares de un vector de prueba, imprimiendo en pantalla la afirmación “Es par” cuando cumpla la condición, y “No es par” cuando no lo haga.

:::exercise

Ejercicio 3.1.3

:::

#vector de prueba
vec <- c(20,17,48,03,95,47,97,65,46,24,98,14,86,93,75,200,350,257,301,1,3,5,12,2)
#condicion
#vector de prueba
vec <- c(20,17,48,03,95,47,97,65,46,24,98,14,86,93,75,200,350,257,301,1,3,5,12,2)
#condicion
ifelse(vec%%2==0,paste("Es par"),paste("No es par"))
grade_code("Excelente lo lograste, sigamos adelante")

Como último ejercicio de esta subsección debes utilizar ifelse() para clasificar los números en determinado rango, de tal manera que para los números entre 20 y 60 en el vector de prueba se imprima “Están en el intervalo” y para los que no estén entre 20 y 60 se imprima “No están en el intervalo”.

:::exercise

Ejercicio 3.1.4

:::

#vector de prueba
vec <- seq(1,100,length.out = 20)
#condicion
#vector de prueba
vec <- seq(1,100,length.out = 20)
#condicion
ifelse(vec>20 & vec<60,paste("Está en el intervalo"),paste("No está en el intervalo"))
grade_code("Excelente lo lograste, sigamos adelante")

3.2 Bucles

Los bucles o ciclos son estructuras de control que permiten recorrer los elementos de un objeto iterable (vector, lista, dataframe, tibble, array,...) repitiendo determinado número de veces el bloque de código o instrucción para cada iteración. Las estructuras de control asociadas con los ciclos son:

+------------+-------------------+ | Estructura | Descripción | +============+===================+ | for() | Para cada uno en | +------------+-------------------+ | while() | Mientras | +------------+-------------------+ | break | Interrupción | +------------+-------------------+ | next | Siguiente | +------------+-------------------+ | repeat | Repetir | +------------+-------------------+

Para empezar trabajaremos con el ciclo for ilustrando cómo funcionan las iteraciones en cada elemento del vector letras el cual posee una longitud de 15.

Para empezar trabajaremos con el ciclo for viendo la siguiente ilustracion y observando el funcionamiento de las iteraciones en cada elemento en el vector letras del ejemplo 3.2.1. Recordemos que letras posee 15 elementos.

knitr::include_graphics("images/for_loops.jpeg")
Ilustracíon hecha por \@allison_horst.

:::example

Ejemplo 3.2.1

:::

#vector
letras
#ciclo for
for (i in 1:length(letras)){
  print(paste("la letra en la posición",i,"es",letras[i]))
}

Una modificación con la cual podemos obtener el mismo resultado del fragmento de código anterior es utilizando la función seq_along() de la siguiente forma.

#vector
letras
#ciclo for
for (i in seq_along(letras)){
  print(paste("la letra en la posición",i,"es",letras[i]))
}

Si la posición no es relevante para nuestras operaciones podemos utilizar el ciclo for de la siguiente forma, olvidándonos de la longitud y del índice de cada iteración.

#vector
letras
#ciclo for
for (i in letras){
  print(i)
}

Veamos una modificación del ejemplo anterior en el cual nos interesa obtener los elementos con índices pares del vector letras.

:::example

Ejemplo 3.2.2

:::

#vector
letras
#ciclo for
for (i in seq(2,length(letras),2)){
  print(paste("la letra en la posición",i,"es",letras[i]))
}

También podemos utilizar condicionales y seq_along() de una forma estratégica para lograr el mismo objetivo.

#vector
letras
#ciclo for con condicional
for (i in seq_along(letras)){
  if(i%%2 == 0){
    print(paste("la letra en la posición",i,"es",letras[i]))
  }
}

Hagamos un ejemplo un poco más elaborado usando el ejemplo 3.1.4 del descuento de comida de perro, utilizando en esta ocasión dos vectores, x vector con distintos números de bolsas de comida (cada número una compra diferente) y des vector que se señala cuando se aplica descuento y cuando no (1= Hay descuento, 0= No hay descuento). Cabe recordar que las ocasiones en la que se aplica un descuento, este es de un 50% con un 10% adicional si el número de bolsas de comida es mayor a 10. El precio por bolsa es de 6800 pesos.

:::example

Ejemplo 3.2.3

:::

#Información
x = c(10,0,2,15,13,18,4,9,7,2,8,16) #numero de bolsas
des = c(1,0,0,1,0,0,1,1,1,0,0,10) #clasificación
#ciclo for
for (i in seq_along(x)){
  cobro = 6800 * x[i]
  #cuando no hay descuento
  if(des[i]==0){
    if(x[i]<=0){
      print("No es un número de compra válido")
    } else{
      print(paste("Total a pagar:",cobro))
    }
  #cuando hay descuento
  } else if(des[i]==1){
    if(x[i]<=0){
      print("No es un número de compra válido")
    } else if(x[i]<10){
      print(paste("Total a pagar:",cobro*.5))
    } else{
      print(paste("Total a pagar:",cobro*.4))
    }
  } else{
    print(paste(des[i],"No es un opción válida de clasificación de descuento"))
  }
}

Ya vimos cómo funciona el ciclo for con vectores, veamos un ejemplo con my_lista, y obtengamos el resumen con summary() para cada objeto de la lista.

:::example

Ejemplo 3.2.4

:::

for (i in seq_along(my_lista)){
  objeto <- my_lista[[i]]
  print(summary(objeto))
}

Se puede ver que el proceso con las listas es tan simple como con los vectores, por ello en el siguiente ejercicio debes hacer uso de la lista carlos_fam_g presentada a continuación y obtener todos los nombres de los familiares de Carlos, e imprimirlos en pantalla junto con el tipo de relación que tienen y su edad. Por ejemplo para el padre se debe mostrar en pantalla : “El padre de Carlos es Juan carlos y tiene 55 años”.

+-------------+----------------+------+ | Relación | Nombre | Edad | +=============+================+======+ | Padre | Juan Carlos | 55 | +-------------+----------------+------+ | Madre | Luz Estela | 53 | +-------------+----------------+------+ | Hijo | Juan Camilo | 3 | +-------------+----------------+------+ | Hijo | Juan Fernado | 5 | +-------------+----------------+------+ | Hijo | Juliana | 1 | +-------------+----------------+------+ | Hermano | Andres Felipe | 25 | +-------------+----------------+------+ | Hermano | Maria Camila | 21 | +-------------+----------------+------+

:::exercise

Ejercicio 3.2.1

:::

# carlos_fam_g
# carlos_fam_g
for (objeto in carlos_fam_g){
  for (i in seq_along(objeto[[1]])){
    print(paste("Relacion con carlos:",objeto[[1]][i],"su nombre es",objeto[[2]][i], "y tiene",objeto[[3]][i],"años"))
  }
}
grade_code("Excelente lo lograste, sigamos adelante")

Como se puede notar el ejercicio se ve más sencillo de lo que es, veamos otro ejercicio para reforzar el aprendizaje del ciclo for. Para este ejercicio usaremos los datos amigos y retomaremos el ejercicio 2.4.1 calculando el IMC ($IMC = \frac{Masa\ (kg)}{Altura(m)^2}$) para todas las personas del conjunto de datos imprimiendo en pantalla una expresión del tipo: “Andres tiene un altura de 1.81 m, una masa de 98 kg y su IMC es 29.91”

:::exercise

Ejercicio 3.2.2

:::

# amigos
# amigos
for (i in seq_along(amigos$nombre)){
  nombre <- amigos$nombre[i]
  masa <- amigos$fisico[[i]][3][[1]]
  altura <- amigos$fisico[[i]][2][[1]]/100
  IMC <- masa/altura^2
  print(paste(nombre,"tiene una altura de",altura,"m, una masa de",masa,"u su IMC es",round(IMC,2)))
}
grade_code("Excelente lo lograste, sigamos adelante")

Modifica el código del ejercicio anterior y úsalo para incluir la variable IMC en la lista de fisico del dataset amigos. Recuerda la indexación y creación de variables tanto en los dataframe como en las listas.

:::exercise

Ejercicio 3.2.3

:::

# amigos
# amigos
for (i in seq_along(amigos$nombre)){
  masa <- amigos$fisico[[i]][3][[1]]
  altura <- amigos$fisico[[i]][2][[1]]/100
  IMC <- masa/altura^2
  amigos$fisico[[i]][4] <- round(IMC,2)
  names(amigos$fisico[[i]]) <- c("edad","altura","masa","IMC")
}
amigos$fisico
grade_code("Excelente lo lograste, sigamos adelante")

Ya sabemos un poco mas de como acceder y crear variables con el ciclo for, ahora tendrás que usarlo para crear una variable adicional en el dataset amigos. La nueva variable se obtuvo de preguntarle a los personajes cuantos meses llevaban solteros, sin embargo se descubrió que los personajes que no están solteros mintieron al respecto. Las respuestas están alojadas en el vector tsoltero y tu tarea es crear la variable adicional tiempo_soltero en el dataset amigos, asignando a los mentirosos el valor de NA.

:::exercise

Ejercicio 3.2.4

:::

# amigos
tsoltero <- c(4,1,9,5,2,6) #tiempo soltero en meses
amigos$tiempo_soltero <- 0
#tiempo_soltero
# amigos
tsoltero <- c(4,1,9,5,2,6) #tiempo soltero en meses
amigos$tiempo_soltero <- 0
#tiempo_soltero
for (i in seq_along(amigos$nombre)){
  soltero <- amigos$sentimental[[i]][1][[1]]
  if (soltero == T){
    amigos$tiempo_soltero[i]<- tsoltero[i]
  } else{
    amigos$tiempo_soltero[i] <- NA
  }
}
amigos
grade_code("Excelente lo lograste, sigamos adelante")

En R también está presente el ciclo while() el cual permite repetir la ejecución de un grupo de instrucciones mientras se cumpla una condición, debemos ser precavidos ya que es muy fácil programar un bucle infinito en el cual la condición siempre se cumpla. Iniciemos adaptando el ejemplo 3.2.1 para las primeras 10 letras del vector letras, usando además un contador. Veamos cómo funciona.

:::example

Ejemplo 3.2.5

:::

i =1 #contador
while(i<=10){
  print(letras[i])
  i =i+1
}

El siguiente ejemplo consiste en la comparación de números de un vector x, que solo funciona mientras los números sean cada vez más grandes, al terminar el ciclo se muestra la razón y el índice por el cual se finalizó.

:::example

Ejemplo 3.2.6

:::

x = c(5,8,12,19,48,95,106,208,406,512,683,791,790,805,900,931)
i = 1
while(x[i]<x[i+1]){
  print(paste(x[i],"es menor que",x[i+1]))
  i=i+1
}
print(paste(x[i],"no es menor que",x[i+1],",error en el indice",i+1,"del vector x"))

Veamos con un ejemplo cómo usar el ciclo while para generar los primeros 20 números de la sucesión de Fibonacci.

:::example

Ejemplo 3.2.7

:::

a = 0
b = 1
count = 1
while(count<=20){
  print(a)
  c<-a+b
  a<-b
  b<-c
  count = count +1
}

Como se puede ver el ciclo while puede adaptarse para funcionar igual que el ciclo for, sin embargo su uso en el lenguaje R es poco frecuente y suele ser más común utilizar el ciclo for acompañado de condicionales. Veamos los anteriores ejemplos adaptados al ciclo for utilizando un nuevo argumento denominado break. El ejemplo 3.2.5 utilizando el ciclo for se ve de la siguiente manera:

:::example

Ejemplo 3.2.8

:::

for (i in seq_along(letras)){
  print(letras[i])
  if(i==10){
    break
  }
}

La adaptación del ejemplo 3.2.6 sería la siguiente:

x = c(5,8,12,19,48,95,106,208,406,512,683,791,790,805,900,931)
for (i in seq_along(x)){
  if (x[i]<x[i+1]){
    print(paste(x[i],"es menor que",x[i+1]))
  }else{
    print(paste(x[i],"no es menor que",x[i+1],",error en el indice",i+1,"del vector x"))
    break
  }
}

Ahora debes adaptar el ejemplo 3.2.7 de la sucesión de Fibonacci usando el ciclo for.

:::exercise

Ejercicio 3.2.5

:::


a=0
b=1
for (i in 1:20){
  print(a)
  c<-a+b
  a<-b
  b<-c
}
grade_code("Excelente lo lograste, sigamos adelante")

Otro de los ciclos que posee R es repeat{}, que es incluso de menor uso que el ciclo while. En general repeat{} repite un grupo de instrucciones indefinidamente, por ello el uso del argumento break resulta obligatorio en este ciclo. Veamos algunos de los ejemplos anteriores adaptados al ciclo repeat{}

:::example

Ejemplo 3.2.9

:::

Iniciemos adaptando el ejemplo 3.2.2

# letras
i=0
repeat{
  i=i+2
  if(i<=length(letras)){
    print(paste("la letra en la posición",i,"es",letras[i]))
  } else{ break }
}

Veamos la adaptación del ejercicio 3.2.2 con el ciclo repeat{}

# amigos
j=0
repeat{
  j=j+1
  if(is.na(amigos$nombre[j])){break}
  nombre <- amigos$nombre[j]
  masa <- amigos$fisico[[j]][3][[1]]
  altura <- amigos$fisico[[j]][2][[1]]/100
  IMC <- masa/altura^2
  print(paste(nombre,"tiene una altura de",altura,"m, una masa de",masa,"u su IMC es",round(IMC,2)))
}

Para finalizar esta subsección entendamos cómo trabaja el argumento next con un ejemplo simple, en el cual evitaremos imprimir los números de 8 y 12 en la serie de números de 1 a 15.

:::example

Ejemplo 3.2.10

:::

for (i in 1:15){
  if(i == 8 | i == 12){
    next
  }
  print(i)
}

Apliquemos también el argumento next al ejemplo 3.2.3 del descuento de comida para perro, en este caso el argumento nos ayuda a hacer un poco más corto el código desarrollado anteriormente, evitando seguir el proceso cuando encontramos opciones de descuento o de compra inválidos.

:::example

Ejemplo 3.2.11

:::

x = c(10,0,2,15,13,18,4,9,7,2,8,16) #numero de bolsas
des = c(1,0,0,1,0,0,1,1,1,0,0,10) #clasificación
for (i in seq_along(x)){
  cobro = 6800*x[i]
  if(x[i]<=0){
    print("No es un número de compra válido")
    next
  }
  if(des[i]!=0 & des[i]!=1){
    print(paste(des[i],"No es un opción válida de clasificación de descuento"))
    next
  }
  if(des[i]==1 & x[i]<10){
    cobro = cobro *.5
  } else if(des[i]==1 & x[i]>=10){
    cobro = cobro * .4
  }
  print(paste("Total a pagar:",cobro))
}

Modifiquemos la respuesta del ejercicio 3.2.1 utilizando el ciclo for en compañía del argumento next para múltiples compras en la heladería, de modo que obtengamos el número de compra, el total correspondiente, el descuento y el total con descuento para cada caso.

:::example

Ejemplo 3.2.12

:::

dia = c("Domingo", "Lunes","Miercoles","jueves","Domingo","Martes","Viernes")
total_compra = c(69500,71350,33700,54000,0,38000,52300)
for (i in seq_along(dia)){
  print(paste("Número de compra", i))  
  descuento = 0
  if(total_compra[i]<=0){
    print("No es un valor de compra válido")
    next
  }
  if(dia[i] =="Lunes" | dia[i] =="Martes" | dia[i] =="Miercoles"){
    if(total_compra[i]>50000){
      descuento = total_compra[i] * .35
    }else{
      descuento = total_compra[i] * .25
    }
  }else if(dia[i] =="Jueves" | dia[i] =="Viernes" | dia[i] =="Sabado" | dia[i] =="Domingo"){
    if(total_compra[i]>60000){
      descuento = total_compra[i] * .15
    }
  } else{
    print("No es una dia de la semana valido")
    next
  }
  print(paste("Total:", total_compra[i]))
  print(paste("Descuento:", descuento))
  print(paste("Total con descuento:",total_compra[i]-descuento)) 
}

Como se ha dicho anteriormente algunos de los ciclos son de poco uso en el lenguaje R, por lo que es más común utilizar funciones vectoriales como ifelse() y funciones derivadas de la familia apply. Primero apliquemos ifelse() a algunos de los ejemplos y ejercicios de esta subsección modificandolos como dataframe o tibble. La familia apply se verá en una subsección posterior.

Retomando el ejemplo 3.1.3 de la promoción en la floristería, observemos su adaptación en un tibble usando ifelse() para multiples compras

:::example

Ejemplo 3.2.13

:::

#promocion de rosas
tibble(rosas= c(2,5,10,21,17,10,2,0,8,3),
       rosas_extra = ifelse(rosas<=0,NA, #numero de rosas validas
                            ifelse(rosas<10,0, # rosas menores a 10
                                   ifelse(rosas==10,1,3))), # Adicion de rosas en 10 unidades y mayores 
       rosas_total = rosas +rosas_extra
       )

La siguiente adaptación corresponde a la promoción de comida de perro.

#descuento comida de perro
tibble(bolsas= c(10,0,2,15,13,18,4,9,7,2,8,16),
       des = c(1,0,0,1,0,0,1,1,1,0,0,10),
       descuento = ifelse(bolsas<=0,NA, #numero de bolsas validas
                          ifelse(des==0,0, #ocasiones sin descuento
                                 ifelse(des!=1,NA, #descuento valido
                                        ifelse(bolsas<10,.5,.6)))), # descuento del 50% general y 60% por al menos diaz bolsas
       cobro = ifelse(bolsas<=0,NA,
                      ifelse(des==0,bolsas*6800,
                             ifelse(des!=1,NA,(1-descuento)*bolsas*6800)))
       )

Y como último ejemplo de esta subsección veremos la adaptación del ejercicio 3.1.2 como un tible con ifelse().

#descuentos heladeria
dias_semana = c("Lunes","Martes","Miercoles","Jueves","Viernes","Sabado","Domingo")
tibble(dia = c("Domingo", "Lunes","Miercoles","jueves","Domingo","Martes","Viernes"),
       total_compra = c(69500,71350,33700,54000,0,38000,53200),
       descuento_porcentaje = ifelse(total_compra<= 0,NA, #Valor de compra valido
                                     ifelse(dia %in% dias_semana, #dias validos
                                            ifelse(dia %in% dias_semana[1:3], # Descuento de lunes a miercoles
                                                   ifelse(total_compra>50000,.35,.25), # Descuento de lunes a miercoles por compras superiores a 50mil
                                                   ifelse(total_compra>60000,.15,0)),NA)), # Descuento por compras mayores a 60mil en el resto de dias
       descuento_valor = total_compra * descuento_porcentaje,
       total_a_pagar = total_compra - descuento_valor
       )

3.3 Funciones

R en su instalación base cuenta por lo menos con 1302 funciones para realizar diversas tareas, sin embargo es común crear nuevas funciones que se ajusten a nuestras necesidades. Pero antes de crear una función debemos entenderla y reconocer sus 3 componentes: 1) argumentos, 2) cuerpo y 3) entorno. Iniciemos creando con function() una función simple para ilustrar los tres componentes y sus atributos.

knitr::include_graphics("images/4ta_imagen.png")  

:::example

Ejemplo 3.3.1

:::

# Funcion
fun_1 <- function(x,y){
  # comentario
  x+y
}
# Argumentos
formals(fun_1)
# Cuerpo
body(fun_1)
# Entorno
environment(fun_1)
# Atributos
attr(fun_1,"srcref")

En general cualquier función creada en lenguaje R posee componentes y atributos, sin embargo algunas funciones base como sum() mean() y otras no poseen estos componentes, debido a que llaman código C directamente. Intenta hallar los componentes y atributos para la funciones prod() y abs().

:::exercise

Ejercicio 3.3.1

:::


# Argumentos
formals(prod)
formals(abs)
# Cuerpo
body(prod)
body(abs)
# Entorno
environment(prod)
environment(abs)
# Atributos
attr(prod,"srcref")
attr(abs,"srcref")
grade_code("Excelente lo lograste, sigamos adelante")

Como se puede ver estas funciones no poseen componentes y atributos propios de una función creada en R sin embargo no todas las funciones base llaman código C. Veamos los componentes y atributos de la función rowMeans()

# Argumentos
formals(rowMeans)
# Cuerpo
body(rowMeans)
# Entorno
environment(rowMeans)
# Atributos
attr(rowMeans,"srcref")

De igual forma, se puede hacer uso de la función function() en conjunto con otras funciones u operadores, en lo que se denomina como funciones anónimas. Estas se caracterizan por ser breves (se pueden escribir en una sola línea de código) y por no poseer frecuentemente los {}. Veamos un ejemplo de una función anónima para la integral $\pi \int_{0}^{6} \frac{x}{12} \sqrt{36-x^2}$, donde la función anónima corresponde al integrando.

:::example

Ejemplo 3.3.2

:::

integrate(function(x) pi * (x/12) * sqrt(36- x^2),0,6 )

El ejemplo muestra que el integrando es lo suficientemente corto como para escribirlo en una línea de código, sin embargo observemos una escritura un poco más extensa para el mismo ejemplo.

integrando <- function(x){
  pi * (x/12) * sqrt(36- x^2)
}
integrate(integrando,0,6 )

Una cuestión importante en las funciones, es el léxico que se maneja, en él que debemos entender que los argumentos son válidos dentro de la función y que crearlos o nombrarlos en el exterior no siempre los afecta. Veamos las salidas del siguiente ejemplo para entender mejor este concepto.

:::example

Ejemplo 3.3.3

:::

# Función
fun_2 <- function() {
  x = 5
  y=10
  x+y
}
#variables independientes
x = 7
y = 3
# Usando fun_2
fun_2()

Retomemos fun_1 y selecciona la salida correcta para el siguiente fragmento de código.

:::exercise

Ejercicio 3.3.2

:::

#funcion
fun_1 <- function(x,y){
  # suma de x y y
  x+y
}
#variables independientes
x= 30
y = x
x = 5
#usando fun_1
fun_1(6,7)
question(" ",
  answer("[1] 30", message = "No estas en lo correcto, sigue intentandolo."),
  answer("[1] 60", message = "No estas en lo correcto, sigue intentandolo."),
  answer("[1] 10", message = "No estas en lo correcto, sigue intentandolo."),
  answer("[1] 13",correct = TRUE, message = "Estas en lo correcto.."),
  answer("[1] 35", message = "No estas en lo correcto, sigue intentandolo."),
  allow_retry = TRUE,
  random_answer_order = TRUE
)

¿Pero qué sucedería con el léxico, si nuestra función no posee argumentos pero requiere de una variable? ¿o si los argumentos de nuestra función no son declarados y se requiere una variable?. Veamos primero una función sin argumentos y cómo funciona la denominada búsqueda dinámica.

:::example

Ejemplo 3.3.4

:::

# Función
fun_3 <- function() x+y
# Usando fun_2
fun_3()

Como nuestra función requiere internamente de un x y un y, obtenemos un error que nos notifica que el objeto x no se encuentra. Es aquí donde podemos aplicar el concepto de búsqueda dinámica, agregando x y y como variables independientes.

# Variables independientes
x = 5
y = 10
# Función
fun_3 <- function() x+y
# Usando fun_2
fun_3()

Ensayemos con el caso en el que nuestra función tiene un argumento pero falta una variable interna.

:::example

Ejemplo 3.3.5

:::

# Función
fun_4 <- function(x) x+y
# Usando fun_2
fun_4(10)

Apliquemos el concepto de búsqueda dinámica añadiendo como variable independiente y e incluyendo el argumento x requerido al usar la función.

#variable independiente 
y = 15
# Función
fun_4 <- function(x) x+y
# Usando fun_2
fun_4(10)

Ya que entendimos ciertos conceptos básicos de las funciones creemos algunas que cumplan las mismas tareas que las funciones base, como por ejemplo mean().

:::example

Ejemplo 3.3.6

:::

my.mean <- function(x){
  #calculo
  sum(x) / length(x)
}
#prueba
my.mean(1:956)
#comparación
mean(1:956)

Personalicemos un poco my.mean con la función message() para que envíe un mensaje mientras hace las operaciones necesarias y además incluyamos return() para devolver los resultados de las operaciones. Para ver la salida de los mensajes debes copiar la función en R y ejecutarla allí, la versión dinámica del curso no permite mostrar en pantalla los mensajes.

my.mean <- function(x){
  message("Calulando...")
  calc <- sum(x) / length(x)
  message("Cálculo terminado")
  return(calc)
}
#prueba
my.mean(1:956)
#comparación
mean(1:956)

Es tu turno de crear funciones y comparar el resultado con una función predeterminada de R. En esta ocasión debes crear la funciones my.var y my.sd para comparar tus resultados con las funciones var() y sd(). Recuerda que la varianza obedece a la siguiente ecuación: $\mathrm{Var}(x) = \frac{1}{n - 1} \sum_{i=1}^n (x_i - \bar{x}) ^2$. Compara tus resultados con los vectores de prueba.

:::exercise

Ejercicio 3.3.3

:::

#vectores de prueba 
x = seq(1,50,5)
y = seq(1,15,length.out = 30)
#Funciones
my.var <-function(){}
my.sd <-function(){}
#pruebas
my.var(x)
my.sd(x)
my.var(y)
my.sd(y)
#comparacion
var(x)
sd(x)
var(y)
sd(y)
#vectores de prueba 
x = seq(1,50,5)
y = seq(1,15,length.out = 30)
#Funciones
my.var <-function(x){
  var = sum((x -mean(x))^2) / (length(x)-1)
  return(var)
}
my.sd <-function(){}
#pruebas
my.var(x)
my.sd(x)
my.var(y)
my.sd(y)
#comparacion
var(x)
sd(x)
var(y)
sd(y)
#vectores de prueba 
x = seq(1,50,5)
y = seq(1,15,length.out = 30)
#Funciones
my.var <-function(x){
  var = sum((x -mean(x))^2) / (length(x)-1)
  return(var)
}
my.sd <-function(x){
  sd = sqrt(my.var(x))
  return(sd)
}
#pruebas
my.var(x)
my.sd(x)
my.var(y)
my.sd(y)
#comparacion
var(x)
sd(x)
var(y)
sd(y)
grade_code("Excelente lo lograste, sigamos adelante")

Retomemos el ejemplo 3.1.4 de descuento de comida para perro y creemos una función que haga todo el trabajo cada vez que lo necesitemos. Al ser una función, cambiaremos algunos argumentos y detendremos el proceso con stop() cada vez que se encuentre un error, sea en los valores de compra o en los de descuento. Además se incluirá un argumento predeterminado que define si la salida es impresiones en pantalla o un vector.

:::example

Ejemplo 3.3.7

:::

descuento_perros <- function(bolsas,descuentos,to_vector = FALSE){
  total <- c()
  if(length(bolsas) != length(descuentos)){
    stop("La longitud de bolsas y descuentos no es la misma")
  }
  for( i in seq_along(bolsas)){
    cobro = 6800*bolsas[i]
    if(bolsas[i]<=0){
      stop(paste("El número de bolsas en la posición",i,"no es válido"))
    }
    if(descuentos[i]!=0 &descuentos[i]!=1){
      stop(paste("El código de descuento en la posición",i,"no es válido"))
    }
    if(descuentos[i]==1 & bolsas[i]<10){
      cobro = cobro *.5
    } else if(descuentos[i]==1 & bolsas[i]>=10){
      cobro = cobro * .4
    }
    if(to_vector == F){
      print(paste("Total a pagar:",cobro))
    } else{
      total <- c(total,cobro)
    }
  }
  if(to_vector==T){
    return(total)
  }
}

Creada la función podemos realizar múltiples pruebas sin necesidad de escribir repetidamente el código. Veamos algunas pruebas e intenta otras por tu cuenta. Para ejecutar las pruebas debes comentar las que presenten errores, ya que para ilustrar su funcionamiento hay pruebas que arrojan error. Estas están marcadas con el comentario #error al final.

#Prueba 1
descuento_perros(c(10,0,2,15,13,18,4,9,7,2,8,16),c(1,0,0,1,0,0,1,1,1,0,0,10)) #error
#Prueba 2
descuento_perros(c(10,2,15,18,16),c(1,0,0,1,10)) # error
#prueba 3
descuento_perros(c(10,2,15,18,16),c(1,0,0,1)) #error
#prueba 4
descuento_perros(c(10,2,15,18,16),c(1,0,0,1,0))
#prueba 5
descuento_perros(c(10,2,15,18,16),c(1,0,0,1,0),to_vector = TRUE)

Es tu turno de adaptar una función al ejercicio de las rosas tratado en el ejemplo 3.1.3. Como ya sabes en esta sección hay muchas posibles soluciones a los problemas planteados, sin embargo en solution encontraras una sola de todas las posibles. Verifica los resultados de la función con los dos objetos de prueba. Para ejecutar las pruebas debes comentar las que presenten errores, ya que para ilustrar su funcionamiento hay pruebas que arrojan error, estas están marcadas con el comentario #error al final.

:::exercise

Ejercicio 3.3.4

:::

# Objetos de prueba
val_1 <- -13
val_2 <- 16
vec_1 <- c(2,5,10,21,17,10,2,0,8,3)
vec_2 <- c(5,10,8,19,21,16,9,3)
#función
promocion_rosas <- function(){}
#Pruebas
promocion_rosas(val_1) #error
promocion_rosas(val_2)
promocion_rosas(vec_1) #error
promocion_rosas(vec_2)
# Objetos de prueba
val_1 <- -13
val_2 <- 16
vec_1 <- c(2,5,10,21,17,10,2,0,8,3)
vec_2 <- c(5,10,8,19,21,16,9,3)
#función
promocion_rosas <- function(rosas){
  for (i in seq_along(rosas)){
    rosas_total = rosas[i]
    if (rosas[i]<=0){
      stop(paste("El número de rosas en la posición",i,"no es válido"))
    }else if(rosas[i]==10){
      rosas_total = rosas[i] + 1
    }else if(rosas[i]>10){
      rosas_total = rosas[i] +3
    }
    print(paste("levarás",rosas_total - rosas[i], "rosas extra, total de rosas",rosas_total))
  }
}
#Pruebas
promocion_rosas(val_1) #error
promocion_rosas(val_2)
promocion_rosas(vec_1) #error
promocion_rosas(vec_2)
grade_code("Excelente lo lograste, sigamos adelante")

Implementa ahora una función para el ejercicio 3.1.2 de descuentos en la heladeria. Recuerda el operador %in% utilizado en el ejemplo 3.2.13. Verifica los resultados de la función con los dos objetos de prueba. Para ejecutar las pruebas debes comentar las que presenten errores, ya que para ilustrar su funcionamiento hay pruebas que arrojan error, estas están marcadas con el comentario #error al final.

:::exercise

Ejercicio 3.3.5

:::

#objetos de prueba
dia_x = "Miercoles"
total_x = 33700
vec_dias_1 = c("Domingo", "Lunes","Miercoles","jueves","Domingo","Martes","Viernes")
vec_total_1 = c(69500,71350,33700,54000,0,38000,53200)
vec_dias_2 = c("Domingo", "Lunes","Miercoles","Jueves","Domingo","Martes","Viernes")
vec_total_2 = c(69500,71350,33700,54000,12000,38000,53200)
# funcion
descuento_helados <- function(){}
#pruebas
descuento_helados(dia_x,total_x)
descuento_helados(vec_dias_1,vec_total_1) #error
descuento_helados(vec_dias_2,vec_total_2)
#objetos de prueba
dia_x = "Miercoles"
total_x = 33700
vec_dias_1 = c("Domingo", "Lunes","Miercoles","jueves","Domingo","Martes","Viernes")
vec_total_1 = c(69500,71350,33700,54000,0,38000,53200)
vec_dias_2 = c("Domingo", "Lunes","Miercoles","Jueves","Domingo","Martes","Viernes")
vec_total_2 = c(69500,71350,33700,54000,12000,38000,53200)
# funcion
descuento_helados <- function(dias,totales){
  dias_semana = c("Lunes","Martes","Miercoles","Jueves","Viernes","Sabado","Domingo")
  if(length(dias)!=length(totales)){
    stop("La longitud de dias y totales no es la misma")
  }
  for (i in seq_along(dias)){
    descuento = 0
    if(totales[i]<=0){
      stop(paste("El valor proporcionado en la posición",i,"no es válido"))
    }
    if(!dias[i] %in% dias_semana ){
      stop(paste("El día proporcionado en la posición",i,"no es válido"))
    }else if(dias[i] %in% dias_semana[1:3]){
      descuento = descuento + .25
      if (totales[i]>50000){
        descuento = descuento + .1
      }
    }else if(totales[i]>60000){
      descuento = descuento + .15
    }
    print(paste("Total:",totales[i],"Descuento:",totales[i]*descuento,"Total a pagar:" ,totales[i]*(1-descuento)))
  }

}
#pruebas
descuento_helados(dia_x,total_x)
descuento_helados(vec_dias_1,vec_total_1) #error
descuento_helados(vec_dias_2,vec_total_2)
grade_code("Excelente lo lograste, sigamos adelante")

Continuando con la teoría acerca de las funciones, trabajaremos el argumento especial ..., el cual tiene dos usos comunes: 1) Para extender una función y 2) Para permitir que la función posea cualquier número de argumentos adicionales. El primer caso es usado comúnmente cuando se anidan funciones. Veamos un ejemplo de este primer caso utilizando dos funciones my_fun_1 y my_fun_2.

:::example

Ejemplo 3.3.8

:::

#Funcion 1 : suma
my_fun_1<- function(v,w,x,y,z){v+w+x+y+z}
my_fun_1(1,2,3,4,5)
#Funcion 2 : suma * 2
my_fun_2 <- function(...){
  my_fun_1(...)*2
}
my_fun_2(1,2,3,4,5)

Para este ejemplo el argumento especial ... permite que my_fun_2 herede los mismos argumentos que my_fun_1, permitiendo hacer operaciones sobre el calculo de my_fun_1. Otro ejemplo simple del uso de ... que puede resultar un poco más familiar es una adaptación de las funciones my.var y my.sd en la solución del ejercicio 3.3.3. Veamos esta adaptación:

:::example

Ejemplo 3.3.9

:::

#Funcion de calculo de varianza
my.var <-function(x){
  var = sum((x -mean(x))^2) / (length(x)-1)
  return(var)
}
#Funcion de calculo de sd
my.sd <-function(...){
  sd = sqrt(my.var(...))
  return(sd)
}
# Prueba
my.sd(1:15)
sd(1:15)

Tratemos una adaptación un poco más elaborada, en la cual se extiendan al mismo tiempo las funciones de descuento_perros y descuento_helados cada una con sus respectivos argumentos.

:::example

Ejemplo 3.3.10

:::

#objetos de prueba
vec_dias_2 = c("Domingo", "Lunes","Miercoles","Jueves","Domingo","Martes","Viernes")
vec_total_2 = c(69500,71350,33700,54000,12000,38000,53200)
#Funcion descuentos 
descuentos <- function(...,perros=F,helados=F){
  if(helados==T & perros == F){
    message("Descuento en heladería")
    descuento_helados(...)
  }else if(perros == T & helados==F){
    message("Descuento en comida para perro")
    descuento_perros(...)
  }else{
    stop("Seleccione adecuadamente la opción de descuento")
  }
}

#pruebas
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T)
descuentos(vec_dias_2,vec_total_2,helados = T)
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T,to_vector=T)

Aunque el ejercicio de la floristería no corresponde exactamente a un descuento, en el siguiente problema debes volver a crear la función descuentos añadiendo la función promocion_rosas() usando el argumento especial ....

:::exercise

Ejercicio 3.3.6

:::

#objetos de prueba
vec_dias_2 = c("Domingo", "Lunes","Miercoles","Jueves","Domingo","Martes","Viernes")
vec_total_2 = c(69500,71350,33700,54000,12000,38000,53200)
#Funcion descuentos 
descuentos <- function(...,perros=F,helados=F,rosas=F){}
#pruebas
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T)
descuentos(vec_dias_2,vec_total_2,helados = T)
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T,to_vector=T)
descuentos(c(5,6,8,10,12,47),rosas = T)
#objetos de prueba
vec_dias_2 = c("Domingo", "Lunes","Miercoles","Jueves","Domingo","Martes","Viernes")
vec_total_2 = c(69500,71350,33700,54000,12000,38000,53200)
#Funcion descuentos 
descuentos <- function(...,perros=F,helados=F,rosas=F){
  if(helados==T & perros == F & rosas ==F){
    message("Descuento en heladería")
    descuento_helados(...)
  }else if(perros == T & helados==F & rosas==F){
    message("Descuento en comida para perro")
    descuento_perros(...)
  }else if(rosas==T & perros==F & helados==F){

  }else{
    stop("Seleccione adecuadamente la opción de descuento")
  }
}

#pruebas
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T)
descuentos(vec_dias_2,vec_total_2,helados = T)
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T,to_vector=T)
descuentos(c(5,6,8,10,12,47),rosas = T)
#objetos de prueba
vec_dias_2 = c("Domingo", "Lunes","Miercoles","Jueves","Domingo","Martes","Viernes")
vec_total_2 = c(69500,71350,33700,54000,12000,38000,53200)
#Funcion descuentos 
descuentos <- function(...,perros=F,helados=F,rosas=F){
  if(helados==T & perros == F & rosas ==F){
    message("Descuento en heladería")
    descuento_helados(...)
  }else if(perros == T & helados==F & rosas==F){
    message("Descuento en comida para perro")
    descuento_perros(...)
  }else if(rosas==T & perros==F & helados==F){
    message("Promoción de rosas")
    promocion_rosas(...)
  }else{
    stop("Seleccione adecuadamente la opción de descuento")
  }
}

#pruebas
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T)
descuentos(vec_dias_2,vec_total_2,helados = T)
descuentos(c(10,2,15,18,16),c(1,0,0,1,0),perros = T,to_vector=T)
descuentos(c(5,6,8,10,12,47),rosas = T)
grade_code("Excelente lo lograste, sigamos adelante")

El otro uso del argumento especial ... es en funciones que reciben cualquier número de argumentos, como ejemplo de ellas tenemos sum(), prod(), paste(), paste0() y otras más. Veamos cómo funcionan estas y creemos una que reciba cualquier cantidad de argumentos.

:::example

Ejemplo 3.3.11

:::

#Funciones base
sum(1,3,5,7,89,c=5,b=8)
prod(3,5,d=8,9)
paste("Hola","Soy una función","con",ar=4,"argumentos")
paste0("c","i","n","c","o")
#Funcion
fun_5 <- function(...){
  list(...)
}
#pruebas
fun_5(1,3,5,7,89,c=5,b=8)
fun_5("Hola","Soy una función","con",ar=4,"argumentos")

fun_5 recibe cualquier cantidad de elementos y los guarda en una lista. El uso que le demos a ella depende de nuestras necesidades y habilidades con las listas.

3.4 Familia apply

Se conoce como familia apply al conjunto de funciones usadas para aplicar funciones en matrices, dataframe, arreglos y listas. Corresponden a una de las características distintivas de R como lenguaje de programación y se utilizan frecuentemente para automatizar tareas complejas. Las funciones de la familia apply se caracterizan por recibir como argumentos a un objeto y al menos una función. Las funciones pertenecientes a esta familia son las siguientes:

  • apply()
  • lapply()
  • sapply()
  • eapply()
  • mapply()
  • rapply()
  • tapply()
  • vapply()

Algunas de estas funciones tienen aplicaciones sumamente específicas y profundizar en ellas no resulta apropiado para un curso corto. Por esta razón, se profundizará solo en algunas de ellas iniciando con la función apply().

apply(X,MARGIN,FUN,...)

Esta función recibe 3 argumentos esenciales y argumentos adicionales inherentes a una función específica, donde:

  • X : Una matriz o un objeto que pueda coercionarse a una matriz.
  • MARGIN : 1 para operar sobre filas, 2 para operar sobre columnas.
  • FUN : Operador o función aplicada.
  • ... : Argumentos adicionales de la función aplicada.

apply() devuelve generalmente un vector y en los casos donde la función devuelve un vector de longitud n devuelve una matriz. Veamos algunos ejemplos.

:::example

Ejemplo 3.4.1

:::

#objetos de prueba
df = data.frame(x=1:10,y=11:20,z=21:30)
m = matrix(1:25,ncol = 5)
#producto de elementos por filas
apply(X = df,MARGIN = 1,FUN = prod)
apply(X = m,MARGIN = 1,FUN = prod)
#producto de elementos por columna
apply(X = df,MARGIN = 2,FUN = prod)
apply(X = m,MARGIN = 2,FUN = prod)
#raiz cuadrada orientacíon filas
apply(df,1,sqrt)

También podemos usar apply() con funciones de usuario e incluso con funciones anónimas. Para ilustrar un ejemplo con estas funciones recordemos el ejemplo de la floristería, tomando como objeto de prueba un dataset que contiene la venta de tres días en la semana.

:::example

Ejemplo 3.4.2

:::

#objetos de prueba
rosas <-  data.frame(lunes=c(2,5,10,21,17,10,2,8,3),
                     martes=c(5,10,8,19,21,16,9,3,19),
                     miercoles=c(10,4,8,19,29,1,2,10,2))
#apply por columna
apply(rosas,2,promocion_rosas)
#funcion anonima filas
apply(rosas,1, function(x) ifelse(x<10,x,ifelse(x==10,x+1,x+3)))
#funcion anonima columnas
apply(rosas,2, function(x) ifelse(x<10,x,ifelse(x==10,x+1,x+3)))

Para entender cómo funcionan los argumentos adicionales al usar apply() utilizaremos la función quantile(), sobre los tres días de ventas de rosas. Primero utilizaremos la función sin argumentos adicionales, de la siguiente forma:

:::example

Ejemplo 3.4.3

:::

#objetos de prueba
rosas <-  data.frame(lunes=c(2,5,10,21,17,10,2,8,3),
                     martes=c(5,10,8,19,21,16,9,3,19),
                     miercoles=c(10,4,8,19,29,1,2,10,2))
#apply por dias
apply(rosas,2,quantile)

Recuerda para conocer los argumentos de una función y la ayuda de la misma puedes ejecutar ?quantile. Ahora usemos los argumentos adicionales para seleccionar cuantiles de interés diferentes a los que están por defecto.

#objetos de prueba
rosas <-  data.frame(lunes=c(2,5,10,21,17,10,2,8,3),
                     martes=c(5,10,8,19,21,16,9,3,19),
                     miercoles=c(10,4,8,19,29,1,2,10,2))
#apply por dias
apply(rosas,2,quantile,probs=c(.33,.66,.99))

Recordemos el ejemplo 2.3.3.4 en el cual determinamos la representación en porcentual de cada elemento de la columna sobre la suma total de la misma. En aquella ocasión determinar esa representación porcentual tomó aproximadamente 8 líneas de código. Ahora con apply(), debes lograrlo en una sola línea.

:::exercise

Ejercicio 3.4.1

:::

#Matriz de prueba 1
m <- matrix(seq(18, 2, length.out = 16), ncol = 4, byrow = TRUE)
m
#porcentaje columna
#Matriz de prueba 1
m <- matrix(seq(18, 2, length.out = 16), ncol = 4, byrow = TRUE)
m
#porcentaje columna
apply(m, 2, function(x) round(100*x/sum(x), 2))
grade_code("Excelente lo lograste, sigamos adelante")

Intenta ahora la estandarización por columnas de la siguiente matriz. Recuerda que la estandarización para este caso corresponde a restar la media de la columna y dividir por la desviación estándar a cada elemento de la misma columna.

:::exercise

Ejercicio 3.4.2

:::

set.seed(20)
#Matriz de prueba 1
m <- matrix(seq(8, 2, length.out = 16), ncol = 4, byrow = TRUE)
m
#estandarización
set.seed(20)
#Matriz de prueba 1
m <- matrix(sample(1:8, 16, replace = TRUE), ncol = 4, byrow = TRUE)
m
#estandarización
apply(m,2,function(x) (x-mean(x))/sd(x))
grade_code("Excelente lo lograste, sigamos adelante")

La siguiente función de la familia apply en la que profundizaremos es lapply()

lapply(X,FUN,...)

Esta función recibe 2 argumentos esenciales y argumentos adicionales inherentes a una función específica, donde:

  • X : Es una lista o un objeto que pueda coercionarse a una lista.
  • FUN : Operador o función aplicada.
  • ... : Argumentos adicionales de la función aplicada.

lapply() devuelve una lista. Veamos algunos ejemplos, iniciando con la adaptación del ejemplo 3.4.1

:::example

Ejemplo 3.4.4

:::

#objetos de prueba
df = data.frame(x=1:10,y=11:20,z=21:30)
m = matrix(1:25,ncol = 5)
#producto de elementos por columnas
lapply(X = df,FUN = prod)
lapply(X = as.data.frame(m),FUN = prod)
#raiz cuadrada de cada elemento
lapply(df,sqrt)

Al igual que apply(), lapply() admite funciones anónimas, usemos el ejemplo 3.4.2 para verlo.

:::example

Ejemplo 3.4.5

:::

#venta de rosas
rosas <-  data.frame(lunes=c(2,5,10,21,17,10,2,8,3),
                     martes=c(5,10,8,19,21,16,9,3,19),
                     miercoles=c(10,4,8,19,29,1,2,10,2))
#promoción 
lapply(rosas, function(x) ifelse(x<10,x,ifelse(x==10,x+1,x+3)))

Veamos un ejemplo involucrando el descuento en comida de perro, en el cual se registraron ventas en tres días de la semana en una lista ventas, cada una con una lista contiene el número de bolsas compradas y el código de descuento. En este ejemplo usaremos además una función anónima que llama una función de usuario.

:::example

Ejemplo 3.4.6

:::

#ventas
ventas = list(lunes=list(bolsas=c(10,2,15,18,16),des=c(1,0,0,1,0)),
              martes=list(bolsas=c(11,13,1,4,6,9),des=c(0,0,1,1,0,1)),
              miercoles=list(bolsas=c(3,10,4,1),des=c(1,1,0,1)))
#descuento comida de perros
lapply(ventas,function(x) descuento_perros(x[[1]],x[[2]],to_vector = T))

Tratemos un ejemplo más familiar recordando el dataset amigos y calculemos el IMC de cada individuo utilizando lapply().

:::example

Ejemplo 3.4.7

:::

#datos 
amigos$fisico
#Calculo IMC
lapply(amigos$fisico,function(list) unname(list[3]/(list[2]/100)**2))

Para terminar con la función lapply() veamos un ejemplo en el que utilicemos una función anónima para llamar la función apply() y aplicarla sobre tres matrices tal cual como se hizo en el ejercicio 3.4.2.

:::example

Ejemplo 3.4.8

:::

#Matrices de prueba 
m1 <- matrix(seq(18, 2, length.out = 16), ncol = 4, byrow = TRUE)
m2 <- matrix(1:16, ncol = 4)
m3 <- matrix(seq(10, 74, length.out = 16), ncol = 4, byrow = TRUE)
#lista de matrices
mm <- list(m1,m2,m3)
#porcentaje
lapply(mm,function(m) apply(m,2,function(x) round(100*x/sum(x),2)))

La última función de la familia apply que trataremos será sappply(), la cual es muy similar a lapply() salvo por la devolución, ya que sapply() devuelve, cuando es posible un objeto mas simple (vector o matriz) en lugar de una lista.

sapply(X,FUN,...,simplify = TRUE)

Esta función recibe 2 argumentos esenciales, y argumentos adicionales inherentes a una función específica, además permite modificar dos argumentos por defecto,donde:

  • X : Es una lista o un objeto que pueda coercionarse a una lista.
  • FUN : Operador o función aplicada.
  • ... : Argumentos adicionales de la función aplicada.
  • simplify : TRUE, devuelve un vector cuando sea posible; FALSE, cumple la misma función que lapply()

Para entender los casos en los que sapply() funciona debes adaptar los ejemplo de lapply(), iniciando con el ejemplo 3.4.4.

:::exercise

Ejercicio 3.4.3

:::

#objetos de prueba
df = data.frame(x=1:10,y=11:20,z=21:30)
m = matrix(1:25,ncol = 5)
#producto de elementos por columnas
sapply()
sapply()
#raiz cuadrada de cada elemento
sapply()
#objetos de prueba
df = data.frame(x=1:10,y=11:20,z=21:30)
m = matrix(1:25,ncol = 5)
#producto de elementos por columnas
sapply(X = df,FUN = prod)
sapply(X = as.data.frame(m),FUN = prod)
#raiz cuadrada de cada elemento
sapply(df,sqrt)
grade_code("Excelente lo lograste, sigamos adelante")

Intenta ahora con el ejemplo 3.4.5 de la promoción de rosas.

:::exercise

Ejercicio 3.4.4

:::

#venta de rosas
rosas <-  data.frame(lunes=c(2,5,10,21,17,10,2,8,3),
                     martes=c(5,10,8,19,21,16,9,3,19),
                     miercoles=c(10,4,8,19,29,1,2,10,2))
#promoción 
#venta de rosas
rosas <-  data.frame(lunes=c(2,5,10,21,17,10,2,8,3),
                     martes=c(5,10,8,19,21,16,9,3,19),
                     miercoles=c(10,4,8,19,29,1,2,10,2))
#promoción 
sapply(rosas, function(x) ifelse(x<10,x,ifelse(x==10,x+1,x+3)))
grade_code("Excelente lo lograste, sigamos adelante")

Por último para evidenciar un caso en el que la simplificación no es posible intenta la adaptación del ejemplo 3.4.6.

:::exercise

Ejercicio 3.4.5

:::

#ventas
ventas = list(lunes=list(bolsas=c(10,2,15,18,16),des=c(1,0,0,1,0)),
              martes=list(bolsas=c(11,13,1,4,6,9),des=c(0,0,1,1,0,1)),
              miercoles=list(bolsas=c(3,10,4,1),des=c(1,1,0,1)))
#descuento comida de perros
#ventas
ventas = list(lunes=list(bolsas=c(10,2,15,18,16),des=c(1,0,0,1,0)),
              martes=list(bolsas=c(11,13,1,4,6,9),des=c(0,0,1,1,0,1)),
              miercoles=list(bolsas=c(3,10,4,1),des=c(1,1,0,1)))
#descuento comida de perros
sapply(ventas,function(x) descuento_perros(x[[1]],x[[2]],to_vector = T))
grade_code("Excelente lo lograste, sigamos adelante")

A partir de las funciones de la familia apply nombradas anteriormente, se puede obtener una noción básica del funcionamiento de la familia completa. Para abordar y conocer mejor las otras funciones de esta familia de funciones, te recomendamos revisar la documentación de R.

4. Datos ordenados y el tidyverse

knitr::include_graphics("images/1ra_imagen.png")  

4.1 El tidyverse

El tidyverse es una colección de paquetes R que trabajan en armonía con el objetivo de cubrir todo el espectro de análisis de datos dentro de R. Los paquetes dentro del tidyverse (al menos los abordados en este curso) son:

`readr` cuyo objetivo consiste en proporcionar una forma rápida y amigable de leer (__importar__) los datos.
wzxhzdk:246
`tidyr` cuyo objetivo consiste en ayudar a crear __datos ordenados__.
wzxhzdk:247
`dplyr` que proporciona un conjunto consistente de funciones que ayudan a resolver los desafíos más comunes de la __manipulación__ de datos.
wzxhzdk:248
`ggplot2` para crear __gráficos__ declarativamente, basado en la denominada gramática de gráficos.
wzxhzdk:249

Para usar los paquetes del tidyverse se deben instalar primero. Para esto, los paquetes se pueden instalar de forma individual:

install.packages('readr')
install.packages('tidyr')
install.packages('dplyr')
install.packages('ggplot2')

y luego cargarlos una vez se necesiten:

library(readr)
library(tidyr)
library(dplyr)
library(ggplot2)

O simplemente se puede instalar y cargar el paquete tidyverse:

install.packages('tidyverse')

library(tidyverse)

tidyverse_update() # Si desea actualizar el tidyverse.

4.2 Datos ordenados

En la naturaleza, los conjuntos de datos vienen en muchos formatos diferentes.

:::example

Ejemplo 4.2.1

:::

library(DSR)

table1 %>%
  rename('País' = country, 'Año' = year, 'Casos' = cases, 'Población' = population) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'
    ))%>%
  kable() %>% 
  kable_styling(full_width = FALSE,
                bootstrap_options = (c('striped', 'bordered')))
table2 %>%
  rename('País' = country, 'Año' = year, 'Variables' = key, 'Valor' = value) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'),
    Variables = case_when(
    Variables == 'cases' ~ 'Casos',
    Variables == 'population' ~ 'Población')
    ) %>%
  kable() %>% 
  kable_styling(full_width = FALSE,
                bootstrap_options = (c('striped', 'bordered')))
table3 %>%
  rename('País' = country, 'Año' = year, 'Tasa' = rate) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'
    ))%>%
  kable() %>% 
  kable_styling(full_width = FALSE,
                bootstrap_options = (c('striped', 'bordered')))
wzxhzdk:256
wzxhzdk:257

Los conjuntos de datos anteriores muestran los mismos datos organizados en cuatro formas diferentes. Sin embargo el conjunto de datos que cumple las siguientes tres reglas es mucho más fácil para trabajar en R:

knitr::include_graphics("images/5ta_imagen.png")  

Los datos que satisfacen estas reglas se conocen como datos ordenados.

:::exercise

Ejercicio 4.2.1

Teniendo en cuenta las reglas sobre datos ordenados, ¿cual de los cuatro conjuntos de datos anteriormente mencionados considera cumple con este principio? :::

question(" ",
  answer("Conjunto de datos 4", message = "No estas en lo correcto, sigue intentandolo."),
  answer("Conjunto de datos 1", correct = TRUE, message = "Estas en lo correcto... en este conjunto de datos cada variable se coloco en su propia columna, cada observación en su propia fila y cada valor en su propia celda."),
  answer("Conjunto de datos 2", message = "No estas en lo correcto, sigue intentandolo."),
  answer("Conjunto de datos 3", message = "No estas en lo correcto, sigue intentandolo."),
  allow_retry = TRUE,
  random_answer_order = TRUE
)

Los datos ordenados funcionan bien en R porque R es un lenguaje de programación vectorizado. Los conjuntos de datos en R están construidos a partir de vectores y las operaciones de R están optimizadas para trabajar con vectores. Los datos ordenados aprovechan estas dos características.

knitr::include_graphics("images/6ta_imagen.png")  

:::caution Los datos ordenados fueron popularizados por Hadley Wickham, y sirven como base para muchos paquetes y funciones de R. Puede obtener más información sobre datos ordenados leyendo Tidy Data, un documento escrito por Hadley Wickham y publicado en el Journal of Statistical Software. :::

4.3 Ordenando los datos con tidyr

El paquete tidyr tiene como objetivo ayudarle a ordenar sus datos. Contiene varias funciones que alteran el diseño de los conjuntos de datos, al tiempo que conserva los valores:

La función pivot_wider() es usada cuando se tiene una observación dispersa en múltiples filas.

:::example

Ejemplo 4.3.1

:::

datos_2 <- DSR::table2 %>%
  rename('País' = country, 'Año' = year, 'Variables' = key, 'Valor' = value) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'),
    Variables = case_when(
    Variables == 'cases' ~ 'Casos',
    Variables == 'population' ~ 'Población')
    )
save(datos_2,file = "data_pkg/datos_2.rda", compress='xz')
datos_2
knitr::include_graphics("images/7ma_imagen.png")  
datos_2_ancho <- pivot_wider(
  data = datos_2, # El nombre del conjunto de datos a ordenar.
  names_from = Variables, # Argumento que indica el nombre de la columna donde se encuentran las variables.
  values_from = Valor # Argumento que indica el nombre de la columna que contiene los valores de las variables.
  )

datos_2_ancho

La función pivot_longer() permite resolver las situaciones en donde se tienen columnas que realmente no representan variables, sino valores de una misma variable.

:::example

Ejemplo 4.3.2

:::

datos_4_a <- DSR::table4 %>%
  rename('País' = country) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'
    ))
save(datos_4_a,file = "data_pkg/datos_4_a.rda", compress='xz')
datos_4_a
knitr::include_graphics("images/8va_imagen.png")  
datos_4_a_largo <- pivot_longer(
  data = datos_4_a, # El nombre del conjunto de datos a ordenar.
  cols = c('1999', '2000'), # Argumento donde se indican las columnas que pueden ser una variable.
  names_to = 'Año', # Argumento donde se indica el nombre de la columna a crear a partir de los datos almacenados anteriormente como columna de datos.
  values_to = 'Casos' # Argumento donde se indica el nombre de la columna a crear a partir de los datos almacenados anteriormente como valores de celda.
  )

datos_4_a_largo

:::exercise

Ejercicio 4.3.1

Teniendo en cuenta el ejemplo planteado anteriormente con la función pivot_longer(), por favor intente hacer lo mismo con el siguiente conjunto de datos llamado datos_4_b con el fin obtener la estructura de datos ordenados presentada en la imagen a continuación. :::

datos_4_b <- DSR::table5 %>%
  rename('País' = country) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'
    ))
save(datos_4_b,file = "data_pkg/datos_4_b.rda", compress='xz')
datos_4_b
knitr::include_graphics("images/9na_imagen.png")  

datos_4_b_largo <- pivot_longer(
  data = datos_4_b,
  cols = c('1999', '2000'),
  names_to = 'Años',
  values_to = 'Población'
  )

datos_4_b_largo
grade_code("¡Muy bien!, a partir de este cambio cada variable se coloco en su propia columna, cada observación en su propia fila y cada valor en su propia celda.")

:::caution Como se observó, la función pivot_longer() hace lo opuesto a pivot_wider(). Por lo tanto, ambas funciones son complementarias, es decir, si al resultado de aplicar la función pivot_wider() se le aplica la función pivot_longer() se llega al conjunto de datos original. Otra observación interesante es que pivot_longer() alarga los conjuntos de datos, mientras que pivot_wider() los hace más anchos. :::

La función separate() lo que hace es dividir una columna en múltiples columnas, tomando como separador algún símbolo, mientras que la función unite() toma múltiples columnas y las une en una única columna, separando los elementos mediante un separador.

:::example

Ejemplo 4.3.3

:::

datos_3 <- DSR::table3 %>%
  rename('País' = country, 'Año' = year, 'Tasa' = rate) %>%
  mutate(País = case_when(
    País == 'Afghanistan' ~ 'Afganistán',
    País == 'Brazil' ~ 'Brasil',
    País == 'China' ~ 'China'
    ))
save(datos_3,file = "data_pkg/datos_3.rda", compress='xz')
datos_3
knitr::include_graphics("images/10ma_imagen.png")  
datos_3_separado <- separate(
  data = datos_3, # El nombre del conjunto de datos a ordenar.
  col = Tasa, # Argumento donde se indica el nombre de la columna que se quiere dividir.
  into =  c('Casos', 'Población'), # Argumento donde se indica los nombres de las nuevas variables.
  sep  =  '/'#, # Argumento donde se indica el símbolo que separa las dos variables en una misma columna.
  #convert = TRUE # Opción que permite hacer la conversión de tipo caracter a numérico.
  )

datos_3_separado

:::exercise

Ejercicio de repaso

:::

El siguiente conjunto de datos contiene casos de tuberculosis registrados en distintos años. Dichos datos se encuentran en el Informe Gobal de Tuberculosis de la Organización Mundial de la Salud, disponible para descargar aquí. Este conjunto de datos proporciona un ejemplo realista de datos desordenados.

data('who')

Tuberculosis <- DSR::who
Tuberculosis

La característica más peculiar del anterior conjunto de datos es su sistema de codificación. Las columnas cinco a sesenta codifican cuatro partes de información que separadas significan lo siguiente:

1) Las primeras tres letras de cada columna indican si los casos de tuberculosis corresponden a casos nuevos o antiguos. En este conjunto de datos, cada columna contiene solo nuevos casos.

2) Las siguientes dos letras describen el tipo de caso de tuberculosis: - rel significa casos de recaída, - ep significa casos de tuberculosis extrapulmonar, - sn significa casos de tuberculosis pulmonar que no pudieron ser diagnosticados por un frotis pulmonar (frotis negativo), - sp significa casos de tuberculosis pulmonar que podrían diagnosticarse por un frotis pulmonar (frotis positivo).

3) La sexta parte describe el sexo de los pacientes con tuberculosis: m para hombres y f para mujeres.

4) La última parte describe el grupo de edad de los pacientes con tuberculosis. El conjunto de datos agrupa los casos en siete grupos de edad: - 014 significa pacientes de 0 a 14 años de edad, - 1524 significa pacientes de 15 a 24 años de edad, - 2534 representa pacientes de 25 a 34 años de edad, - 3544 significa pacientes que tienen entre 35 y 44 años, - 4554 significa pacientes de 45 a 54 años de edad, - 5564 significa pacientes de 55 a 64 años de edad, - 65 significa pacientes que tienen 65 años o más.

El ejercicio de repaso propuesto consiste en que haciendo uso de las funciones mecionadas del paquete tidyr trate de ordenar el conjunto de datos Tuberculosis, e intente obtener un conjunto de datos similar a como se observa en la imagen a continuación.

knitr::include_graphics("images/11va_imagen.png")  
save(Tuberculosis, file = 'data_pkg/Tuberculosis.rda', compress='xz')

Tuberculosis_2 <- pivot_longer(
  data = ,
  cols = c(),
  names_to = 'codificacion',
  values_to = 'valor'
  )

Tuberculosis_3 <- (
  data = Tuberculosis_2,
  col = codificacion,
  into = c('new', 'type', 'sexage'),
  sep = ' '
  )

Tuberculosis_4 <- separate(
  data = ,
  col = sexage,
  into = c(' ', ' '),
  sep = 1 # Aqui 1 indica el primer elemento dentro de la variable sexage, es decir m o f.
  )

Tuberculosis_5 <- pivot_wider(
  data = Tuberculosis_4,
  names_from = type,
  values_from = Valor
  )
Tuberculosis_2 <- pivot_longer(
  data = Tuberculosis,
  cols = c(5:60),
  names_to = 'codificacion',
  values_to = 'valor'
  )

Tuberculosis_3 <- separate(
  data = Tuberculosis_2,
  col = codificacion,
  into = c('new', 'tipo', 'sexage'),
  sep = '_'
  )

Tuberculosis_4 <- separate(
  data = Tuberculosis_3,
  col = sexage,
  into = c('sex', 'age'),
  sep = 1 # Aqui 1 indica el primer elemento dentro de la variable sexage.
  )

Tuberculosis_5 <- pivot_wider(
  data = Tuberculosis_4,
  names_from = tipo,
  values_from = valor
  )

5. Manipulación/limpieza de datos

5.1 Descripción de los datos a usar

En esta parte del curso sobre la manipulación de datos se empleara el conjunto de datos penguins, el cual resultó de una investigación donde se examinó el dimorfismo sexual ecológico entre pingüinos del género Pygoscelis.

knitr::include_graphics("images/Pygoscelis_penguins.png")  
Ilustracíon hecha por \@allison_horst.

5.1.1 Información sobre el conjunto de datos

Descargo de responsabilidad: El conjunto de datos penguins hace parte del paquete palmerpenguins. Este conjunto de datos debe entenderse como datos de muestra para aprender sobre herramientas de manipulación y visualización.

Cita de datos: Gorman KB, Williams TD, Fraser WR (2014) Dimorfismo sexual ecológico y variabilidad ambiental dentro de una comunidad de Pingüinos antárticos (Género Pygoscelis). PLoS ONE 9 (3): e90081. https://doi.org/10.1371/journal.pone.0090081

Estructura general de lo datos: A continuación podrá hechar un vistazo de la estructura del conjunto de datos penguins:

palmerpenguins::penguins

1) En speciespodrá encontrar las tres especies existentes del genéro de pingüinos Pygoscelis.

2) En island podrá encontrar el nombre de las islas donde fueron ubicadas las colonias de pingüinos.

3) En `culmen_length_mm` se registró la longitud del culmen (margen superior del pico) de las tres especies de pingüinos. 4) En `culmen_depth_mm` se registró la profundidad del culmen de las tres especies de pingüinos.
wzxhzdk:291
Ilustracíon hecha por \@allison_horst.

5) En flipper_length_mm se registró la longitud de la aleta de las tres especies de pingüinos.

6) En body_mass_g se registró la masa corporal de cada uno de los pingüinos.

7) En sex podrá encontrar la información sobre el sexo (FEMALE para hembras y MALE para machos) de cada uno de los pingüinos.

5.2 Manipulación de datos con dplyr

Se suele decir que la manipulación y la limpieza de los datos suele ocupar un 80% del tiempo en el análisis de datos. También es sabido que esta no es una experiencia alegre. Sin embargo, existen herramientas disponibles que ayudan en esta tarea.

El paquete dplyr es un paquete que permite obtener partes de los datos de una manera rápida, fácil de entender y fácil de replicar. Aprender y usar este paquete hará del proceso de manipulación y limpieza de datos una tarea más agradable.

knitr::include_graphics("images/tidyverse_war.jpg")  
Ilustracíon hecha por \@allison_horst.

5.2.1 Una gramática para la manipulación de datos

El paquete dplyr tiene como objetivo proporcionar una función para cada verbo básico de la manipulación de datos. Estos verbos se pueden organizar en tres categorías según el componente del conjunto de datos sobre el que trabajan:

1) Filas: + `filter()`. + `slice()`. + `arrange()`. 2) Columnas: + `select()`. + `rename()`. + `mutate()`. + `relocate()`. 3) Grupos de filas y columnas: + `summarise()`.
wzxhzdk:293
Ilustracíon hecha por \@allison_horst.
5.2.1.1 Filtrar filas con filter()

La función filter() permite elegir y extraer filas que satisfacen ciertas condiciones. La sintaxis general de filter() es: filter(dataset, condition).

Si desea seleccionar un grupo específico de valores de una variable de tipo carácter, se puede usar el operador de comparación ==.

:::example

Ejemplo 5.2.1.1.1

:::

knitr::include_graphics("images/12va_imagen.png")  
filter(
  .data = penguins,
  species == 'Adelie',
  island == 'Dream'
  )

:::caution Si bien en el ejemplo se uso el operador de comparación igual a (==), se pueden utilizar otros operadores. Por ejemplo, filter(.data = penguins, species != 'Adelie'), seleccionará todas las filas diferentes de (!=) Adelie. :::

Si desea filtrar variables numéricas en función de sus valores, puede hacerlo por medio de los operadores >, >=, <, <=, == y !=.

:::example

Ejemplo 5.2.1.1.2

:::

filter(
  .data = penguins,
  body_mass_g < 4400
  )
filter(
  .data = penguins,
  body_mass_g <= 4400,
  body_mass_g >= 3800
  )

filter(
  .data = penguins,
  between(body_mass_g, 3800, 4400)
  )

Para filtrar filas vacías se puede usar la función is.na() dentro de filter().

:::example

Ejemplo 5.2.1.1.3

:::

filter(
  .data = penguins,
  !is.na(culmen_length_mm)
  )

5.2.1.2 Elegir filas usando su posición con slice()

La función slice() permite indexar filas por sus ubicaciones dentro del conjunto de datos. Esta permite seleccionar, eliminar y duplicar filas.

:::example

Ejemplo 5.2.1.2.1

:::

knitr::include_graphics("images/13va_imagen.png")  
slice(
  .data = penguins,
  c(1:4)
  )
slice_head(
  .data = penguins,
  n = 4
  )

slice_tail(
  .data = penguins,
  n = 4
  )
slice_sample(
  .data = penguins,
  n = 4
  )
slice_min(
  .data = penguins,
  culmen_depth_mm, n = 4
  )

slice_max(
  .data = penguins,
  body_mass_g, n = 4
  )

5.2.1.3 Organizar filas con arrange()

La función arrange() permite reordenar las filas un conjunto de datos en función del valor de una determinada variable. Esto puede ser útil si se desea ver rápidamente qué mediciones tuvieron los valores más altos o más bajos.

:::example

Ejemplo 5.2.1.3.1

:::

knitr::include_graphics("images/14va_imagen.png")  
arrange(
  .data = penguins,
  desc(culmen_depth_mm)
  )
arrange(
  .data = penguins,
  culmen_depth_mm
  )

:::caution Al usar la función arrange() siempre se pondrán los valores de NA al final del conjunto de datos. :::

5.2.1.4 Seleccionar columnas con select()

La función select() permite elegir y extraer columnas de interés de un conjunto de datos.

:::example

Ejemplo 5.2.1.4.1

:::

knitr::include_graphics("images/15va_imagen.png")  
select(
  .data = penguins,
  species,
  culmen_length_mm,
  culmen_depth_mm,
  body_mass_g
  )

select(
  .data = penguins,
  species,
  culmen_length_mm:culmen_depth_mm,
  body_mass_g
  )

select(
  .data = penguins,
  -island,
  -flipper_length_mm,
  -sex
  )

select(
  .data = penguins,
  -(species:sex),
   species,
   culmen_length_mm,
   culmen_depth_mm,
   body_mass_g
  )

Si se tiene una gran cantidad de columnas o variables con una estructura similar, se puede utilizar la concordancia parcial mediante la adición de starts_with(), ends_with() o contains() en la sentencia de selección.

:::example

Ejemplo 5.2.1.4.2

:::

select(
  .data = penguins,
  starts_with('culmen')
  )

select(
  .data = penguins,
  ends_with('mm')
  )

select(
  .data = penguins,
  contains('length')
  )

Puede identificar el nombre de las columnas inicialmente, y luego referirse a ellas dentro de la función select() por medio de la función one_of() o utilizando el operador !!.

:::example

Ejemplo 5.2.1.4.3

:::

variables_cualitativas <- c('species', 'island', 'sex')

select(
  .data = penguins,
  one_of(variables_cualitativas)
  )

select(
  .data = penguins,
  !!variables_cualitativas
  )

La función select_if() permite seleccionar columnas en base a su tipo de dato. Para esto, se puede emplear las funciones is.character(), is.numeric(), is.integer(), is.double(), is.logical(), is.factor().

:::example

Ejemplo 5.2.1.4.4

:::

select_if(
  penguins,
  is.factor
  )

select_if(
  penguins,
  ~!is.numeric(.)
  )

:::exercise

Ejercicio 5.2.1.4.1

En el ejemplo anterior se seleccionaron solo las columnas o variables cuyo valor es de tipo distinto al numérico. Intente por favor seleccionar esta vez aquellas columnas o variables numéricas. :::


select_if(
  penguins,
  is.numeric
  )

select_if(
  penguins,
  ~!is.factor(.)
  )
grade_code("¡Muy bien! Sigue así y lograras grandes avances.")

Puede cambiar el nombre de las columnas o variables con la función select().

:::example

Ejemplo 5.2.1.4.5

:::

select(
  .data = penguins,
  gender = sex
  )

5.2.1.5 Cambiar el nombre de las columnas con rename()

La función rename() permite cambiar los nombres de las columnas o variables.

:::example

Ejemplo 5.2.1.5.1

:::

knitr::include_graphics("images/16va_imagen.png")  
rename(
  .data = penguins,
  gender = sex
  )

5.2.1.6 Agregar nuevas columnas con mutate()

A menudo es útil agregar nuevas variables o columnas que son funciones de las ya existentes. Esto se puede hacer por medio de la función mutate().

:::example

Ejemplo 5.2.1.6.1

:::

knitr::include_graphics("images/17va_imagen.png")  
mutate(
  .data = penguins,
  body_mass_kg = body_mass_g / 1000
  )

transmute(
  .data = penguins,
  body_mass_kg = body_mass_g / 1000
  )
mutate(
  .data = penguins,
  body_mass_g_VS_prom_body_mass = body_mass_g - round(mean(body_mass_g, na.rm = TRUE), digits = 1),
  body_mass_g_VS_min_body_mass = body_mass_g - min(body_mass_g, na.rm = TRUE)
  )

Para cambiar el nombre de los valores de las columnas o variables cualitativas, se puede usar la función recode() dentro de la función mutate().

:::example

Ejemplo 5.2.1.6.2

:::

mutate(
  .data = penguins,
  sex_2 = recode(
    .x = sex,
    'FEMALE' = 'Hembra',
    'MALE' = 'Macho'
    )
  )

Si desea convertir una columna numérica en una columna o variable cualitativa, puede hacer uso de la función case_when().

:::example

Ejemplo 5.2.1.6.3

:::

mutate(
  .data = penguins,
  body_mass_2 = case_when(
    body_mass_g < 3400 ~ 'Liviano',
    body_mass_g >= 3400 & body_mass_g <= 4400 ~ 'Normal',
    body_mass_g > 4400 ~ 'Pesado'
    )
  )

5.2.1.7 Cambiar el orden de las columnas con relocate()

Una forma fácil de cambiar el orden de las columnas en el conjunto de datos es mediante el uso de la función relocate().

:::example

Ejemplo 5.2.1.7.1

:::

knitr::include_graphics("images/18va_imagen.png")  
relocate(
  .data = penguins,
  species:sex,
  .before = culmen_length_mm
  )

:::exercise

Ejercicio 5.2.1.7.1

En el ejemplo anterior la columna culmen_length_mm se movio hacia la última columna por medio de la función .before(). Intente por favor haciendo uso de la función .after() mover esa misma columna para que sea la primera columna. :::


relocate(
  .data = penguins,
  species:sex,
  .after = culmen_length_mm
  )

5.2.1.8 Resumir valores con summarise()/summarize() y group_by()

La función summarise() (o summarize()) permite obtener un nuevo conjunto de datos el cual contiene un resumen de una determinada columna, calculando un valor único de los múltiples valores en esa columna.

:::example

Ejemplo 5.2.1.8.1

:::

summarise(
  .data = penguins,
  Prom_length = round(mean(culmen_length_mm, na.rm = TRUE), digits = 2),
  Prom_depth = round(mean(culmen_depth_mm, na.rm = TRUE), digits = 2)
  )

Promedio <- function(x){
  x = na.omit(x)
  Suma = sum(x)
  Total = length(x)
  Media = Suma/Total
  print(Media)
  }


summarise(
  .data = penguins,
  Prom_length = Promedio(culmen_length_mm),
  Prom_depth = Promedio(culmen_depth_mm)
  )

Usar la función summarise() puede ser útil por sí sola, pero es aún más útil cuando se usa para saber las diferencias entre grupos. Para hacer esto, se puede combinar con la función group_by().

:::example

Ejemplo 5.2.1.8.2

:::

knitr::include_graphics("images/19va_imagen.png")  
Grupos <- group_by(
  .data = penguins,
  species
  )
Grupos

summarise(
  .data = Grupos,
  Prom_lenght = round(mean(culmen_length_mm, na.rm = TRUE), digits = 2),
  Prom_depth = round(mean(culmen_depth_mm, na.rm = TRUE), digits = 2)
  )

:::caution La agrupación permite comparar rápidamente diferentes subconjuntos de los datos. La agrupación permite enmarcar la pregunta de análisis en términos de comparar grupos de observaciones, en lugar de observaciones individuales. Esta forma hace que sea más fácil hacer y responder preguntas complejas sobre los datos. :::

5.2.1.9 Resumir valores con summarise()/summarize() y across()

A menudo es útil realizar la misma operación en varias columnas. La función across() permite realizar esto.

:::example

Ejemplo 5.2.1.9.1

:::

knitr::include_graphics("images/20va_imagen.png")  
summarise(
  .data = penguins,
  across(
    .cols = c(culmen_length_mm, culmen_depth_mm, flipper_length_mm),
    .fns = mean,
    na.rm = TRUE
    )
  )

summarise(
  .data = penguins,
  across(
    .cols = ends_with('mm'),
    .fns = mean,
    na.rm = TRUE
    )
  )

summarise(
  .data = penguins,
  across(
    .cols = where(is.numeric),
    .fns = mean,
    na.rm = TRUE
    ),
  across(
    .cols = where(is.factor),
    .fns = nlevels
    )
  )

5.2.1.10 Realización de operaciones secuenciales con %>%

Un enfoque para realizar análisis de datos complejos consiste en crear objetos intermedios para usar en dicho análisis. Este es un flujo de trabajo muy común.

:::example

Ejemplo 5.2.1.10.1

:::

a1 <- rename(
  .data = penguins,
  gender = sex
  )
a2 <- filter(
  .data = a1,
  gender == 'FEMALE'
  )
a3 <- mutate(
  .data = a2,
  body_mass_kg = body_mass_g / 1000
)

mutate(
  filter(
    rename(
      .data = penguins,
      gender = sex
      ),
    gender == 'FEMALE'
    ),
  body_mass_kg = body_mass_g / 1000
  )

Para solucionar el problema que puede resultar del enfoque anterior, el paquete dplyr proporciona el operador de tubería (%>%). El %>% es un operador que permite encadenas funciones. Lo que hace es tomar la salida de una función y pasarla como entrada de la siguiente función.

:::example

Ejemplo 5.2.1.10.2

:::

knitr::include_graphics("images/21va_imagen.png")  
Peso_fem_kg <- penguins %>%
  rename(gender = sex) %>%
  filter(gender == 'FEMALE') %>%
  mutate(body_mass_kg = body_mass_g / 1000)
Peso_fem_kg

:::exercise

Ejercicio 5.2.1.10.1

Intente por favor calcular el valor medio de la longitud de la aleta (flipper_length_mm) de las tres especies de pinguinos del conjunto de datos penguins, pero que sean solo de sexo (sex) macho (MALE). :::


penguins %>%
  filter(sex == 'MALE') %>%
  summarise(
    across(
      .cols = flipper_length_mm,
      .fns = mean,
      na.rm = TRUE
      )
  )

5.2.1.11 Unir conjuntos de datos con _join()

A menudo los datos se pueden almacenar en múltiples conjuntos de datos. En algún momento se querrá acceder a la información de dichos conjuntos de datos, por lo cual necesitará una forma de poder combinarlos. A este proceso se le denomina unión (join) por la sencilla razón de que unira dichos conjuntos de datos.

El paquete dplyr cuenta con un conjunto de funciones de combinación para realizar este procedimiento.

Datos_a <- tribble(
  ~ID , ~x1,
  '1', 'a1',
  '2', 'a2'
  )

Datos_b <- tribble(
  ~ID , ~x2,
  '2', 'b1',
  '3', 'b2'
  )
save(Datos_a, file = 'data_pkg/Datos_a.rda', compress='xz')
save(Datos_b, file = 'data_pkg/Datos_b.rda', compress='xz')
wzxhzdk:339
wzxhzdk:340

Esta unión retorna todas las columnas del primer y segundo conjunto de datos, pero solo retorna las filas del primer conjunto de datos que coinciden con el segundo conjunto de datos.

:::example

Ejemplo 5.2.1.11.1

:::

knitr::include_graphics("images/22va_imagen.png")  
inner_join(
  x = Datos_a,
  y = Datos_b,
  by = 'ID'
  )

:::caution Generalmente, las uniones llevadas a cabo usando la función inner_join() no son apropiadas para su uso en el análisis de datos dado que es muy fácil perder observaciones. :::

Esta unión retorna todas las columnas del primer y segundo conjunto de datos, pero solo retorna las filas del conjunto de datos que se especifica en x como argumento.

:::example

Ejemplo 5.2.1.11.2

:::

knitr::include_graphics("images/23va_imagen.png")  
left_join(
  x = Datos_a,
  y = Datos_b,
  by = 'ID'
  )

Esta función es opuesta a la función left_join(), en el sentido de que solo retorna las filas del conjunto de datos que se especifica en y como argumento.

:::example

Ejemplo 5.2.1.11.3

:::

knitr::include_graphics("images/24va_imagen.png")  
right_join(
  x = Datos_a,
  y = Datos_b,
  by = 'ID'
  )

Esta unión retorna toda las columnas y filas de ambos conjunto de datos. De esta forma, retorna una fila para cualquier observación independiente si coinciden o no.

:::example

Ejemplo 5.2.1.11.4

:::

knitr::include_graphics("images/25va_imagen.png")  
full_join(
  x = Datos_a,
  y = Datos_b,
  by = 'ID'
  )

:::caution Si deseas ver un resumen de las posibilidades que ofrece el paquete dplyr y profundizar más en su uso, puedes mirar su hoja de trucos aquí. :::

6. Visualización de datos

6.1 ¿Que es la visualización de datos? Definición e importancia

La visualización de datos es la práctica de convertir datos en una representación gráfica. Para comprender cuán significativa es la visualización de los datos, un hecho simple es que a los cerebros humanos les resulta más difícil comprender datos complejos cuando están codificados en números y texto en comparación con los gráficos.

Es impensable cualquier sector profesional sin el uso de elementos de visualización, pues estos facilitan la transmisión de información. De las misma forma, es erróneo considerar a la visualización de datos como un recurso secundario o adicional, finalmente prescindible.

:::caution El concepto de visualización de datos no es nuevo. De hecho, la visualización de datos ha existido durante siglos. Aquí podrás encontrar una visualización que corresponde a una línea de tiempo sobre el desarrollo de los gráficos estadísticos. :::

### 6.2 Visualización de datos con `ggplot2` `ggplot2` es un paquete de `R` dedicado a la visualización de datos. Mediante su uso es posible construir casi cualquier tipo de gráfico. Si bien `ggplot2` es un paquete de `R` que permite producir gráficos estadísticos como muchos otros paquetes, es diferente a la mayoría de estos paquetes de gráficos debido a que tiene una gramárica subyacente profunda: la __gramática de los gráficos__.
wzxhzdk:349
Ilustracíon hecha por \@allison_horst.

6.2.1 Una gramática para la visualización de datos

Una gramática de gráficos es un marco que sigue un enfoque en capas para describir y construir visualizaciones o gráficos de manera estructurada.

knitr::include_graphics("images/26va_imagen.png")  

Para explicar el concepto de la gramática de gráficos en capas implementado en el paquete ggplot2 se empleara el conjunto de datos pokemon. Estos datos es uno de los muchos conjuntos de datos del proyecto Datos de miércoles cuyo propósito consiste en buscar que lo usuarios de R desarrollen habilidades de visualización y procesado de datos usando las herramientas del tidyverse.

knitr::include_graphics("images/27va_imagen.png")  

Como puedes ver, este conjunto de datos contiene siete variables:

  1. En id se describe la identificación de cada pokémon.
  2. En nombre podrá encontrar el nombre del pokémon.
  3. En tipo se describe el tipo de pokémon (eléctrico, agua, veneno, fantasma, hielo, psíquico, entre otros).
  4. En ataque podrá encontrar el daño que el pokémon puede causar en ataques.
  5. En defensa podrá encontrar la resistencia del pokémon al daño ante ataques.
  6. En velocidad se describe la velocidad del pokémon al atacar en cada ronda.
  7. En puntos_vida se describe la cantidad de daño que puede resistir cada pokémon.
pokemon <- readr::read_csv("https://raw.githubusercontent.com/cienciadedatos/datos-de-miercoles/master/datos/2019/2019-07-10/pokemon.csv")

pokemon <- pokemon %>%
  mutate(ataque = ataque + fuerza_especial_ataque, defensa = defensa + fuerza_especial_defensa) %>%
  select(ID_poke, nombre_ingles, tipo_1, ataque, defensa, velocidad, puntos_vida) %>%
  rename('id' = ID_poke, 'nombre' = nombre_ingles, 'tipo' = tipo_1) 
  #filter(nombre %in% c('Charmander', 'Bulbasaur', 'Butterfree', 'Pikachu', 'Squirtle'))

save(pokemon, file = 'data_pkg/pokemon.rda', compress='xz')

pokemon_2 <- pokemon %>%
  select(nombre_ingles, tipo_2) %>%
  rename('nombre' = nombre_ingles, 'tipo_2' = tipo_2)

save(pokemon_2, file = 'data_pkg/pokemon_2.rda', compress='xz')
6.2.1.1 Capas 1-2-3: datos-estética-geometrías

Estas son las capas que determinan la representación visual de los datos. Para asociar el gráfico a un conjunto de datos en específico se emplea el argumento data. Luego se define un mapeo haciendo uso de la función aes() dentro de ggplot() para seleccionar las variables a graficar. Por último, con geoms se indica cómo se representaran los datos en el gráfico.

:::example

Ejemplo 6.2.1.1.1

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total)
  ) +
  geom_point()

#ggplot(
  #data = pokemon,
  #aes(x = ataque, y = fuerza_total)
  #) +
  #geom_point(
    #size = 3.4,
    #colour = 'yellow',
    #fill = 'yellow',
    #alpha = 0.4,
    #shape = 'square filled'
    #)

Lista de colores en R y formas de puntos

knitr::include_graphics("images/colores_1.png")  
knitr::include_graphics("images/colores_2.png")  
knitr::include_graphics("images/colores_3.png")  
knitr::include_graphics("images/colores_4.png")  
knitr::include_graphics("images/colores_5.png")  
knitr::include_graphics("images/colores_6.png")  
knitr::include_graphics("images/colores_7.png")  
knitr::include_graphics("images/puntos.png")  

:::exercise

Ejercicio 6.2.1.1.1

En el ejemplo anterior se uso la función aes() para indicarle al geom_point() cuáles serían las posiciones x y y para cada punto. Sin embargo otra propiedad estetica que se puede modificar es el color de los puntos por medio del argumento colour. Intente por favor modificar el código anterior de modo que se coloreen los puntos de acuerdo al tipo de pokemon. :::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )
ggplot( # Primera forma
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point()

ggplot( # Segunda forma
  data = pokemon,
  aes(x = ataque, y = fuerza_total)
  ) +
  geom_point(aes(colour = tipo, fill = tipo))

:::caution Las asignaciones estéticas, definidas con la función aes(), describen cómo las variables se asignan a propiedades visuales o estéticas. Además de una posición horizontal (x) y vertical (y), cada punto puede tener también un tamaño (size), un color (colour y fill) y una forma (shape). :::

6.2.1.2 Capa 4: facetas

Las facetas son una de las capas más importantes para construir una visualización de datos efectiva. Las facetas consisten de múltiples gráficos de lado a lado utilizados para mostrar los niveles de una variable categórica. Hay dos tipos de facetado:

+ `facet_wrap()` wzxhzdk:364
+ `facet_grid()` wzxhzdk:365

:::example

Ejemplo 6.2.1.2.1

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point() #+
  #facet_wrap(~ tipo)

#ggplot(
  #data = pokemon,
  #aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  #) +
  #geom_point() +
  #facet_wrap(~ tipo, ncol = 3, dir = 'h') # "h" de horizontal.

#ggplot(
  #data = pokemon,
  #aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  #) +
  #geom_point() +
  #facet_wrap(~ tipo, nrow = 6, dir = 'v') # "v" de vertical.

:::example

Ejemplo 6.2.1.2.2

:::

pokemon_2 <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    ) %>%
  inner_join(pokemon_2, 'nombre')
pokemon_2

#ggplot(
  #data = pokemon_2,
  #aes(x = ataque, y = fuerza_total)
  #) +
  #geom_point() +
  #facet_grid(tipo ~ tipo_2)

#ggplot(
  #data = pokemon_2,
  #aes(x = ataque, y = fuerza_total)
  #) +
  #geom_point() +
  #facet_grid(vars(tipo), vars(tipo_2))
6.2.1.3 Capa 5: estadísticas

La capa 5 de estadísticas (stat_), permite presentar resumenes estadísticos dentro de los gráficos por medio de una transformación estadística de los datos.

:::example

Ejemplo 6.2.1.3.1

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

Modelo <- lm(
  formula = fuerza_total ~ ataque, 
  data = pokemon
  )
summary(Modelo)

Int_Pend <- tribble(
  ~Intercepto, ~Pendiente,
       138.24,       1.96
  )

#ggplot(
  #data = pokemon,
  #aes(x = ataque, y = fuerza_total)
  #) +
  #geom_point() +
  #geom_abline(
    #data = Int_Pend, 
    #aes(intercept = Intercepto, slope = Pendiente)
    #)

#ggplot(
  #data = pokemon,
  #aes(x = ataque, y = fuerza_total)
  #) +
  #geom_point() +
  #stat_smooth(method = 'lm', colour = 'black')

#ggplot(
  #data = pokemon,
  #aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  #) +
  #geom_point() +
  #facet_wrap(~ tipo) +
  #stat_smooth(method = 'lm', colour = 'black')

:::caution Además de la función geom_point(), existen otras funciones geom_ que permiten indicar la forma en como se desea representar los datos en la visualización. Cada una de estas funciones pueden estar asociadas con una función stat_ que se encargan de realizar los cálculos para obtener los parámetros necesarios para realizar una determinada gráfica. Aquí podrás encontrar una lista de estas funciones. :::

:::example

Ejemplo 6.2.1.3.2

:::

wzxhzdk:369
wzxhzdk:370
6.2.1.4 Capa 6: coordenadas

Un sistema de coordenadas asigna la posición de los objetos geométricos en la superficie de una gráfica. Con el paquete ggplot2, dicha posición se especifica mediante dos coordenadas (x y y).

:::example

Ejemplo 6.2.1.4.1

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total)
  ) #+
  #geom_point() +
  #coord_polar()

Existen dos tipos de sistema de coordenadas comúnenmente usados en el paquete ggplot2:

+ Sistema de coordenadas cartesianas wzxhzdk:372
+ Sistema de coordenadas polar wzxhzdk:373

:::example

Ejemplo 6.2.1.4.2

:::

knitr::include_graphics("images/Astronauts_tidytuesday.png")
Ilustracíon hecha por [\@Z3tt](https://github.com/Z3tt/TidyTuesday).
6.2.1.5 Capa 7: tema

La capa 7 de tema, ayuda a que las gráficas sean estéticamente agradables o coincidan con una guía de estilo existente, sin afectar lo ya hecho a partir de las capas anteriormente presentadas.

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point(alpha = 0.4) +
  facet_wrap(~ tipo, ncol = 6) +
  stat_smooth(method = 'lm', colour = 'black') #+
  #labs(
    #x = 'Ataque',
    #y = 'Fuerza total',
    #title = 'Relación entre ataque y fuerza total de acuerdo al tipo de pokemon'
  #) +
  #scale_colour_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  #scale_fill_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4'))

El paquete ggplot2 viene con una serie de temas integrados. Estos son:

knitr::include_graphics("images/32va_imagen.png")  

:::example

Ejemplo 6.2.1.5.1

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point(alpha = 0.4) +
  facet_wrap(~ tipo, ncol = 6) +
  stat_smooth(method = 'lm', colour = 'black') +
  labs(
    x = 'Ataque',
    y = 'Fuerza total',
    title = 'Relación entre ataque y fuerza total de acuerdo al tipo de pokemon'
  ) +
  scale_colour_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  scale_fill_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  theme_bw()

:::caution ggplot2 no está limitado a los temas que tiene integrados. Puede hacer uso de otros paquetes, como ggthemes de Jeffrey Arnold, para disponer de muchos más temas. Aquí podrá encontrar información sobre este paquete. :::

Los temas integrados anteriores cuentan con componentes individuales que a su vez pueden ser modificados usando la función theme(). Una manera general del código que se puede emplear para esto es de la forma: plot + theme(element.name = element_function()).

Estos pueden afectar la apariencia del gráfico en su conjunto. Estos son:

knitr::include_graphics("images/33va_imagen.png")  

:::example

Ejemplo 6.2.1.5.2

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point(alpha = 0.4) +
  facet_wrap(~ tipo, ncol = 6) +
  stat_smooth(method = 'lm', colour = 'black') +
  labs(
    x = 'Ataque',
    y = 'Fuerza total',
    title = 'Relación entre ataque y fuerza total de acuerdo al tipo de pokemon'
  ) +
  scale_colour_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  scale_fill_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  theme_bw() +
  theme(
    plot.background = element_rect(colour = 'gray34', fill = 'white', size = 1.4),
    plot.title = element_text(size = 10, colour = 'black', face = 'bold')
  )

Estos pueden controlar la apariencia de los ejes. Estos son:

knitr::include_graphics("images/34va_imagen.png")  

:::example

Ejemplo 6.2.1.5.3

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point(alpha = 0.4) +
  facet_wrap(~ tipo, ncol = 6) +
  stat_smooth(method = 'lm', colour = 'black') +
  labs(
    x = 'Ataque',
    y = 'Fuerza total',
    title = 'Relación entre ataque y fuerza total de acuerdo al tipo de pokemon'
  ) +
  scale_colour_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  scale_fill_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  theme_bw() +
  theme(
    plot.background = element_rect(colour = 'gray34', fill = 'white', size = 1.4),
    plot.title = element_text(size = 10, colour = 'black', face = 'bold'),
    axis.text.x = element_text(size = 10, face = 'bold', angle = -45, hjust = 0, vjust = 1),
    axis.text.y = element_text(size = 10, face = 'bold'),
    axis.title = element_text(size = 12, face = 'bold')
  )

Estos pueden controlar la apariencia de todas las leyendas. Estos son:

knitr::include_graphics("images/35va_imagen.png")  

:::example

Ejemplo 6.2.1.5.4

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point(alpha = 0.4) +
  facet_wrap(~ tipo, ncol = 6) +
  stat_smooth(method = 'lm', colour = 'black') +
  labs(
    x = 'Ataque',
    y = 'Fuerza total',
    title = 'Relación entre ataque y fuerza total de acuerdo al tipo de pokemon'
  ) +
  scale_colour_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  scale_fill_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  theme_bw() +
  theme(
    plot.background = element_rect(colour = 'gray34', fill = 'white', size = 1.4),
    plot.title = element_text(size = 10, colour = 'black', face = 'bold'),
    axis.text.x = element_text(size = 10, face = 'bold', angle = -45, hjust = 0, vjust = 1),
    axis.text.y = element_text(size = 10, face = 'bold'),
    axis.title = element_text(size = 12, face = 'bold'),
    legend.position = 'none'
  )

Estos pueden controlar la apariencia de los gráficos facetados. Estos son:

knitr::include_graphics("images/36va_imagen.png")  

:::example

Ejemplo 6.2.1.5.5

:::

pokemon <- pokemon %>%
  mutate(
    fuerza_total = ataque + defensa + velocidad + puntos_vida
    )

ggplot(
  data = pokemon,
  aes(x = ataque, y = fuerza_total, colour = tipo, fill = tipo)
  ) +
  geom_point(alpha = 0.4) +
  facet_wrap(~ tipo, ncol = 6) +
  stat_smooth(method = 'lm', colour = 'black') +
  labs(
    x = 'Ataque',
    y = 'Fuerza total',
    title = 'Relación entre ataque y fuerza total de acuerdo al tipo de pokemon'
  ) +
  scale_colour_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  scale_fill_manual(values = c('cyan', 'gray34', 'deeppink', 'orangered', 'yellow', 'turquoise', 'springgreen', 'red', 'black', 'darkmagenta', 'green', 'blue', 'maroon', 'violetred', 'chocolate', 'coral4', 'thistle', 'pink4')) +
  theme_bw() +
  theme(
    plot.background = element_rect(colour = 'gray34', fill = 'white', size = 1.4),
    plot.title = element_text(size = 10, colour = 'black', face = 'bold'),
    axis.text.x = element_text(size = 10, face = 'bold', angle = -45, hjust = 0, vjust = 1),
    axis.text.y = element_text(size = 10, face = 'bold'),
    axis.title = element_text(size = 12, face = 'bold'),
    legend.position = 'none',
    strip.background = element_rect(colour = 'black', fill = 'gray64'),
    strip.text = element_text(size = 10, colour = 'black', face = 'bold')
  )

:::caution Si deseas conocer más sobre la gramática de gráficos en capas propuesto por Hadley Wickham, te invitamos a revisar su articulo titulado A layered grammar of graphics que cubre en detalle su propuesta en la construcción de gráficos y también habla sobre el paquete ggplot2. :::

6.3 Gráficos estadísticos con ggplot2

Ya vimos como funciona la gramática de gráficas y las distintas capas que ponemos agregar y utilizar para crearlos, es hora de que pongamos en practica estos conceptos. Iniciemos viendo las capas individualmente partiendo de un esquema que no posee datos, para ello debemos cargar el paquete ggplot2 y ejecutar la siguiente linea de código.

:::example

Ejemplo 6.3.1

:::

# Cargando paquete
library(ggplot2)
# Esquema base
ggplot()

Generalmente en la función ggplot() asignamos los datos que vamos a graficar usando el argumento data. Supongamos que tenemos un set de datos llamado "mis_datos", en este caso la función del esquema base con la capa de los datos quedaría de la forma ggplot(data=mis_datos).

Continuemos manejando el esquema de un gráfico sin usar datos. El siguiente paso es agregar la capa de estética, la cual puede añadirse directamente en la función ggplot() con el argumento mapping = aes() o de manera independiente con la función aes(). Para el segundo caso debemos agregar una capa usando el operador +. Veamos los dos casos estableciendo valores arbitrarios tanto para x como para y.

# Cargando paquete
library(ggplot2)
# Esquema base con mapping
ggplot(mapping = aes(x=0,y=0))
# Esquema base con aes()
ggplot()+
  aes(x=0,y=0)

Después de estas dos capas se agrega la capa de geometrías, que en general depende de el tipo de gráfico que necesitemos o el que mejor se adapte a nuestros datos. Para conocer algunos gráficos agruparemos algunos de ellos en 7 conjuntos: 1) Gráficos de distribución, 2) Gráficos de correlación, 3) Gráficos de clasificación y 4) Mapas.

6.3.1 Gráficos de distribución

Estos gráficos se utilizan generalmente en variables continuas como edad, altura, masa, etc. Para iniciar esta sección de gráficos haremos el gráfico de densidad para la altura de los personajes de Starwars.

Starwars

:::example

Ejemplo 6.3.1.1

:::

ggplot(data = Starwars)+ # Datos
  aes(x=height)+ # Estética
  geom_density() # Geometría 

Sabemos que los personajes de Starwars no son todos del mismo genero, entonces modifiquemos el gráfico anterior utilizando facetas para distinguir por genero usando facet_wrap() y eliminado los personajes que no tiene genero definido. Ademas usemos el operador tubería %>% para realizar una operación secuencial que una directamente nuestros datos con el gráfico.

Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=height)+ # Estética
  geom_density()+ # Geometría
  facet_wrap(~gender) #Faceta

Otra forma de diferenciar por género sin usar las facetas, es otorgar color a la variable distintiva, esto podemos hacerlo en la capa estética del gráfico agregando para este caso “color= gender”. Veamos cómo quedaría el gráfico.

Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=height,color=gender)+ # Estética con color
  geom_density() # Geometría

Ahora hagamos modificaciones de imagen para personalizar y embellecer los dos gráficos anteriores, para ello utilizaremos argumentos como “alpha” y "fill" agregando ademas la capa de tema. También nos apoyaremos en la libreríapatchwork para fusionar los dos gráficos en uno solo.

#libreria
library(patchwork)
base <- Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=height,color=gender,fill=gender)+ # Estética con color y relleno
  geom_density(alpha=.3) # Geometría

(base +theme_classic()) / # base con tema clásico
  (base +theme_dark()+facet_wrap(~gender)) #base con faceta y tema oscuro 

Sigamos usando la misma variable “height” de Starwars para aprender cómo se hacen los gráficos de histograma, en este caso reuniremos todos los tipos de gráficos que hicimos en el ejemplo anterior en uno solo. Fijate muy bien en la sintaxis del código y todos los comentarios en él. El argumento “alpha” hace referencia a transparencia y el argumento “fill” a relleno.

:::example

Ejemplo 6.3.1.2

:::

# Gráfico base
base <- Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=height)+ # Estética básica 
  geom_histogram(alpha=.5) # Geometría
# opciones sobre la base 
base /
  (base +aes(fill=gender)+theme_classic()) / #base + estética con color +  tema clásico 
  (base +aes(fill=gender)+theme_dark()+facet_wrap(~gender)) #base + estética con color + faceta + tema clasico 

Veamos otro tipo de gráfico que es conocido como Boxplot, para estos gráficos seguiremos usando los mismos datos que en los anteriores ejemplo, agregando las capas de estadísticas y coordenadas. Veamos un ejemplo para la variable “height” para cada genero.

:::example

Ejemplo 6.3.1.3

:::

# Gráfico base
Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=gender,y=height)+ # Estética básica 
  geom_boxplot(fill=NA)+ # geometría sin relleno
  stat_summary(fun=mean,geom="point", color="red") + # Estadísticas con geometría
  coord_flip() # Coordenadas invertidas

El siguiente gráfico es conocido como el gráfico de violín y lo implementaremos igual que el boxplot salvo por la geometría.

:::example

Ejemplo 6.3.1.4

:::

# Gráfico base
Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=gender,y=height,fill=gender)+ # Estética básica con relleno
  geom_violin(alpha=.3)+ # geometría con transparencia
  stat_summary(fun=mean,geom="point", color="red") + # Estadísticas con geometría
  coord_flip() # Coordenadas invertidas

Hasta el momento hemos visto gráficas con una sola geometría, ahora veamos gráficas con varias geometrías que incluya las dos gráficas anteriores y una tercera geometría conocida como geom_jitter().

:::example

Ejemplo 6.3.1.5

:::

# Gráfico base
Starwars %>% 
  filter(!is.na(gender)) %>% 
  ggplot(data=.)+ # Datos (.)
  aes(x=gender,y=height,fill=gender,color=gender)+ # Estética color y  con relleno
  geom_boxplot(alpha=.2)+ # geometría boxplot con transparencia
  geom_violin(alpha=.3)+ # geometría violín con transparencia
  geom_jitter()+
  stat_summary(fun=mean,geom="point",size=6,shape=23,color="black") + # Estadísticas con geometría puntos
  coord_flip() + # Coordenadas invertidas
  theme_light() #tema

6.3.2 Gráficos de relación

Los siguientes gráficos corresponde a ilustraciones que muestran la relación entre dos o más variables continuas. Veamos el primero de ellos el cual se denomina gráfico de dispersión entre dos variables. Usaremos los datos de masa y altura de Starwars para relacionarlas en este gráfico.

:::example

Ejemplo 6.3.2.1

:::

Starwars %>% 
  filter(!is.na(gender)) %>% 
  filter(mass<quantile(Starwars$mass,probs = .99,na.rm = T)) %>% 
  ggplot(data = .)+ #Datos
  aes(x=height,y=mass,color=gender)+ #estética
  geom_point() # geometría

Usando las mismas variables del ejemplo anterior veamos un gráfico de densidad de dos dimensiones. En general este gráfico es útil cuando nuestras variables a relacionar tiene una gran cantidad de observaciones que son difíciles de procesar por un geom_point().

:::example

Ejemplo 6.3.2.2

:::

Starwars %>% 
  filter(!is.na(gender)) %>% 
  filter(mass<quantile(Starwars$mass,probs = .99,na.rm = T)) %>% 
  ggplot(data = .)+ #Datos
  aes(x=height,y=mass)+ # Estética
  stat_density_2d(aes(fill = ..density..), geom = "raster", contour = FALSE) #Estadística con geometría

Otro tipo de gráfico que podemos realizar es el que relaciona el tiempo con una variable continua, estas son las denominadas series de tiempo. Para el siguiente ejemplo tomaremos la variable de precio del petróleo de el dataset Seatbelts, el cual corresponde a una serie temporal que muestra los totales mensuales de conductores de automóviles en Gran Bretaña que murieron o resultaron gravemente heridos desde enero de 1969 hasta diciembre de 1984. Para mayor información del dataset ejecuta ?Seatbelts

:::example

Ejemplo 6.3.2.3

:::

as.matrix(Seatbelts) %>% 
  data.frame()  %>% 
  mutate(date=seq(as.Date("1969-01-01"),as.Date("1984-12-01"), by="month")) %>% 
  ggplot(data=.)+ #Datos
  aes(x=date,y=PetrolPrice)+ #Estética
  geom_line()+ # Geometría de lineas
  geom_point()+ # Geometría de puntos 
  scale_x_date(date_breaks = "12 month",date_labels = "%y-%m")+ #Coordenadas 
  theme_light() # Tema

Ya vimos cómo relacionar gráficamente dos variables continuas y una variable continua con el tiempo, veamos ahora cómo relacionar más de dos variables continuas en un gráfico. Para ello nos valdremos de la matriz de correlación de las 4 variables numéricas del dataset iris. Consulta la información del dataset con ?iris para obtener más información.

:::example

Ejemplo 6.3.2.4

:::

iris %>% 
  select_if(is.numeric) %>%
  cor(method = "pearson") %>% 
  as_tibble() %>%
  mutate(name=colnames(iris)[1:4]) %>% 
  relocate(name) %>% 
  pivot_longer(cols = 2:5,names_to="name_2",values_to="Corr") %>% 
  ggplot(data=.)+ #Datos
  aes(x=name,y=name_2,fill=Corr)+ # Estética
  geom_tile()+ # Geometría
  scale_fill_gradientn(colours = topo.colors(5),limits=c(-1,1),breaks=seq(-1,1,.2)) #Coordenadas

6.3.3 Gráficos de clasificación

Los gráficos de clasificación corresponden generalmente a conteos o resúmenes numéricos de variables categóricas. Como primer ejemplo veamos un gráfico de barras de el número de autos que tienen 4, 6 u 8 cilindros en el dataset mtcars. Para este caso la función geom_bar() hace automáticamente el conteo de los autos, width corresponde al grosor de las barras.

:::example

Ejemplo 6.3.3.1

:::

mtcars %>% 
  ggplot(data = .)+ #Datos
  aes(x=factor(cyl),fill=factor(cyl))+ #Estética
  geom_bar(width = .5) + #Geometría
  theme_linedraw() #tema

En el casos de que nuestros datos tengan el conteo o variable de interés de nuestra variable categórica, podemos usar el argumento stat=”identity” en geom_bar() para que este no sea calculado por la función, en cuyo caso debemos asignar adicionalmente el conteo o variable y.

mtcars %>% 
  group_by(cyl) %>% 
  summarise(n=n()) %>% 
  ggplot(data = .)+ #Datos
  aes(x=factor(cyl),fill=factor(cyl),y=n)+ # Estética
  geom_bar(stat ="identity", width = .5)+ # Geometría
  coord_flip() # Coordenadas invertidas

Otra forma de generar un gráfico de barras es combinar dos geometrías y crear el gráfico denominado “lollipop”, el cual es una visualización un poco más llamativa que cumple el mismo fin con algunos toques estéticos.

:::example

Ejemplo 6.3.3.2

:::

data.frame( x=letters,
            y=sample(1:60,26,replace = T)) %>% 
  ggplot(data=.)+ #Datos
  aes(x=x,y=y)+ #Estética base
  geom_point(color="blue", size=4,alpha=0.5)+ #Geometría de puntos
  geom_segment(aes(x=x, xend=x, y=0, yend=y),color="skyblue")+ #Geometría de segmentos con estética adicional
  theme_classic() # Tema

Retomemos los datos del número de cilindros de mtcars con su respectivo conteo y veamos un gráfico de barras modificado utilizando coord_polar(). Recuerda consultar la documentación de esta función para tener mayor claridad respecto al gráfico.

:::example

Ejemplo 6.3.3.3

:::

mtcars %>% 
  group_by(cyl) %>% 
  summarise(n=n()) %>% 
  ggplot(data=.)+ #Datos 
  aes(x=factor(cyl),y=n, fill=factor(cyl))+ # Estética
  geom_bar(stat = "identity")+ #Geometría
  scale_y_continuous(breaks = seq(0,16,2))+ #coordenadas
  coord_polar(theta="y") #Coordenadas

Otro gráfico que se puede realizar con coord_polar() usando los datos del número de cilindros de mtcars es el siguiente.

:::example

Ejemplo 6.3.3.4

:::

mtcars %>% 
  ggplot(data=.) + #Datos
  aes(x = factor(1), fill = factor(cyl))+ #Estética
  geom_bar(width = 1, color="white")+ # Geometría
  coord_polar(theta = "y",start = pi / 3)+ #Coordenadas
  scale_fill_brewer(palette = 7)+  #Coordenadas
  theme_void() #Tema

Otra forma de representar los datos del número de cilindros de mtcars con su respectivo conteo es con el gráfico de rosca, el cual combina dos geometrías y aplicar retoques estéticos para lograr una visualización más llamativa. Para este caso se necesita manipular los datos de una forma específica que veremos a continuación.

:::example

Ejemplo 6.3.3.5

:::

mtcars %>% 
  group_by(cyl) %>% 
  summarise(n=n()) %>% 
  mutate(frac=n/sum(n),
         ymax=cumsum(frac),
         ymin=c(0,head(ymax,-1)),
         pos=(ymax+ymin)/2,
         label=paste0(cyl,"\n valor:",n)) %>% 
  ggplot(data=.)+ #Datos
  aes(ymax=ymax, ymin=ymin, xmax=4, xmin=3, fill=factor(cyl))+ #Estética
  geom_rect() + #Geometría 1
  geom_text( x=2.5, aes(y=pos, label=label, color=factor(cyl)), size=4)+ #Geometría 2
  xlim(c(1, 4))+ # Coordenadas limites eje x
  coord_polar(theta="y")+ #Coordenadas
  scale_fill_viridis_d() + # Coordenadas escala de relleno
  scale_color_viridis_d() + # Coordenadas escala de color
  theme_void() #Tema

Como último gráfico de esta sección tenemos las barras apiladas, que pueden ser útiles para ver conteos en simultáneo de dos variables categóricas. Para este caso representamos el conteo de cilindros y carburadores de los automóviles de mtcars.

:::example

Ejemplo 6.3.3.6

:::

mtcars %>% 
  ggplot(data=.)+ #Datos
  aes(x=factor(cyl),fill=factor(carb))+ #Estética
  geom_bar()+ #Geometría
  scale_fill_viridis_d(option = "plasma") #Coordenadas

6.3.4 Mapas

La cuestión con los mapas es un poco más compleja y para graficarlos tenemos múltiples geometrías que nos pueden ayudar como geom_map(), geom_sf(), geom_polygon(), etc. Sin embargo el uso de cada una de ellas depende exclusivamente del formato de los datos y para ello debemos relacionarnos más con análisis geoespacial. Para no adentrarnos y extendernos en los formatos de datos espaciales presentaremos dos breves ejemplos nombrando paquetes útiles para graficar mapas.

El primer ejemplo consiste en graficar el número de asesinatos por cada 100.000 personas en los distintos estados de norte america. Para graficar este mapa usamos los datos USArrests, te invito a que consultes su estructura y lo explores detalladamente.

:::example

Ejemplo 6.3.4.1

:::

USArrests %>% 
  ggplot(data=.)+#Datos
  aes(x=long,y=lat,group=group,order=order,fill=Murder)+ #Estética
  geom_polygon(color="white")+ # Geometría
  scale_fill_distiller(palette = 4)+ #Coordenadas
  labs(title = "Asesinatos USA - 1973")+ # Etiquetas
  theme_void() #Tema

El segundo ejemplo es un gráfico de homicidios en Colombia por departamentos para el año 2013.

:::example

Ejemplo 6.3.4.2

:::

departamentos %>% 
  ggplot(data=.)+ #Datos
  geom_polygon(aes(x=long,y=lat,group=group,order=order,fill=homicidios))+ #Geometría de forma
  geom_text(data = . %>% distinct(depto,lab_long,lab_lat), 
            aes(x=lab_long,y=lab_lat, label=depto),size=1.5)+ #Geometría de texto
  scale_fill_viridis_c()+ #Coordenadas escala de color
  labs(title = "Homicidios Colombia - 2013")+ # Etiquetas
  theme_void() #Tema

Otros paquetes paquetes que podemos utilizar para graficar mapas y datos geoespaciales son tmap , leaflet , ggmap y algunas de sus dependencias, aunque estas tres son las más relacionadas con el tidyverse.

6.4 Animando sus visualizaciones de datos

Ya en este punto de seguro se preguntará ¿es posible ir más alla de lo que se ha visto hasta ahora? La respuesta es definitivamente un si. Un ejemplo de ello es la famosa visualización de Hans Rosling donde representaba la población mundial, la expectativa de vida y varios indicadores económicos mediante una visualización animada.

Afortunadamente, hacer este tipo de visualizaciones se ha vuelto hoy en día bastante simple mediante el uso de paquetes de R.

#### 6.4.1 Construcción de visualizaciones animadas mediante el uso del paquete `gganimate` El paquete `gganimate` extiende la gramática de gráficos implementada en el paquete `ggplot2` proporcionando una rica gramática o funciones de animación. Esta nueva gama de funciones se pueden agregar al código de graficación de la misma manera que se agrega un `geom_` u otra capa gráfica.
wzxhzdk:410
Ilustracíon hecha por \@allison_horst.

Al crear gráficas animadas, el gráfico no se mueve sino que en realidad son muchos gráficos individuales que luego se unen como si tratase de cuadros de película:

knitr::include_graphics("images/37va_imagen.png")  

:::example

Ejemplo 6.4.1.1

:::

library(ggrepel)

gapminder_2 <- gapminder %>%
  filter(country == 'China')

gapminder %>%
  rename(
    'Continente' = continent
    ) %>%
  mutate(
    Continente = recode(
      .x = Continente,
      'Africa' = 'África', 
      'Americas' = 'América', 
      'Asia' = 'Asia', 
      'Europe' = 'Europa', 
      'Oceania' = 'Oceanía'
      )
    ) %>%
  ggplot(
    data = .,
    aes(x = gdpPercap, y = lifeExp)
    ) +
  geom_point(aes(size = pop, colour = Continente, fill = Continente), alpha = 0.4) +
  facet_wrap(~ year) +
  scale_colour_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_fill_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_size_area(guide = FALSE, max_size = 14) +
  scale_x_log10() +
  labs(x = 'PIB per cápita', y = 'Expectativa de vida al nacer') +
  theme_bw() +
  theme(
    axis.text = element_text(size = 12, face = 'bold'),
    axis.title = element_text(size = 14, face = 'bold'),
    plot.title = element_text(size = 14, face = 'bold'),
    legend.position = 'top',
    legend.title = element_text(size = 14, face = 'bold'),
    legend.text = element_text(size = 12),
    strip.text = element_text(size = 12, face = 'bold'),
    strip.background = element_rect(colour = 'black', fill = 'gray64')
    ) +
  geom_text_repel(
    data = gapminder_2,
    aes(label = country),
    size = 5,
    box.padding = unit(2.8, 'lines'),
    point.padding = unit(0.8, 'lines'),
    segment.color = 'black'
    )
gapminder::gapminder
knitr::include_graphics("images/ejemplo_gapminder.png")  
knitr::include_graphics("images/ejemplo_gapminder_animado.gif")

Para usar el paquete gganimate se debe instalar primero. Para esto, el paquete se pueden instalar de dos formas:

#install.packages('gganimate')
#devtools::install_github('thomasp85/gganimate')

y luego se debe cargar una vez se necesite:

#library(gganimate)

6.4.1.1 Definición de la animación: transition_*()

Las funciones transition_*() son un conjunto de funciones encargadas de interpretar los datos y distribuirlos en subconjuntos de datos de acuerdo a una de sus variables, la variable de transición.

:::example

Ejemplo 6.4.1.1.1

:::

gapminder %>%
  rename(
    'Continente' = continent
    ) %>%
  mutate(
    Continente = recode(
      .x = Continente,
      'Africa' = 'África', 
      'Americas' = 'América', 
      'Asia' = 'Asia', 
      'Europe' = 'Europa', 
      'Oceania' = 'Oceanía'
      )
    ) %>%
  ggplot(
    data = .,
    aes(x = gdpPercap, y = lifeExp)
    ) +
  geom_point(aes(colour = Continente, fill = Continente, size = pop), alpha = 0.4) +
  scale_colour_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_fill_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_size_area(max_size = 14, guide = FALSE) +
  scale_x_log10() +
  labs(x = 'PIB per cápita', y = 'Expectativa de vida al nacer') +
  theme_bw() +
  theme(
    axis.text = element_text(size = 12, face = 'bold'),
    axis.title = element_text(size = 14, face = 'bold'),
    legend.position = c(0.8, 0.2),
    legend.title = element_text(size = 14, face = 'bold'),
    legend.text = element_text(size = 12)
    ) #+
  #transition_time(year)
knitr::include_graphics("images/1ra_animacion.gif")

:::caution Además de la función transition_time(), existen otras funciones transition_ que permiten realizar ditintos gráficos animados. Aquí podrás encontrar una lista de estas funciones y de otras funciones implementadas en el paquete gganimate. :::

6.4.1.2 Crear estelas de sombra: shadow_*()

Con el conjunto de funciones shadow_*() se pueden aplicar sombras que muestran los puntos de datos anteriores. Estas funciones son shadow_wake(), shadow_trail() y shadow_mark().

:::example

Ejemplo 6.4.1.2.1

:::

gapminder %>%
  rename(
    'Continente' = continent
    ) %>%
  mutate(
    Continente = recode(
      .x = Continente,
      'Africa' = 'África', 
      'Americas' = 'América', 
      'Asia' = 'Asia', 
      'Europe' = 'Europa', 
      'Oceania' = 'Oceanía'
      )
    ) %>%
  ggplot(
    data = .,
    aes(x = gdpPercap, y = lifeExp)
    ) +
  geom_point(aes(colour = Continente, fill = Continente, size = pop), alpha = 0.4) +
  scale_colour_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_fill_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_size_area(max_size = 14, guide = FALSE) +
  scale_x_log10() +
  labs(x = 'PIB per cápita', y = 'Expectativa de vida al nacer') +
  theme_bw() +
  theme(
    axis.text = element_text(size = 12, face = 'bold'),
    axis.title = element_text(size = 14, face = 'bold'),
    legend.position = c(0.8, 0.2),
    legend.title = element_text(size = 14, face = 'bold'),
    legend.text = element_text(size = 12)
    )  #+
  #transition_time(year) +
  #shadow_trail()
knitr::include_graphics("images/2da_animacion.gif")

6.4.1.3 Etiquetado dinámico: {frame_time}

El paquete gganimate proporciona un etiquetado dinámico ({frame_time}) que facilita comprender a que transición se refiere cada punto de dato presentado en la gráfica.

:::example

Ejemplo 6.4.1.3.1

:::

gapminder %>%
  rename(
    'Continente' = continent
    ) %>%
  mutate(
    Continente = recode(
      .x = Continente,
      'Africa' = 'África', 
      'Americas' = 'América', 
      'Asia' = 'Asia', 
      'Europe' = 'Europa', 
      'Oceania' = 'Oceanía'
      )
    ) %>%
  ggplot(
    data = .,
    aes(x = gdpPercap, y = lifeExp)
    ) +
  geom_point(aes(colour = Continente, fill = Continente, size = pop), alpha = 0.4) +
  scale_colour_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_fill_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_size_area(max_size = 14, guide = FALSE) +
  scale_x_log10() +
  labs(x = 'PIB per cápita', y = 'Expectativa de vida al nacer', title = 'Año: {frame_time}') +
  theme_bw() +
  theme(
    axis.text = element_text(size = 12, face = 'bold'),
    axis.title = element_text(size = 14, face = 'bold'),
    legend.position = c(0.8, 0.2),
    legend.title = element_text(size = 14, face = 'bold'),
    legend.text = element_text(size = 12),
    plot.title = element_text(size = 14, face = 'bold')
    ) #+
  #transition_time(year) +
  #shadow_trail()
knitr::include_graphics("images/3ra_animacion.gif")

6.4.1.4 Guardar como un GIF

El paquete gganimate permite también guardar los gráficos animados como un GIF.

:::example

Ejemplo 6.4.1.4.1

:::

Graf_animado <- gapminder %>%
  rename(
    'Continente' = continent
    ) %>%
  mutate(
    Continente = recode(
      .x = Continente,
      'Africa' = 'África', 
      'Americas' = 'América', 
      'Asia' = 'Asia', 
      'Europe' = 'Europa', 
      'Oceania' = 'Oceanía'
      )
    ) %>%
  ggplot(
    data = .,
    aes(x = gdpPercap, y = lifeExp)
    ) +
  geom_point(aes(colour = Continente, fill = Continente, size = pop), alpha = 0.4) +
  scale_colour_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_fill_manual(values = c('yellow', 'red', 'black', 'cyan', 'green')) +
  scale_size_area(max_size = 14, guide = FALSE) +
  scale_x_log10() +
  labs(x = 'PIB per cápita', y = 'Expectativa de vida al nacer', title = 'Año: {frame_time}') +
  theme_bw() +
  theme(
    axis.text = element_text(size = 12, face = 'bold'),
    axis.title = element_text(size = 14, face = 'bold'),
    legend.position = c(0.8, 0.2),
    legend.title = element_text(size = 14, face = 'bold'),
    legend.text = element_text(size = 12),
    plot.title = element_text(size = 14, face = 'bold')
    ) #+
  #transition_time(year) +
  #shadow_trail()

#animate(Graf_animado, fps = 10, width = 950, height = 650)
#anim_save('Graf_animado.gif')

:::caution Si deseas ver un resumen de las posibilidades que ofrece el paquete ggplot2 y profundizar más en su uso, puedes mirar su hoja de trucos aquí. De igual forma, si deseas conocer más sobre el paquete gganimate puedes mirar su hoja de trucos aquí. :::

7. Modelación estadística básica

8. Comunicación de resultados

La comunicación de resultados es un aspecto muy importante en cualquier campo de la ciencia y en el análisis estadístico no es la excepción. A lo largo de los años se usado el enfoque manual (cortar y pegar) para comunicar resultados en forma de informes, artículos científicos, presentaciones académicas, libros, sitios web, etc. Sin embargo este enfoque manual se a visto reemplazado por la automatización permitiendo emitir resultados reproducibles y dinámicos con mayor simplicidad. En R y en muchos otros lenguajes de programación el formato de documento Markdown abrió las puertas a la interacción entre código de programación y texto plano, dando como resultado documentos que integran sus características y emiten distintos formatos de acuerdo a las necesidades del usuario. Markdown en conjunto con Pandoc permiten compilar formatos como PDF, HTML, Word, LaTex, PowerPoint, Beamer, etc. con una sintaxis bastante simple, dando como resultado documentos básicos susceptibles de personalización con algunos conocimientos de CSS y LaTex.

Aunque la sintaxis de Markdown es simple y admite muchos lenguajes incluyendo todos los inherentes a los formatos de salida, en R existe su propia versión en el paquete rmarkdown creado a principios de 2014. Esta versión en conjunto con knitr se han convertido en una de las herramientas más conocidas, divulgadas y avaluadas en toda la comunidad de programadores de R, por lo que conocerla y manejarla resulta apropiado para los usuarios de R.

Como primer paso para el uso de rmarkdown es necesario instalarlo:

#install.packages("rmarkdown",dependencies = T)

:::caution Si deseas ver un resumen de las posibilidades que ofrece el paquete rmarkdown puedes mirar su hoja de trucos aquí. :::

wzxhzdk:427
Ilustracíon hecha por \@allison_horst.
wzxhzdk:428

No obstante si cuenta con la IDE de Rstudio no es explícitamente necesario que lo instale ya que el lo hace por usted. Tenga en cuenta que rmarkdown maneja la sintaxis de Markdown, para familiarizarse con ella puede tomar el Tutorial de Rmarkdown.

Como primer paso para aprender sobre las opciones de rmarkdown debemos conocer las plantillas que posee abriendo Rstudio y creando un nuevo Rmarkdown de la siguiente forma:

En la siguiente imagen se ilustra el panel de opciones de Rmarkdown mostrando los formatos de salida que podemos usar para generar documentos ya sean HTML,PDF, Word, presentaciones, Shiny y otras plantillas.

Generar cualquier tipo de documento, presentación, Shiny o plantilla es tan simple como seleccionar la opción que prefiera y dar click en OK. Después de pulsar OK se abrirá un archivo Rmd con instrucciones básicas correspondientes al formato seleccionado, para crear el archivo con el formato de interés debe buscar el icono de knit o ejecutar el comando CTRL + SHIFT + K.

knitr::include_graphics("images/rmarkdownflow.png")

Cada uno de los archivos posee la extensión .Rmd y cuenta por lo menos con tres tipos de contenidos:

8.1 Encabezado YAML

El encabezado YAML se encuentra en la parte inicial del documento y denota características del archivo, como título, autor, fecha, descripción, opciones de tabla de contenido, condiciones estéticas, opciones de referencias, opciones de figuras, etc. Además este encabezado YAML establece las opciones de render para los distintos formatos, viéndose en su estado más básico para cada uno de ellos de la siguiente manera:

---
title: "Untitled"
output: ******
---

Donde ****** puede ser reemplazado por los distintos formatos que podemos usar. Los formatos por defecto que podemos renderizar son los siguientes:

Para el formato html y para las presentaciones ioslides, podemos agregar un argumento extra que nos permita obtener un documento reactivo y dinámico como el curso que estás realizando en este momento. Veamos el YAML que corresponde a cada uno.

wzxhzdk:431
wzxhzdk:432

Es importante mencionar que cambiar el output es el equivalente a seleccionar alguna de las opciones en el panel de Rmarkdown mostrado anteriormente en la animación. Estos dos últimos formatos interactivos los encuentras en la sección de shiny en el mismo panel. Además, podemos renderizar otros formatos provenientes de paquetes, como los siguientes:

#install.packages("learnr")
#install.packages("rmdshower")
#install.packages("flexdashboard")
#install.packages("rmdformats")
#install.packages("xaringan")
#install.packages("prettydoc")
#install.packages("tufte")
#install.packages("rticles")
#install.packages("revealjs")

Dando para cada caso como resultado un YAML del tipo:

wzxhzdk:434
wzxhzdk:435
wzxhzdk:436
wzxhzdk:437
wzxhzdk:438
wzxhzdk:439
wzxhzdk:440
wzxhzdk:441
wzxhzdk:442

:::exercise

Ejercicio 8.1

:::

Para este ejercicio debes volver a Rstudio y explorar cada uno de los formatos disponibles en el panel de Rmarkdown, abre y ejecuta con knitr cada uno de ellos viendo sus diferencias y las salidas obtenidas. No dudes en pedir ayuda si tienes problemas en la ejecución o si tienes dudas con respecto a los diferentes formatos.

knitr::include_graphics("images/rmarkdown_wizards.png")

R en conjunto con diversos paquetes ofrece múltiples alternativas de render, sin embargo debemos mencionar que Markdown fue diseñado originalmente para la salida html, por lo que este formato posee características más ricas en comparación con los demás formatos de salida. Las siguientes subsecciones están orientadas al manejo de estas características para los distintos formatos. Tenga presente que este curso ofrece solo un preámbulo de las posibilidades que brinda Rmarkdown, si desea profundizar en cada uno de los formatos, consulte R Markdown: The Definitive Guide

8.1.1 Opciones de tabla de contenido

Estas opciones aplican para los formatos html y pdf y denotan el funcionamiento de la tabla de contenido con el argumento toc: true. Adicionalmente, para especificar la profundidad de los encabezados utilizamos el argumento toc_depth. Veamos el YAML que corresponde para estos dos formatos:

wzxhzdk:444
wzxhzdk:445

Como se observa el máximo encabezado que se mostrará en la tabla de contenido para el formato html será de título de segundo nivel, mientras que para el pdf será de tercer nivel. Un opción adicional que se puede incluir en el formato html corresponde a la tabla de contenido flotante con el argumento toc_float:true, el cual la mostrará permanentemente al lado izquierdo del contenido principal. Veamos el uso de este argumento en el YAML:

---
title: "Untitled"
output: 
  html_document:
    toc: true
    toc_depth: 2
    toc_float: true
---

8.1.2 Opciones de figuras

Al igual que con la tabla de contenido las opciones para figuras solo están disponibles para los formatos html y pdf, su configuración solo incluye tres aspectos: 1) alto; 2) ancho y 3) subtítulo, los argumentos en los dos formatos son exactamente los mismos dando como resultado YAML del tipo:

wzxhzdk:447
wzxhzdk:448

Si combinados las opciones de tabla de contenido con las opciones de figura el resultado de los YAML seria el siguiente:

wzxhzdk:449
wzxhzdk:450

8.1.3 Opciones de impresión de dataframe

Estas opciones son para los formatos pdf y html y determinan la estética de los marcos de datos en el documento de salida. Veamos la siguiente tabla que muestra las opciones disponibles.

+----------+----------------------------------------+----------------+-----------------+ | Opción | Descripción | pdf_document | html_document | +==========+========================================+================+=================+ | default | Llama print.data.frame | Disponible | Disponible | +----------+----------------------------------------+----------------+-----------------+ | kable | Usa la función knitr::kable | Disponible | Disponible | +----------+----------------------------------------+----------------+-----------------+ | tibble | Usa la función tibble::print.tbl_df | Disponible | Disponible | +----------+----------------------------------------+----------------+-----------------+ | paged | Usa la función rmarkdown::paged_table| No disponible | Disponible | +----------+----------------------------------------+----------------+-----------------+

Supongamos que queremos usar la opción kable para un pdf y la opción paged para un html, sus YAML correspondientes haciendo uso de las opciones de tabla de contenido y de figuras tendrán el siguiente aspecto:

wzxhzdk:451
wzxhzdk:452

8.1.4 Opciones de Referencias

Las opciones de referencias bibliográficas son limitadas, sin embargo están disponibles para todos los formatos haciendo uso del lenguaje BibTeX. Lo primero es tener un archivo con la extensión correspondiente (.bib) que contenga las citaciones de interés, veamos un ejemplo del contenido de un archivo con esta extensión:

@Manual{rbase,
  title = {R: A Language and Environment for Statistical Computing},
  author = {{R Core Team}},
  organization = {R Foundation for Statistical Computing},
  address = {Vienna, Austria},
  year = {2020},
  url = {https://www.R-project.org/},
}

@Manual{curso_actual,
  title = {CursoR: Un Curso Amigable Para R},
  author = {Duvan Nieves and Leonardo Lopez},
  year = {2020},
  note = {R package version 0.1.0},
  url = {https://github.com/Duvancho321/Curso_R},
}
Ahora el enlace con el YAML es relativamente simple, solo debemos usar el argumento `bibliography:` y agregar el nombre de nuestro archivo, Veamos un ejemplo suponiendo que nuestro archivo lleva el nombre **“biblio.bib”**:
wzxhzdk:454
Con un YAML como el anterior y citando en nuestro texto plano obtendremos las referencias bibliográficas la final de nuestro documento sin importar el formato seleccionado. En caso de que deseemos una bibliografía sin citar explícitamente en el texto plano podemos usar el argumento `nocite: '@*'`
wzxhzdk:455

8.1.5 Opciones de adicionales

En nuestro YAML podemos agregar otras opciones como autores, fecha, subtítulos y otras que conciernen a formatos más específicos. Por el momento veamos las opciones nombradas anteriormente reuniendo todo lo que hemos aprendido del YAML.

wzxhzdk:456
wzxhzdk:457

8.2 Trozos de código

Los trozos o fragmentos de código funcionan de forma similar a un Script y pueden producir salidas de texto, gráficos o tablas. La forma de insertarlos es usando el comando CTRL + ALT + I o Cmd + Option + I en macOS. Un fragmento de código R luce de la siguiente forma:

Fijémonos en los tres botones que se ven en la parte derecha del chunk, el primero nos da opciones de configuración que trataremos con detalle más adelante, el segundo ejecuta todos los trozos de código de arriba y el último ejecuta el fragmento actual.

8.2.1 Opciones globales de fragmentos de código

8.2.2 Opciones individuales de fragmentos de código

8.2.3 Otros lenguajes soportados

8.3 Texto

sintaxis Markdown titulos negrilla citaciones codigo html ecuaciones importar imagenes hiperbinculos pie de pagina

9. Lectura de datos

10. Referencias



Duvancho321/Curso_R documentation built on Sept. 3, 2020, 5:17 p.m.