class: inverse, center, middle, title-slide <style type="text/css"> .remark-slide table{ border: none } .remark-slide-table { } tr:first-child { border-top: none; } tr:last-child { border-bottom: none; } </style> <style type="text/css"> /* THIS IS A CSS CHUNK - THIS IS A COMMENT */ /* Size of font in code echo. E.g. 10px or 50% */ .remark-code { font-size: 70%; } /* Size of font in text */ .medium-text { font-size: 75%; } /* Size of font in tables */ .small-table table { font-size: 6px; } .medium-table table { font-size: 8px; } .medium-large-table table { font-size: 10px; } </style> # Introducción a R para la epidemiología aplicada ### Limpieza de datos con R (parte 2) [contact@appliedepi.org](mailto:contact@appliedepi.org) --- # Objetivos y agenda - Familiarizarte con más funciones utilizadas para limpiar datos en un contexto de salud pública - Añade pasos avanzados de re-codificación lógica a tu "cadena de comandos" de limpieza de datos - Introducción de los conceptos de estructura de "datos ordenados" <div class="tabwid"><style>.cl-f1a96fa6{}.cl-f1a0ec96{font-family:'Arial';font-size:11pt;font-weight:normal;font-style:normal;text-decoration:none;color:rgba(0, 0, 0, 1.00);background-color:transparent;}.cl-f1a4ee90{margin:0;text-align:left;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);padding-bottom:5pt;padding-top:5pt;padding-left:5pt;padding-right:5pt;line-height: 1;background-color:transparent;}.cl-f1a4ff20{width:1.033in;background-color:transparent;vertical-align: middle;border-bottom: 1.5pt solid rgba(102, 102, 102, 1.00);border-top: 1.5pt solid rgba(102, 102, 102, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff2a{width:2.603in;background-color:transparent;vertical-align: middle;border-bottom: 1.5pt solid rgba(102, 102, 102, 1.00);border-top: 1.5pt solid rgba(102, 102, 102, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff2b{width:1.033in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff34{width:2.603in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff35{width:1.033in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff3e{width:2.603in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff3f{width:1.033in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff48{width:2.603in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(0, 0, 0, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff49{width:1.033in;background-color:transparent;vertical-align: middle;border-bottom: 1.5pt solid rgba(102, 102, 102, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff52{width:2.603in;background-color:transparent;vertical-align: middle;border-bottom: 1.5pt solid rgba(102, 102, 102, 1.00);border-top: 0 solid rgba(0, 0, 0, 1.00);border-left: 0 solid rgba(0, 0, 0, 1.00);border-right: 0 solid rgba(0, 0, 0, 1.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff53{width:1.033in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(255, 255, 255, 0.00);border-top: 0 solid rgba(255, 255, 255, 0.00);border-left: 0 solid rgba(255, 255, 255, 0.00);border-right: 0 solid rgba(255, 255, 255, 0.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}.cl-f1a4ff5c{width:2.603in;background-color:transparent;vertical-align: middle;border-bottom: 0 solid rgba(255, 255, 255, 0.00);border-top: 0 solid rgba(255, 255, 255, 0.00);border-left: 0 solid rgba(255, 255, 255, 0.00);border-right: 0 solid rgba(255, 255, 255, 0.00);margin-bottom:0;margin-top:0;margin-left:0;margin-right:0;}</style><table data-quarto-disable-processing='true' class='cl-f1a96fa6'><thead><tr style="overflow-wrap:break-word;"><th class="cl-f1a4ff20"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Tiempo</span></p></th><th class="cl-f1a4ff2a"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Tema</span></p></th></tr></thead><tbody><tr style="overflow-wrap:break-word;"><td class="cl-f1a4ff2b"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">20 minutos</span></p></td><td class="cl-f1a4ff34"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Revisión del módulo anterior</span></p></td></tr><tr style="overflow-wrap:break-word;"><td class="cl-f1a4ff35"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">20 minutos</span></p></td><td class="cl-f1a4ff3e"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Demostración - codificación lógica</span></p></td></tr><tr style="overflow-wrap:break-word;"><td class="cl-f1a4ff3f"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">2.5 horas</span></p></td><td class="cl-f1a4ff48"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Ejercicio</span></p></td></tr><tr style="overflow-wrap:break-word;"><td class="cl-f1a4ff49"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">20 minutos</span></p></td><td class="cl-f1a4ff52"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Repaso</span></p></td></tr></tbody><tfoot><tr style="overflow-wrap:break-word;"><td colspan="2"class="cl-f1a4ff53"><p class="cl-f1a4ee90"><span class="cl-f1a0ec96">Toma los descansos que necesites durante los ejercicios</span></p></td></tr></tfoot></table></div> ??? Fíjate en las pausas de estiramiento. --- # Más funciones de limpieza de datos En este módulo abordaremos: | La función | Utilidad | | ---------- | ----------------------------------------- | | `mutate()` | crear y transformar columnas | | `ifelse()` | recodificación lógica simple de valores | | `case_when()` | recodificación lógica compleja de valores | | `age_categories()` | crear columna de categoría de edad | | `coalesce()` | priorización de valores | | `select()` | subconjuntos y ordenar columnas | --- class: inverse, center, middle # Repaso del módulo anterior --- # Contar una historia con el operador pipe El **orden de la limpieza** es una historia, una **serie de acciones** realizadas sobre datos sin procesar. En operador **`%>%`** "pipe" significa **"y entonces"** (...realiza la acción en la línea siguiente). ¿Alguien puede explicar verbalmente este código, comando por comando? ```r vig <- vig_bruta %>% clean_names() %>% rename( edad_anios = edad, fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) ``` --- # Contar una historia con el operador pipe A **orden de limpieza** es una historia, una **serie de acciones** realizadas a los datos en "crudo". En operador pipe (**`%>%`**) significa **"y entonces"** (...realiza la acción en la línea siguiente). ¿Alguien puede explicar verbalmente este comando? ```r vig <- vig_bruta %>% # crea un nuevo objeto como: el objeto crudo canalizado a..... clean_names() %>% # estandarizar los nombres de las columnas rename( # editar el nombre de las columnas manualmente edad_anios = edad, # edad a edad en años fecha_sintomas = fecha_inicio_sintomas, # usa date_ como prefijo fecha_notifica = fecha_de_notifica) %>% # usa date_ como prefijo mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% # ayuda R a entender fecha de inicio de sintomas (convertir a objeto de fecha) mutate(fecha_notifica = mdy(fecha_notifica)) %>% # ayuda R a entender fecha de reporte mutate(sexo = recode(sexo, # edita los valores de sexo para que: "m" = "hombre", # "m" is now "masculino" "f" = "mujer")) # "f" is now "femenino" ``` --- # Errores comunes ¿Cuál es la causa de este mensaje de error? ```r # crea una base de datos limpia vig <- vig_bruta %>% clean_names() %>% rename( edad_anios = edad, fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) ``` ```r Error in recode(sexo, m = "hombre", f = "mujer") : object 'sexo' not found ``` --- # Errores comunes ¿Cuál es la causa de este mensaje de error? ```r # create limpios dataset vig <- vig_bruta %>% clean_names() %>% rename( edad_anios = edad, fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% * mutate(fecha_notifica = mdy(fecha_notifica)) mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) ``` ```r Error in recode(sexo, m = "hombre", f = "mujer") : object 'sexo' not found ``` Falta un operador pipe, lo que rompe la cadena. La función `mutate(sexo...` no ha recibido la base de datos del paso anterior, y por tanto R no tiene contexto de lo que la columna `sexo` es. --- # Errores comunes ¿Cuál es la causa de este mensaje de error? ```r # create limpios dataset vig <- vig_bruta %>% clean_names() %>% rename( edad_anios = edad, fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) %>% # make a table with the limpios datos tabyl(vig, sexo, distrito) ``` ```r Error in `dplyr::select()`: ! Can't subset columns that don't exist. ✖ Column `vig` doesn't exist. ``` --- # Errores comunes ¿Cuál es la causa de este mensaje de error? ```r # create limpios dataset vig <- vig_bruta %>% clean_names() %>% rename( edad_anios = edad, fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% mutate(sexo = recode(sexo, "m" = "hombre", * "f" = "mujer")) %>% # make a table with the limpios datos tabyl(vig, sexo, distrito) ``` ```r Error in `dplyr::select()`: ! Can't subset columns that don't exist. ✖ Column `vig` doesn't exist. ``` Un operador pipe "colgante" o adicional pasó el conjunto de datos limpio a un comando *separado* (`tabyl()`) que no debe de formar parte de los comandos de limpieza. --- # Script desordenado .pull-left[ **Evita esto** ```r *vig <- vig_bruta %>% clean_names() *vig <- vig_bruta %>% clean_names() %>% rename( fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica) tabyl(vig, sexo, distrito) class(vig$fecha_sintomas) *vig <- vig_bruta %>% clean_names() %>% rename( fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = report_date) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) ``` ] .pull-right[ - Guarda **un solo comando de limpieza** . Añade nuevas funciones o comandos conectados con operadores pipe. ] --- # Limpiar script .pull-left[ Script limpio ```r # Create limpios dataset ------------------------------------- *vig <- vig_bruta %>% clean_names() %>% rename( fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = report_date) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_de_notifica)) %>% mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) ``` ] .pull-right[ - Guarda **un solo comando de limpieza** . Añade nuevas funciones o comandos conectados con operadores pipe. ] --- # Limpiar script .pull-left[ Limpiar script ```r # Create limpios dataset ------------------------------------- vig <- vig_bruta %>% clean_names() %>% rename( fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = report_date) %>% mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_de_notifica)) %>% mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) # Area de prueba --------------------------------------------- *tabyl(vig, sexo, distrito) # cross-tabulate *class(vig$fecha_sintomas) # check column class ``` ] .pull-right[ - Para mejor lectura del código, coloca estos comandos simples en el **Área de pruebas** ] --- # Presentar vs. guardar **Presentar** Este comando no tiene el operador **`<-`**, por lo que sólo *presenta* lo modificado `vig` en la consola. La base de datos de origen no ha cambiado. ```r *vig_bruta %>% # presenta solo los casos mayores de 10 años filter(edad > 10) ``` **Guardar** Añadir el operador de asignación **<-** para crear un nuevo objeto dataframe o base de datos. ```r *vig_mas10 <- vig_bruta %>% # crea un nuevo objeto dataframe filter(edad > 10) ``` (No hay salida en la consola de R, pero sí un nuevo objeto en el panel Entorno R) ??? Observa cómo los nombres de las columnas en `vig` ¡son antiguos! Es porque nuestros cambios aún no se han guardado. --- class: inverse, center, middle # Recodificación de valores --- # Ejemplo de base de datos ```r vig_bruta ``` ``` ## id_caso edad sexo hospital lab_conferma epilink fiebre ## 1 86340d 1 f Port Hospital TRUE si si ## 2 699d82 27 m Port Hospital FALSE si si ## 3 cb7ca3 14 m Mitilary Hospital TRUE si si ## 4 26d162 31 m Port Hospital TRUE si si ## 5 26d162 31 m Port TRUE si si ## 6 2ebf95 36 m Port FALSE no si ``` --- ## `recode()` para ediciones manuales Utiliza `recode()` dentro de `mutate()` para *re-codificación* manual o directa ```r vig_bruta %>% mutate(hospital = recode(hospital, # for reference: OLD = NEW "Other" = "Military Hospital", "Port" = "Port Hospital", "Port Hopital" = "Port Hospital", "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) ``` ``` ## id_caso edad sexo hospital lab_conferma epilink fiebre ## 1 86340d 1 f Port Hospital TRUE si si ## 2 699d82 27 m Port Hospital FALSE si si ## 3 cb7ca3 14 m Mitilary Hospital TRUE si si ## 4 26d162 31 m Port Hospital TRUE si si ## 5 26d162 31 m Port Hospital TRUE si si ## 6 2ebf95 36 m Port Hospital FALSE no si ``` --- ## Uso de `ifelse()` para una re-codificiación con lógica simple `ifelse()` dentro de `mutate()` comprueba lógicamente cada fila. Crea una nueva columna llamada `age_group`: - "adulto", si la evaluación da VERDADERA - "menor", si la evaluación es FALSA ```r vig_bruta %>% mutate(age_group = ifelse( test = edad >= 18, #evaluación yes = "adulto", #Si es verdadera no = "menor")) #si es falsa ``` ``` ## id_caso edad sexo hospital lab_conferma epilink fiebre age_group ## 1 86340d 1 f Port Hospital TRUE si si menor ## 2 699d82 27 m Port Hospital FALSE si si adulto ## 3 cb7ca3 14 m Mitilary Hospital TRUE si si menor ## 4 26d162 31 m Port Hospital TRUE si si adulto ## 5 26d162 31 m Port TRUE si si adulto ## 6 2ebf95 36 m Port FALSE no si adulto ``` --- ## Nombres y orden de los argumentos Si los argumentos se escriben en el orden predeterminado según la documentación de la función, no es necesario escribir explícitamente sus nombres. Los argumentos de `ifelse()` son `ifelse(test = , yes = , no = )` Este comando: ```r vig_bruta %>% mutate(age_group = ifelse( test = edad >= 18, yes = "adulto", no = "menor")) ``` También puede escribirse como ```r vig_bruta %>% mutate(age_group = ifelse(edad >= 18, "adulto", "menor")) ``` .nota[¿Cuáles son sus ventajas e inconvenientes?] --- ## Re-codificación con lógica compleja Utiliza la función **`case_when()`** en `mutate()` para comprobar una serie de criterios lógicos en cada fila y asignar el nuevo valor correspondiente. <h4>La sintaxis case_when() es la siguiente: criterios logicos para la fila <span style="color:deeppink">~</span> resultado si se satisface</h4> En **orden importa**. Escribe los criterios más específicos en la parte superior del comando (para que se activen primero) y los criterios más generales en la parte inferior. --- class: inverse, center, middle # Datos ordenados ## Reflexiones para el final de este ejercicio --- # Datos desordenados A menudo, los datos brutos de salud pública tienen este aspecto: <img src="../../images/messy_data.png" width="847" /> ¿Qué dificultades ves para manejar estos datos en R? --- # Datos ordenados Los datos limpios también deben estar "ordenados". A continuación se exponen 3 principios básicos de los "datos ordenados": -- 1. Cada **valor** debe tener su propio **celda** -- 2. Cada **variable** debe tener su propia **columna** -- 3. Cada **observación** debe tener su propia **fila** .footnote[Fuente : [R para la Ciencia de Datos](https://r4ds.had.co.nz/tidy-datos.html) ] ??? --- # Valores en su propia celda .pull-left[ **Desordenado:** ¿Qué cambios harían que este conjunto de datos estuviera más ordenado? |case |edad | |:------|:---------| |Case 1 |31, anios | |Case 2 |24, anios | |Case 3 |18, meses | |Case 4 |33, anios | ] -- .pull-right[ **Ordenada:** La edad y la unidad de edad están separadas en celdas distintas |case | edad|unidad_edad | |:------|----:|:-----------| |Case 1 | 31|anios | |Case 2 | 24|anios | |Case 3 | 18|meses | |Case 4 | 33|anios | ] ??? Empecemos por una fácil... --- # Terminología Estructuralmente, los "dataframes" (base de datos) en R consisten en **columnas** y **filas**. -- Sin embargo, **"variables"** y **"observaciones** son más *abstractas* conceptos: - **Variables** medida uno *atributo subyacente* (edad, resultado o fecha de inicio) - **Observaciones** se refieren a una *unidad de análisis* -- Idealmente, se alinean: **columnas = variables** y **filas = observacion** <img src="../../images/data_cleaning/tidy_image_es.png" width="746" height="65%" /> .footnote[Fuente de la imagen: [R para la Ciencia de Datos](https://r4ds.had.co.nz/tidy-datos.html)] --- # Pero no siempre está claro... .pull-left[ **Datos no ordenados** |País | Enero| Febrero| Marzo| |:------------|-----:|-------:|-----:| |Mozambique | 3200| 3300| 4100| |Lesotho | 500| 750| 900| |South Africa | 5100| 6200| 8100| - ¿Es "Enero" una variable? - ¿Dónde está la variable "mes"? - ¿Tiene su propia columna? - ¿Cada observación es una fila? ] -- .pull-right[ **Datos ordenados** |País |Mes | casos| |:------------|:-------|-----:| |Mozambique |Enero | 3200| |Mozambique |Febrero | 3300| |Mozambique |Marzo | 4100| |Lesotho |Enero | 500| |Lesotho |Febrero | 750| |Lesotho |Marzo | 900| |South Africa |Enero | 5100| |South Africa |Febrero | 6200| |South Africa |Marzo | 8100| ] --- class: inverse, center, middle ## Ejercicio Ir al sitio web del curso Abre el ejercicio del Módulo 3 e inicia sesión Sigue las instrucciones para abrir tu proyecto R "ebola" y continuar codificando Avisa a un instructor si no estás seguro de lo que tienes que hacer <img src="../../images/breakout/COVID dominoes.png" width="100%" />