--- title: "Capítulo 20" subtitle: "Vectores" output: ioslides_presentation --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = FALSE, warning = FALSE, message = FALSE) ``` ```{r, echo=FALSE, message=FALSE, results='hide'} library(tidyverse) library(lobstr) ``` ## Vectores Son los tipos de datos que forman la base de la estructura de los tibbles. La gran mayoría de las funciones que manipulen a los tibbles se escriben a nivel de vecotres.
![](data-structures-overview.png){ width=80% }
Los tipos: - Atómicos + Contienen datos homogéneos. - Listas + Pueden contener datos heterogéneos - NULL + Vector de longitud cero, utilizado para representar la ausencia de un vector. - Vectores aumentados. + Factores, fechas, dataframes y tibbles.
## Vectores atómicos * Hay cuatro tipos de vectores atómicos: + **Lógicos**: Se pueden escribir (TRUE, FALSE) o (T, F). Donde NA es un valor especial. + **Dobles**: Se pueden especificar mediante decimales (0.1234) o en notación científica (1.23e4) o hexadecimal (0xcafe). Tienen cuatro valores especiales: Inf, -Inf, NaN y NA. + **Enteros**: Se escriben similar a los dobles pero seguidos de una L (1234L, 1e4L, 0xcafeL), solo tienen un valor especial NA. + **Caracteres**: Se escriben mediante comillas simples y dobles ("hola") o ('chau'). ```{r, echo=T, eval=T} lgl_var <- c(TRUE, F, 1:10%%4==0, NA) int_var <- c(1L, 6L, 10L) dbl_var <- c(1, 2.5, 4.5) chr_var <- c("these are", "some strings") ``` Cuando los parametros de **c()** son otros vectores atómicos, retorna un vector atómico: ```{r, echo=T, eval=T, collapse=T} c(c(1,2), c(3,4)) ``` ## Propiedades Cada vector tiene dos propiedades claves. Su ***tipo***: ```{r, echo=T, collapse=T} typeof(lgl_var) typeof(int_var) typeof(dbl_var) typeof(chr_var) ``` Su ***longitud***: ```{r, echo=T, collapse=T} x <- list("a", "b", 1:10) length(x) ```` ## Detalles de implementación No existen escalares, sólo son vectores de longitud 1. ```{r, echo = T, eval=T, collapse=T} 3[1] ``` Los enteros son exactos, los dobles pueden no serlo. ```{r, echo=T, collapse =T} sqrt(2)^2 == 2 ``` Para comparar valores de punto flotante (no es posible representarlos mediante una cantidad finita de memoria): ```{r, echo=T, collapse =T} near(sqrt(2)^2, 2) #requiere dplyr ``` La complejidad se propaga, en particular los vectores asumen el tipo del elemento más complejo. ```{r, echo=T, collapse=T} typeof(c(1L, 2L, 3.5)) ``` ## Implementación en la memoria Si bien los objetos en _R_ pueden dar la apariencia de mutabilidad, la gran mayoría no lo son. Inclusive en las asignaciones mas sencillas. ```{r, echo=T, eval=T, collapse=T} x <- 1 obj_addr(x) # librery lobstr x <- 2 obj_addr(x) ``` Los objetos asignados a partir de otros son punteros a la misma celda de memoria. ```{r, echo=T, collapse=T, eval=T} x <- 1 y <- x obj_addr(x) obj_addr(y) ``` ## _R_ crea copias al modificar los vectores. ```{r,echo=T, collapse=F,eval=T} x <- c(1,2,3) y <- x y[3] <- 3 all(x==y) # x, y <- 1,2,3 obj_addr(x) obj_addr(y) ``` ## Para listas y dataframes el comportamiento es diferente. En vez de guardar los valores mismos, guarda referencias a éstos. ```{r, echo=T, eval=F} l1 <- c(1,2,3) l2 <- l1 # Comparten memroia. ```
![](l-modify-1.png){ width=60% }
```{r, eval=F, collapse=T, echo=T} l2[[3]] <- 4 ```
![](l-modify-2.png){ width=60% }
Luego, si se modifica algún valor, los comunes siguen compartiendo memoria.
## Algo similar pero más sutil ocurre con los dataframes y tibbles: ```{r, eval=F, echo=T} d1 <- data.frame(x = c(1, 5, 6), y = c(2, 4, 3)) ``` ```{r, echo=T, eval=F} d2 <- d1 d2[, 2] <- d[, 2] * 2 ``` ![](d-modify-c.png){ width=30% } Si modificamos una columna, se crea una copia de la columna modificada y se le hace referencia. Siguen compartiendo memoria de las columnas en común. ## En cambio, si modificamos cualquier fila del dataframe, se hace una copia entera del mismo. Esto puede llevar a complicaciones de saturación de memoria. ```{r, echo=T, eval=F} d1 <- data.frame(x = c(1, 5, 6), y = c(2, 4, 3)) d3 <- d1 d3[1, ] <- d3[1, ]*3 ``` ![](d-modify-r.png){ width=70% } ## Vectores de caracteres ```{r, eval=T, echo=T} x <- c("a", "a", "abc", "d") ``` La idea: ![](character.png){ width=30% } La realidad: ![](character-2.png){ width=30% } ```{r,echo=T, eval=F} ref(x, character = T) # Muestra las referencias en memoria. ``` R utiliza un grupo global de cadenas de caracteres, donde cada elemento de un vector de caracteres hace referencia a una cadena única dentro del grupo. ## Valores especiales Hay caracteres especiales para los infinitos, los valores faltantes y los números que no existen. ```{r,echo=T, eval=T, collapse=T} c(-1,0, 1, NA)/0 ``` Los valores NaN y NA son infecciosos: ```{r, echo=T, eval=T, collapse=T} NA > 5 10*NA !NaN NaN + 5 ``` ## No siempre se propagan, pero no hay que apostar a esto: ```{r, eval=T, echo=T, collapse=T} NA ^ 0 NaN ^ 0 NA | TRUE NA & FALSE NaN | TRUE ``` Consideremos el siguiente caso: ```{r, eval=T, echo=T, collapse=T} x <- c(NA, 5, NaN, NA, 10) x == NA ``` La lógica de esto es NA no tienen porque ser los mismos no hacer referencia al mismo valor no existente. ## Testeo ¿Cómo los comparamos? ```{r, echo=T, eval=T, collapse=T} v <- c(0, Inf, NA, NaN) is.finite(v) is.infinite(v) is.na(v) is.nan(v) ``` La comparación de tipos: ```{r, eval=T, echo=T, collapse=T} is_logical(c(T, NA)) is_atomic(c('caracter')) is_character(c('h', 3)) #Convierte el 3 a caracter. is_list(list('h',3)) #Preserva los tipos. ``` ## Dimensiones Podemos crear vectores multidimensionales mediante comandos o cambiando la dimensión. ```{r, eval=T, echo=T, collapse=T} z <- 1:6 #1, 2, 3, 4, 5, 6 dim(z) <- 2:3 print(z) ``` Usando los comandos _array_ y _matrix_ : ```{r, echo=T, collapse=T, eval=T} matrix(1:6, nrow=2, ncol=3) array(1:12, c(2,3)) ``` ## Con _array_ y _dim_ podemos incluso crear tensores. ```{r, echo=T, collapse=F, eval=T} array(1:24, c(2,2,3)) ``` ## Coerción Coerción explícita: Es preferible intentar arreglar el tipo previamente a forzar una conversión. ```{r, eval=T, collapse=T, echo=T} v <- c(TRUE, FALSE, TRUE) as.integer(v) as.character(v) as.integer(c('caracter')) # Puede causar advertencias y NA: ``` Coerción implícita: Ocurre cuando se utiliza un vector en un contexto que espera otro tipo de vector. ```{r, echo=T, collapse=T, eval=T} v <- c(T, F, T, T) sum(v) if (length(3)){print(TRUE)} #En el otro sentido. ``` ## Reciclado R implícitamente coerciona las longitudes de los vectores para que las operaciones tengan sentido. Esto se llama _recycling_ o _broadcasting_. Vector y escalares: ```{r, eval=T, echo=T, collapse=T} sample(10) + 10 runif(10) > 0.5 ``` Vector y otros vectores: ```{r, eval=T, echo=T, collapse=T} rep(1,10) rep(1,10) + 1:2 #Expande el vector de menor longitud. rep(1,10) + 1:3 #Si el vector no es múltiplo, lo trunca. ``` ## Para evitar errores el reciclado de vectores con dimension mayor a 1, solo permite reciclar escalares. ```{r, echo=T, collapse=T, eval=T} tibble(x=1:4, y=2) try(tibble(x=1:4, y=1:2)) matrix(1, nrow=3, ncol=3) + 2 #matrix(1, 3, 3) + 1:2) #ERROR ``` ## Nombrar vectores Se puede nombrar cualquier tipo de vector, el nombrarlos facilita la selección y creación de subconjuntos. Al crearlos: ```{r, eval=T, echo=T} c(x = 1, y =2, z = 4) ``` Después de crearlos utilizando _purrr::set_names_ : ```{r, eval=T, echo=T} v <- 1:3 set_names(v, c("a", "b", "c")) ``` ## Subconjuntos y selección (subsetting) Al igual que _filter_ para las filas de un tibble, se puede seleccionar subconjuntos un vector. Ésta se hace mediante los corchetes rectos _[_ . Hay cuatro formas de utilizar ésto. 1. Utilizando un vector de enteros. Si los enteros son positivos, devuelve los elementos tales que los indices correspondan al valor de los enteros. ```{r, eval=T, echo=T, collapse=T} x <- c("uno", "dos", "tres", "cuatro", "cinco") x[c(3,2,5)] ``` Se puede incluso repetir indices, generando vectores de longitud mayor al original. ```{r, eval=T, echo=T, collapse=T} x[c(1,1,1,5,5,5,3,3)] #Creamos un vector de ``` Los enteros negativos eliminan elementos en las posiciones especificadas. No se permite mezclar enteros positivos con negativos. ```{r, eval=T, echo=T, collapse=T} x[c(-1,-3,-5)] ``` ## 2. Podemos crear subconjuntos mediante vectores lógicos, preserva sólo los índices que corresponden al valor _TRUE_ ```{r, eval=T, echo=T, collapse=T} x <- c(10, 3, NA, 5, 8, 1, NA, 9 , NaN) x[c(!is.na(x))] x[c(TRUE,FALSE)] # Porque funciona? x[x %% 2 == 0] # Los pares o NA.. ``` 3. Si el vector tiene nombres, podemos usarlos para seleccionar. ```{r, eval=T, echo=T, collapse=T} x <- c(abc = 1, def = 2, xyz = 5) x[c("xyz", "def")] x[rep("xyz", 10)] ``` ## 4. Usando [ ], nada. Nos permite seleccionar todos los elementos en la dimensión no especificada. Es particularmente util en datos multi dimensionados. ```{r, eval=T, echo=T, collapse=F} x <- array(1:27, c(3,3,3)) x[c(T,F), , c(1,3)] # Recicla la primer coordenada! ``` ## Listas Son una estructura más complejas que los vectores atómicos ya que pueden contener otras listas en su interior. Se pueden utilizar para crear estructuras jerárquicas como árboles. UUtilizamos _str()_ para obtener información de la lista sin despleguar todos sus contenidos. ```{r, eval=T, echo=T, collapse=F} x_named <- list(a = 1, b = 2, c =3, d = 4) str(x_named) ``` ## Pueden contener otros tipos, vectores e inclusive listas y listas de listas. ```{r, eval=T, echo=T, collapse=F} l <- list("a", 1L, 1.5, TRUE, c(1,2), list(a=1,b=2, c=list(1,3))) str(l) ``` ## Subconjuntos de listas Hay tres formas de seleccionar los elementos de una lista. 1. El corchete simple [ Extrae una sublista, el resultado siempre es una lista, al igual que con los vectores se pueden utilizar indices enteros o vectores lógicos: ```{r, eval=T, echo=T, collapse=T} a <- list(a = 1:3, b= "una cadena", c = pi, d = list(-1, -5)) str(a[1:2]) str(a[c(T, F, F)]) ``` ## 2. El doble corchete [[ Extrae un solo componente de una lista, eliminando un nivel de jerarquía. El resultado tiene el tipo del elemento que retorna. ```{r, eval=T, echo=T, collapse=T} a <- list(a = 1:3, b= "una cadena", c = pi, d = list(-1, -5)) str(a[[4]]) a[[3]] # Es un doble typeof(a[[3]]) a$b # El simbolo $ permite seleccionar al que hace referencia, es un atajo de seleccion. ``` ## Atributos Podemos agregarla metadatos cualesquiera a un vector mediante el uso de atributos (_attr_). ```{r, eval=T, echo=T, collapse =T} x <- 1:10 attr(x, "saludo") # Al campo "saludo" le asigna NULL ya que no se especificó. attr(x, "saludo") <- "hola!" # Al campo "saludo" le asigna la cadena "hola". attr(x, "despedida") <- "Chau!" attributes(x) ``` ## Atributos Hay 3 atributos principales utilizados comúnmente las diferentes partes de R. + Los **nombres**. + Las **dimensiones**. + La **clase**. - Las clases son parte de la programación orientada a objetos y nos permite crear estructuras y funciones cuyos comportamiento varie bajo distintas condiciones. Las clases más comunes de R son S3 (sencillas, fáciles usar pero con poca estructura formal), S4 (son como S3, pero enforza el formalismo, son más dificiles de usar, pero menos propensas a causar errores en proyectos grandes) y R6 (no son nativas a R, son clases de referncia, la principal ventaja es que NO se copian al modificar) Un ejemplo: ```{r, eval=T, echo=T, collapse =T} methods("as.Date") ``` ## Vectores Aumentados Los vectores aumentados son abstracciones que se construyen sobre los vectores y listas. Son los tibbles, data frames, arrays, factores, fechas, y muchas más. Factores: Como ya vimos, están diseñados para representar datos categoricos. Se construyen sobre los vectores enteros y tienen un atributo de _nivel_ . ```{r, eval=T, echo=T, collapse =T} x <- factor(c("ab", "cd", "ab"), levels = c("ab", "cd", "ef")) x typeof(x) attributes(x) ``` ## Las _fechas_ y _fechas hora_ son vectores numericos que representan el número de dias desde el 1ro de enero de 1970. ```{r, eval=T, echo=T, collapse =T} x <- as.Date("1971-01-01") unclass(x) typeof(x) ``` ```{r, eval=T, echo=T, collapse =T} x <- lubridate::ymd_hm("1970-01-01 01:00") unclass(x) ``` ## Los tibbles son listas aumentadas, tienen las clases tbl_df, tbl y data.frame. Sus atributos son los nombres de las columnas ( _names_ ) y los nombres de las filas _row.names_ . ```{r, eval=T, echo=T, collapse =T} tb <- tibble::tibble(x = 1:5, y = 5:1) df <- data.frame(x = 1:5, y = 5:1) typeof(tb) typeof(df) attributes(tb) ```