--- title: "Tibbles y Datos Ordenados" author: "Luciana Sastre" date: "5/05/2021" output: beamer_presentation: default ioslides_presentation: default slidy_presentation: default --- ```{r,echo=FALSE, message=FALSE, results='hide'} #tinytex::install_tinytex() library('knitr') library(datos) library(tidyverse) ``` ## Tibbles Los Tibbles son data frames que están modificados para facilitar el trabajo con el tidyverse. ## Crear tibbles En R, la mayoría de los paquetes suelen usar data frames clásicos entonces si queremos convertirlo en un tibble lo hacemos con **as_tibble()** \fontsize{9pt}{7.2}\selectfont ```{r} as_tibble(flores) ``` ## Crear tibble Podemos crear un tibble a partir de vectores individuales usando \textbf{tibble()} ```{r} tibble( x = 1:5, y = 1, z = x^2 + y) ``` ## Crear tibble Otra forma de crear un tibble es con \textbf{tribble()}, en donde los nombres de la columna se definen con fórmulas (comienzan con ~) y cada entrada está separada con comas. ```{r} tribble(~x, ~y, ~z, "a",2,3.6, "b", 1, 8.5) ``` ## tibble vs data.frame Existen dos motivos principales para usar un tibble: - Su impresión en pantalla - Selección de subconjuntos ## Impresión en pantalla \fontsize{10pt}{7.2}\selectfont Los tibble se imprimen en la pantalla de forma refinada: solo muestra las primeras 10 filas y las columnas que entren en el ancho de la pantalla y además del nombre, muestra el tipo de cada columna. Si queremos una visión completa de los datos podemos utilizar \textbf{view()}. Y si queremos una visión especifica podemos usar \textbf{print()} y controlar el número de filas y el ancho mostrado en pantalla. \fontsize{8pt}{7.2}\selectfont ```{r} vuelos ``` ## Selección de subconjuntos Si queremos recuperar alguna variable individual necesitamos algunas herramientas: - \textbf{[[} permite extraer variables usando tanto su nombre como su posición - **$** nos permite extraer variables mediante el nombre ## Selección de subconjuntos ```{r} ejemplo <- tibble( x= 1:5, y= 6:10) ejemplo$x ejemplo[[2]] ``` ## Datos ordenados Vamos a aprender una manera consistente para organizar los datos en R a la que llamaremos \textbf{tidy data} En este capítulo veremos una introducción práctica a los datos ordenados (tidy data) y a las herramientas que tiene el paquete \textbf{tidyr} que es parte del núcleo del tidyverse ## Reglas para ordenar Nosotros podemos representar los mismos datos subyacentes de distintas formas. Ahora vamos a ver 4 formas distintas de organizar un mismo conjunto de datos. Cada tabla muestra los mismos valores de 4 variables: - país - año (anio) - población - casos --- ```{r} tabla1 ``` --- ```{r} tabla2 ``` --- ```{r} tabla3 ``` --- ```{r} tabla4a ``` ```{r} tabla4b ``` ## Reglas para ordenar Existen 3 reglas interrelacionadas que hacen que un conjunto de datos sea ordenado: 1. Cada variable debe tener su propia columna 2. Cada observación debe tener su propia fila 3. Cada valor debe tener su propia celda ![](C:\Users\Usuario\Desktop\Facultad\Seminario R-analisis de datos\3 reglas.PNG){width='450px'} ## Observación Estas reglas están interrelacionadas pues no es posible cumplir solo un par de las tres. Esto lleva a un conjunto práctico de instrucciones que es incluso más simple: 1. Coloca cada conjunto de datos en un tibble 2. Coloca cada variable en una columna Notar que de las 4 tablas que vimos solo la tabla1 estaba ordenada ## ¿Porque es importante tener los datos ordenados? 1. Si tenemos una estructura de datos consistente, resulta más fácil aprender las herramientas que funcionan con ella 2. Existe una ventaja específica en situar las variables en las columnas pues permite que la naturaleza vectorizada de R brille. Esto es pues muchas de las funciones que vienen en R trabajan con vectores de valores. ## Ejemplos de tabla1 Calcular tasa por cada 10,000 habitantes ```{r} tabla1 %>% mutate(tasa = casos / poblacion * 10000) ``` --- Calcular casos por anio ```{r} tabla1 %>% count(anio, wt=casos) ``` --- \fontsize{8pt}{7.2}\selectfont Visualizar cambios en el tiempo \fontsize{8pt}{7.2}\selectfont ```{r} library(ggplot2) ggplot(tabla1, aes(anio, casos)) + geom_line(aes(group = pais), colour = "grey50") + geom_point(aes(colour = pais)) ``` ## Pivotear En la realidad, gran parte de los datos que encontramos están desordenados ¿Por qué? 1. No todos están familiarizados con los principios de datos ordenados 2. Los datos usualmente están organizados para facilitar distintas tareas del análisis --- Para la mayoría de los análisis necesitamos realizar algún tipo de orden. EL primer paso es entender cuáles son las variables y cuáles son las observaciones (No siempre es fácil y puede que necesitemos consultar quienes crearon el dataset) El siguiente paso es resolver uno de los siguientes problemas: 1. Una variable se extiende por varias columnas 2. Una observación está dispersa entre múltiples filas ## Datos "largos" Un problema común es cuando es un dataset los nombres de las columnas no representan variables, sino que representa los valores de una variables. Tomemos como ejemplo taba4a ```{r} tabla4a ``` Los nombres de las columnas 1999 y 2000 representan los valores de la variable años, los valores en las columnas 1999 y 2000 representan valores de las variable caos y cada fila muestra dos observaciones en lugar de una. --- Para ordenarlo necesitamos \textbf{pivotear} las columnas que no cumplen las reglas en un nuevo par de variables. Para ellos necesitamos 3 parámetros: 1. El conjunto de columnas cuyos nombres son valores y no variables. (1999 y 2000) 2. El nombre de la variable cuyos valores forman los nombres de las columnas. Llamaremos a esto \textbf{key} (anio) 3. El nombre de la variable cuyos valores están repartidos en las celdas. Llamaremos a esto \textbf{value} (casos) Dados estos parámetros, podemos ordenar tabla4a con la función **pivot_longer()** --- \fontsize{8pt}{7.2}\selectfont ```{r} tabla4a %>% pivot_longer(cols = c(`1999`, `2000`), names_to = "anio", values_to = "casos") ``` Las columnas a girar quedan ordenadas siguiendo el estilo de select() (es decir, en el orden que las nombres). Como las variables anio y casos no existen en tabla4a, debemos poner sus nombres en comillas. --- ![](C:\Users\Usuario\Desktop\Facultad\Seminario R-analisis de datos\pivotear largo.PNG){width='400px'} --- Notar que con tabla4b podemos hacer lo mismo \fontsize{8pt}{7.2}\selectfont ```{r} tabla4b ``` \fontsize{7pt}{7.2}\selectfont ```{r} tabla4b %>% pivot_longer(cols = c(`1999`, `2000`), names_to = "anio", values_to = "poblacion") ``` ## Función interesante Nosotros podemos combinar las versiones ordenadas de tabla4a y tabla4b en un único tibble con **left_join()** \fontsize{7pt}{7.2}\selectfont ```{r} tidy4a <- tabla4a %>% pivot_longer(cols = c(`1999`, `2000`), names_to = "anio", values_to = "casos") tidy4b <- tabla4b %>% pivot_longer(cols = c(`1999`, `2000`), names_to = "anio", values_to = "poblacion") left_join(tidy4a, tidy4b) ``` ## Datos "anchos" **pivot_wider()** que es lo opuesto de pivot_longer(). Lo usamos cuando una observación aparece en múltiples filas. \fontsize{9pt}{7.2}\selectfont ```{r} tabla2 ``` --- Para ordenarlo necesitamos dos parámetros: 1. La columna de donde obtenemos los nombres de las variables (tipo) 2. La columna desde la que obtener los valores (cuenta) ```{r} tabla2 %>% pivot_wider(names_from = tipo, values_from = cuenta) ``` --- ![](C:\Users\Usuario\Desktop\Facultad\Seminario R-analisis de datos\pivotear ancho.PNG){width='400px'} ## Separar y Unir Notar que hasta ahora hemos podido ordenar las tablas 2 y 4 ¿Qué pasa con la 3? Esta tiene un problema diferente. Hay una columna (tasa) que contiene dos variables (casos y población) ```{r} tabla3 ``` Para solucionar problemas como este necesitamos la función \textbf{separate()}. También aprenderemos a usar su complemento \textbf{unite()} ## Separar \textbf{separate()} desarma una columna en varias columnas según la posición de un carácter separador. La función toma el nombre de la columna a separar y el nombre de las columnas a donde irá el resultado ```{r} tabla3 %>% separate(tasa, into = c("casos", "poblacion")) ``` --- ![](C:\Users\Usuario\Desktop\Facultad\Seminario R-analisis de datos\separate.PNG){width='400px'} ## Observaciónes Por defecto separate() dividirá la columna en donde encuentre un carácter no alfanumérico (no numero o letra). Si deseamos usar un carácter especifico podemos especificarlo en el argumento sep de separate() \fontsize{10pt}{7.2}\selectfont ```{r} tabla3 %>% separate(tasa, into = c("casos", "poblacion"), sep = "/") ``` --- Por defecto separate() preserva el tipo de columna. Podemos pedir a separate() que intente convertir a un tipo más acertado con convert = TRUE \fontsize{10pt}{7.2}\selectfont ```{r} tabla3 %>% separate(tasa, into = c("casos", "poblacion"), convert = TRUE) ``` --- También podemos pasar un vector de enteros a sep. separate() interpreta los enteros como las posiciones donde dividir en donde los valores positivos comienzan en 1 al extremo izquierdo y los negativos comienzan en -1 al extremo derecho. Por ejemplo podemos usar esto para separar los últimos dos dígitos de cada año. (Esto no ordena los datos pero es un ejemplo útil para entender el funcionamiento) \fontsize{8pt}{7.2}\selectfont ```{r} tabla3 %>% separate(anio, into = c("siglo", "anio"), sep = 2) ``` ## Unir \textbf{unite()} combina múltiples columnas en una. No suele ser tan utilizada como separate() pero aún así está bueno conocerla La función toma un dataframe, el nombre de la variable a crear y un conjunto de columnas a combinar (las mismas se especifican con el criterio de la función select()) ## Ejemplo Podemos unir las columnas siglo y anio que creamos en el ejemplo anterior. Los datos están guardados en tabla5 ```{r} tabla5 %>% unite(nueva, siglo, anio) ``` ## Observaciones - Para unite() también necesitaremos del argumento sep. Por defecto, al unir pondrá un guion bajo entre los valores. Si no queremos ningún separador usamos "" ```{r} tabla5 %>% unite(nueva, siglo, anio, sep = "") ``` ## Valores faltantes Al cambiar la representación de un dataset corremos el riesgo de generar valores faltantes. Los valores pueden perderse de dos formas: 1. Explicita: aparece un NA 2. Implícita: simplemente no aparece en los datos Para estudiar su comportamiento utilizaremos otro dataset ```{r} acciones <- tibble( anio = c(2015, 2015, 2015, 2015, 2016, 2016, 2016), trimestre = c(1, 2, 3, 4, 2, 3, 4), retorno = c(1.88, 0.59, 0.35, NA, 0.92, 0.17, 2.66) ) ``` --- Existen dos valores faltantes en el dataset: 1. El retorno del cuarto trimestre de 2015 (explicito) 2. El retorno al primer trimestre del 2016 (implícito) ## Observaciones La forma en que se representa un dataset puede dejar explícitos los valores implícitos. En el ejemplo anterior podemos volver explícitos los valores faltantes implícitos al mover los años a las columnas ```{r} acciones %>% pivot_wider(names_from = anio, values_from = retorno) ``` --- Otra forma de hacer explícitos los valores faltantes en datos ordenados es con \textbf{complete()}. \textbf{complete()} tomo un conjunto de columnas y encuentra todas las combinaciones únicas. Luego se asegura que el dataset contenga todos los valores y completa con NA donde sea necesario. \fontsize{8pt}{7.2}\selectfont ```{r} acciones %>% complete(anio, trimestre) ``` --- Una forma de completar los datos faltantes es con \textbf{fill()}. Esta función toma un conjunto de columnas sobre las cuales los valores faltantes son reemplazados por el valor anterior más cercano. (metodo LOCF: last observation carried forward) Esta herramienta es útil pues en algunos casos en que la fuente de datos es usada principalmente para ingresar datos, los valores faltantes pueden indicar que el valor previo debe arrastrarse hacia adelante. --- \fontsize{8pt}{7.2}\selectfont ```{r} tratamiento <- tribble( ~sujeto, ~tratamiento, ~respuesta, "Derrick Whitmore", 1, 7, NA, 2, 10, NA, 3, 9, "Katherine Burke", 1, 4 ) tratamiento tratamiento %>% fill(sujeto) ``` ## Estudio de caso Vamos a trabajar con el dataset \textbf{oms} que contiene datos de tuberculosis detallados por año, edad, sexo y método de diagnóstico. Hay mucha información epidemiológica en el dataset pero es complicado trabajar con estos datos tal como están --- \fontsize{8pt}{7.2}\selectfont ```{r} oms ``` --- Esto es un ejemplo muy típico de la vida real. Contiene columnas que son redundantes, códigos extraños de variables y muchos valores faltantes. En otras palabras, oms está muy desordenado y necesitamos varios pasos para ordenarlo. Usualmente la mejor forma de empezar es reunir las columnas que no representen variables. Observar que tenemos: - pais, iso2 e iso3 son variables redundantes que se refieren al pais - anio es claramente una variables - No sabemos todavía el significa de las otras columnas, pero dada la estructura de los nombres de las variables parecieran ser valores y no variables. --- \fontsize{8pt}{7.2}\selectfont Necesitamos agrupar las columnas desde nuevos_fpp_h014 hasta recaidas_m65 (de la primera a la última). Como no sabemos que representan, les daremos el nombre genérico de "clave". Sabemos que las celdas representan la cuenta de casos por lo que usaremos la variable casos. Momentáneamente nos centraremos en los valores que están presentes y no en los Na \fontsize{8pt}{7.2}\selectfont ```{r} oms1 <- oms %>% pivot_longer( cols = nuevos_fpp_h014:nuevosrecaida_m65, names_to = "clave", values_to = "casos", values_drop_na = TRUE ) oms1 ``` --- Podemos tener una noción de la estructura de los valores en la nueva columna clase si realizamos un conteo ```{r} oms1 %>% count(clave) ``` --- Experimentando y probando un poco podríamos resolver esto por nuestra cuenta pero por suerte tenemos a mano un diccionario de datos: 1. Lo que aparece previo al primer _ nos indica si la columna contiene nuevos o antiguos casos de tuberculosis. (En este caso son todos nuevos) 2. Lo que aparece luego del _ refiere al tipo de tuberculosis: - recaída: refiere a casos reincidentes - ep: tuberculosis extra pulmonar - fpn: casos de tuberculosis extra pulmonar que no se pueden detectar mediante examen de frotis pulmonar - fpp: los casos que si se pueden detectar por examen de frotis pulmonar 3. Lo que aparece después del segundo _ es el sexo de los pacientes 4. Los números finales refieren al grupo etario que se organizó en 6 categorías: 014, 1524, 2534, 3544, 4554, 5564, 65 (65 o más) --- \fontsize{8pt}{7.2}\selectfont Necesitamos realizar un pequeño cambio al formato de los nombres de las columnas. Los nombres de las columnas son un tanto inconsistentes pues en vez de ser nuevos_recaida están como nuevosrecaida. Para este cambio utilizaremos **str_replace()**. Vamos a aprender a cómo utilizarlo más adelante pero por momento solo nos interesa saber que reemplaza los caracteres "nuevosrecaida" por "nuevos_recaida" \fontsize{8pt}{7.2}\selectfont ```{r} oms2 <- oms1 %>% mutate(clave = stringr::str_replace(clave, "nuevosrecaida", "nuevos_recaida")) oms2 ``` --- Usando \textbf{separate()} vamos a separar los valores de cada código. Lo vamos a utilizar dos veces. Primero vamos a dividir según cada _ \fontsize{10pt}{7.2}\selectfont ```{r} oms3 <- oms2 %>% separate(clave, c("nuevos", "tipo", "sexo_edad"), sep = "_") oms3 ``` --- Ahora vamos a eliminar nuevos pues es constante en nuestro dataset e iso2 e iso3 pues son redundantes \fontsize{10pt}{7.2}\selectfont ```{r} oms4 <- oms3 %>% select(-nuevos, -iso2, -iso3) oms4 ``` --- Luego separamos sexo_edad en sexo y edad dividiendo luego del primer carácter \fontsize{10pt}{7.2}\selectfont ```{r} oms5 <- oms4 %>% separate(sexo_edad, c("sexo", "edad"), sep = 1) oms5 ``` --- Ahora oms esta ordenado!!! Nosotros hemos realizado paso a paso, pero en general lo que se hace es formar un incrementalmente un encadenamiento complejo usando pipes: \fontsize{8pt}{7.2}\selectfont ```{r} oms %>% pivot_longer( cols = nuevos_fpp_h014:nuevosrecaida_m65, names_to = "clave", values_to = "valor", values_drop_na = TRUE) %>% mutate(clave = stringr::str_replace(clave, "nuevosrecaida", "nuevos_recaida")) %>% separate(clave, c("nuevos", "tipo", "sexo_edad")) %>% select(-nuevos, -iso2, -iso3) %>% separate(sexo_edad, c("sexo", "edad"), sep = 1) ```