# load packages ---------------------------------------------------------------- library(introexercises) # get datos for exercises library(learnr) # create lessons from rmd library(gradethis) # evaluate exercises library(dplyr) # wrangle datos library(stringr) # strings library(flair) # highlight code library(ggplot2) # visualise datos library(lubridate) # work with dates library(epikit) # for emojis library(skimr) # browse datos library(fontawesome) # for emojis library(janitor) # limpios datos # library(RMariaDB) # connect to sql database ## set options for exercises and checking --------------------------------------- ## Define how exercises are evaluated gradethis::gradethis_setup( ## note: the below arguments are passed to learnr::tutorial_options ## set the maximum execution time limit in seconds exercise.timelimit = 60, ## set how exercises should be checked (defaults to NULL - individually defined) # exercise.checker = gradethis::grade_learnr ## set whether to pre-evaluate exercises (so users see answers) exercise.eval = FALSE ) # ## event recorder --------------------------------------------------------------- # ## see for details: # ## https://pkgs.rstudio.com/learnr/articles/publishing.html#events # ## https://github.com/dtkaplan/submitr/blob/master/R/make_a_recorder.R # # ## connect to your sql database # sqldtbase <- dbConnect(RMariaDB::MariaDB(), # user = Sys.getenv("userid"), # password = Sys.getenv("pwd"), # dbname = 'excersize_log', # host = "144.126.246.140") # # # ## define a function to collect datos # ## note that tutorial_id is defined in YAML # ## you could set the tutorial_version too (by specifying version:) but use package version instead # recorder_function <- function(tutorial_id, tutorial_version, user_id, event, datos) { # # ## define a sql query # ## first bracket defines variable names # ## values bracket defines what goes in each variable # event_log <- paste("INSERT INTO responses ( # tutorial_id, # tutorial_version, # date_time, # user_id, # event, # section, # label, # question, # answer, # code, # correct) # VALUES('", tutorial_id, "', # '", tutorial_version, "', # '", format(Sys.time(), "%Y-%M%-%D %H:%M:%S %Z"), "', # '", Sys.getenv("SHINYPROXY_PROXY_ID"), "', # '", event, "', # '", datos$section, "', # '", datos$label, "', # '", paste0('"', datos$question, '"'), "', # '", paste0('"', datos$answer, '"'), "', # '", paste0('"', datos$code, '"'), "', # '", datos$correct, "')", # sep = '') # # # Execute the query on the sqldtbase that we connected to above # rsInsert <- dbSendQuery(sqldtbase, event_log) # # } # # options(tutorial.event_recorder = recorder_function)
# hide non-exercise code chunks ------------------------------------------------ knitr::opts_chunk$set(echo = FALSE)
# datos prep -------------------------------------------------------------------- vig_bruta <- rio::import(system.file("dat/listado_vigilancia_20141201.csv", package = "introexercises")) pruebas <- rio::import(system.file("dat/datos_prueba.csv", package = "introexercises"))
Bienvenido/a al curso "Introducción a R para epidemiología aplicada", ofrecido por Applied Epi, una organización sin ánimo de lucro y el principal proveedor de formación, apoyo y herramientas de R para profesionales de primera línea en salud pública.
knitr::include_graphics("images/logo.png", error = F)
Este ejercicio se centra en la limpieza de datos, incluyendo filtrado de filas, selección de columnas, eliminación de filas duplicadas, creación de columnas nuevas, creación de variables categóricas y limpieza de datos.
Importarás y limpiarás un listado de un brote ficticio de ébola.
Este ejercicio te guía a través de las tareas que debes realizar en RStudio en tu ordenador.
Hay varias formas de obtener ayuda:
1) Busca la sección de ayuda (ver más abajo) 2) Pide ayuda a tu instructor/a o facilitador/a de tu curso en directo 3) Programa una llamada 1 a 1 con un/a instructor/a para "Tutoría del curso". 4) Publica una pregunta en la Comunidad de Applied Epi
Este es el aspecto que tendrán esa sección de ayuda:
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una pista
¡Aquí verás una pista útil!
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
vig %>% filter( edad > 25, distrito == "Bolo" )
Aquí tienes más explicaciones sobre por qué funciona la solución.
Responder a las preguntas del cuestionario te ayudará a comprender el material. Las respuestas no se registran.
Para practicar, responde a las siguientes preguntas:
quiz( question_radio("¿Cuándo debería mirar las secciones de ayuda en rojo?", answer("Tras intentar escribir el código yo mismo", correct = TRUE), answer("Antes de intentar escribir el código", correct = FALSE), correct = "Revisar el código de ejemplo después de intentar escribirlo usted mismo puede ayudarle a mejorar", incorrect = "Por favor, intente completar el ejercicio usted mismo, o use las pistas disponibles, antes de mirar la respuesta." ) )
question_numeric( "¿Qué tan preocupado/a estás ante la idea de empezar este tutorial, en una escala de 1 (nada nervioso) a 10 (extremadamente preocupado/a)?", answer(10, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T), answer(9, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T), answer(8, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T), answer(7, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T), answer(6, message = "De acuerdo, lo conseguiremos juntos", correct = T), answer(5, message = "De acuerdo, lo conseguiremos juntos", correct = T), answer(4, message = "¡Me gusta tu confianza!", correct = T), answer(3, message = "¡Me gusta tu confianza!", correct = T), answer(2, message = "¡Me gusta tu confianza!", correct = T), answer(1, message = "¡Me gusta tu confianza!", correct = T), allow_retry = TRUE, correct = "Gracias por responder", min = 1, max = 10, step = 1 )
Por favor, envía un correo electrónico a contact@appliedepi.org si tienes preguntas sobre el uso de estos materiales.
En los próximos módulos, trabajaremos con datos de un brote ficticio de ébola.
Crea un nuevo proyecto de RStudio dentro de la sub-carpeta "ebola".
Se trata de un brote y un análisis distintos, por lo que merece su propio proyecto (un entorno de R autónomo y portátil con datos, scripts y resultados). Si no recuerdas cómo crear un nuevo proyecto, consulta la sección de ayuda que aparece a continuación
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
1) Abre RStudio (asegúrate de abrir RStudio y no sólo R).
2) En RStudio pulsa Archivo -> Nuevo Proyecto. En la ventana emergente, selecciona "Directorio existente".
knitr::include_graphics("images/create_project.png")
3) Crea el proyecto en la sub-carpeta "intro_curso/ebola"
Confirma que el nombre del proyecto en la esquina superior derecha de la sesión actual de RStudio es "ebola". Si no estás en ningún proyecto, se leerá "Proyecto: (Ninguno)".
# adding xfun::relative_path() creates a dynamic file path between the Rmd location and the here() path. # It dynamically creates the ../../etc filepath. knitr::include_graphics("images/ebola_project_dropdown.png", error = F)
Tu proyecto "ebola" de RStudio debería tener las siguientes sub-carpetas:
# adding xfun::relative_path() creates a dynamic file path between the Rmd location and the here() path. # It dynamically creates the ../../etc filepath. knitr::include_graphics("images/ebola_project_home.png", error = F)
Normalmente, los proyectos de análisis en salud pública contendrán sub-carpetas tales como:
Así es como debería verse la estructura de tus carpetas:
r emo::ji("folder")
Escritorior emo::ji("folder")
intro_cursor emo::ji("folder")
modulo1r emo::ji("document")
modulo1_script.Rr emo::ji("folder")
covidr emo::ji("folder")
ebolar emo::ji("folder")
datosr emo::ji("folder")
limpiosr emo::ji("folder")
brutosr emo::ji("folder")
pobr emo::ji("folder")
shpr emo::ji("folder")
scriptsr emo::ji("folder")
ejemplosr emo::ji("folder")
salidasr emo::ji("warning")
Puede que también tengas otros archivos como ".Rhistory". No los elimines, déjalos.
Abre un nuevo script de R y guárdalo en la sub-carpeta "ebola/scripts", con el nombre "analisis_ebola". Puedes abrir un nuevo script haciendo clic en Archivo -> Nuevo Archivo -> Script. Éste será el script de R principal para tu trabajo en las próximas sesiones del curso.
¡A codificar! Añade un encabezado de sección en la parte superior llamado "Acerca de este script". Para ello, coloca el cursor donde deba empezar la nueva sección y pulsa Ctrl, Mayús y R al mismo tiempo (o Cmd, Mayús y R en un ordenador Mac). En la ventana emergente, nombra la sección "Acerca de este script".
El nuevo encabezado debería tener este aspecto
# Acerca de este script ----------------------------------------------
Siguiente, utiliza # para escribir algunas líneas de comentarios debajo del título para documentar:
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy
Podrás navegar entre los encabezados de sección utilizando el botón "Esquema" situado en la parte superior derecha del script de R.
La organización del script es MUY importante. Recuerda que un script suele ejecutarse de arriba hacia abajo. Por tanto, tendrás que organizar tu guión de forma lógica.
Lo normal es que empieces cargando paquetes y luego continúes con la importación de datos. Recuerda que sólo queremos un pacman::p_load()
cerca de la parte superior del script para instalar/ cargar paquetes.
El primer comando de R de cualquier script suele estar destinado a instalar y cargar los paquetes para todo el script.
Añade un nuevo encabezado de sección llamado "Cargar paquetes".
Tu secuencia de comandos debería ser algo parecido a esto
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy # Cargar paquetes ----------------------------------------------
Ahora, escribe un comando utilizando {pacman} y su función p_load()
que instalará/ cargará los siguientes paquetes:
Escribe tu comando verticalmente para que puedas añadir breves comentarios sobre cada paquete. No olvides escribir pacman::p_load()
, un estilo sintáctico especial que también carga el paquete {pacman}.
Intenta escribir primero este comando por ti mismo/a y luego comprueba el código en la solución que aparece a continuación.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Cargar paquetes ---------------------------------------------- # El paquete pacman instalará cada paquete de ser necesario y los cargará para # que se puedan utilizar en esta sesión pacman::p_load( rio, # para importar datos here, # para localizar archivos usando rutas relativas skimr, # para revisar los datos janitor, # para limpiar los datos epikit, # para crear catgegorías de edad tidyverse # para la gestión y visualización de datos )
Trabajaremos con un "listado de casos" donde cada fila representa un caso de ébola durante un brote. Está almacenado como un archivo CSV en la carpeta "ebola/datos/brutos/", con el nombre "listado_vigilancia_20141201.csv".
Nombraremos la base de datos importada en R como vig_bruta
como abreviatura de "listado de vigilancia en bruto".
Añade una nueva sección titulada "Importar datos".
Ahora tu script debería ser algo parecido a esto
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy # Cargar paquetes ---------------------------------------------- # El paquete pacman instalará cada paquete de ser necesario y los cargará para # que se puedan utilizar en esta sesión pacman::p_load( rio, # para importar datos here, # para localizar archivos usando rutas relativas skimr, # para revisar los datos janitor, # para limpiar los datos epikit, # para crear catgegorías de edad tidyverse # para la gestión y visualización de datos ) # Importar datos ----------------------------------------------
El comando para importar los datos de Ébola diferirá del comando de importación del Módulo 1 porque los datos se guardan en la sub-carpeta "ebola/datos/brutos" y no en la carpeta "raíz" del proyecto.
Normalmente se aconseja guardar los datos en sub-carpetas, quizás por estado de procesamiento como "brutos" o "limpios", o por periodo de tiempo si los datos se actualizan rutinariamente.
Cualquier función import()
debe conocer dónde encontrar el archivo a importar: a esto se llama la ruta del archivo.
Un comando de importación con una ruta de archivo "absoluta" o "fija" podría parecerse al comando siguiente.
NO AÑADAS ESTE CÓDIGO EN TU SCRIPT
vig_bruta <- import("C:/Users/Laura/Documents/intro_course/ebola/datos/brutos/listado_vigilancia_20141201.csv")
¿Cuál es el problema? Recuerda que un proyecto de RStudio puede transferirse a otro/a compañero/a para que ejecute el script en su ordenador... pero la mayor parte de esta ruta de archivos puede no existir en el ordenador de tu colega (por ejemplo, C:, "Laura", "Documentos", etc.), ¡y este comando no se ejecutaría!
El uso de un proyecto de RStudio nos permite iniciar la ruta de archivos en la raíz del proyecto "ebola". Por ejemplo, este comando funcionaría para importar los datos en la mayoría de los ordenadores:
vig_bruta <- import("datos/brutos/listado_vigilancia_20141201.csv")
Pero esto sigue sin ser óptimo.
Sugerimos utilizar la función here()
para escribir la ruta del archivo. Esto tiene las siguientes ventajas:
1) Evita complicaciones debidas a la dirección de la barra
2) Facilita mucho la importación de datos de sub-carpetas al escribir informes automatizados ("R Markdown"), que trataremos más adelante en el curso
La siguiente solución resuelve ambos problemas
Recomendamos utilizar here()
para construir rutas de archivos más flexibles. Asegúrate de haber instalado y cargado el paquete {here} añadiéndolo a tu pacman::p_load()
y volviéndolo a ejecutar.
Ejecuta el comando here()
en tu script, dejando los paréntesis vacíos.
here()
¿Qué ves?
here()
imprime automáticamente tu ruta completa del archivo hasta la carpeta del proyecto RStudio "ebola". Tu salida debería coincidir con el directorio de tu ordenador.
En el ordenador de ejemplo de Laura, la salida de here()
impresa en su Consola es:
"C:/Users/Laura/Documents/intro_course/ebola"
Añade sub-carpetas al final de esta ruta entre comillas y separadas por comas, así
here("datos", "brutos")
Para Laura, esto se imprimiría:
"C:/Users/Laura/Documents/intro_course/ebola/datos/brutos"
Termina tu función here()
con el nombre y la extensión del archivo de la base de datos así:
here("datos", "brutos", "listado_vigilancia_20141201.csv")
En el ordenador de Laura, se imprimiría:
"C:/Users/Laura/Documents/intro_course/ebola/datos/brutos/listado_vigilancia_20141201.csv"
La salida de la función here()
es la ruta completa a los datos desde la raíz del proyecto de RStudio, adaptada al ordenador del usuario. Es importante destacar que esto también funciona para los informes automatizados.
here()
e import()
juntosAhora viene la combinación secreta: introduce el comando here()
en la función import()
¡"Anida" una función dentro de la otra!
# Importar el archivo de las sub-carpetas "datos" y "brutos" vig_bruta <- import(here("datos", "brutos", "listado_vigilancia_20141201.csv"))
Observa los dos paréntesis de cierre al final del comando, uno para cada función.
La ruta del archivo producida por here()
se proporciona como un valor nominal al primer argumento de import()
. Esto indica import()
dónde encontrar la base de datos.
En tu script, escribe un comando con import()
y here()
que importe el "listado_vigilancia_20141201.csv" de la sub-carpeta "datos/brutos" del proyecto de R "ebola". Nombra el objeto vig_bruta
(abreviatura de "listado vigilancia brutos") utilizando el operador de asignación <-
.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Importar datos ---------------------------------------------- # Importar datos brutos del brote de ébola vig_bruta <- import(here("datos", "brutos", "listado_vigilancia_20141201.csv"))
¡Acuérdate de limpiar tu guión a medida que vayas haciendo los ejercicios!
Tu script debería tener ahora este aspecto:
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy # Cargar paquetes ---------------------------------------------- # El paquete pacman instalará cada paquete de ser necesario y los cargará para # que se puedan utilizar en esta sesión pacman::p_load( rio, # para importar datos here, # para localizar archivos usando rutas relativas skimr, # para revisar los datos janitor, # para limpiar los datos epikit, # para crear catgegorías de edad tidyverse # para la gestión y visualización de datos ) # Importar datos ---------------------------------------------- # Importar datos brutos del brote de ébola vig_bruta <- import(here("datos", "brutos", "listado_vigilancia_20141201.csv"))
NOTA: aunque hayas escrito algunas pruebas de here()
en tu script, debes eliminarlos o desactivarlos colocando un símbolo # a su izquierda.
A medida que desarrolles tu script "Análisis del brote de ébola", intenta mantener sólo los comandos que sean necesarios para el análisis. Los demás comandos pueden ir a un "Área de pruebas" en la parte inferior del script, o desactivarse.
Primero vamos a ejecutar un código para buscar elementos en la base de datos que puedan requerir limpieza.
Añade una nueva sección titulada "Análisis exploratorio", debajo de la sección "Importar datos".
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy # Cargar paquetes ---------------------------------------------- # El paquete pacman instalará cada paquete de ser necesario y los cargará para # que se puedan utilizar en esta sesión pacman::p_load( rio, # para importar datos here, # para localizar archivos usando rutas relativas skimr, # para revisar los datos janitor, # para limpiar los datos epikit, # para crear catgegorías de edad tidyverse # para la gestión y visualización de datos ) # Importar datos ---------------------------------------------- # Importar datos brutos del brote de ébola vig_bruta <- import(here("datos", "brutos", "listado_vigilancia_20141201.csv")) # Análisis exploratorio -----------------------------------------
En las próximas secciones, verás comandos desconocidos que utilizan funciones como tabyl()
y ggplot()
.
Por ahora, basta con copiar y pegar el código y ejecutarlo para ver los resultados. Aprenderás más sobre estas funciones en los próximos módulos.
La función de {base}, names()
, devuelve los nombres de las columnas de una base de datos. Escribe el nombre de la base de datos dentro de la función, y visualiza el resultado en la Consola.
names(vig_bruta)
Recuerda que en el Módulo 1, todos los nombres de las columnas estaban normalizados, sin espacios ni caracteres especiales. ¡Este no es el caso de los datos sobre el ébola!
quiz(caption = "Cuestionario - nombres de columnas", question("¿Cuál de los nombres de columnas contiene una sintáxis no estándar y que requiere limpieza?", allow_retry = T, answer("fecha inicio sintomas", correct = T, message = "Esta contiene un espacio"), answer("peso (kg)", correct = T, message = "Se tienen que remover los espacios y los paréntesis"), answer("hospital", message = "Se tienen que remover los espacios"), answer("unidad edad", correct = T, message = "Se tienen que remover los espacios"), answer("alt (cm)", correct = T, message = "Se tienen que remover los espacios y los parentésis"), answer("fecha de notifica", correct = T, message = "Se tienen que remover los espacios") ) )
El paquete {janitor} y su función tabyl()
pueden utilizarse para hacer una tabulación rápida de los valores de una columna determinada. Basta con escribir tabyl()
con el nombre de la base de datos como primer argumento y el nombre de la columna como segundo argumento.
Prueba ahora el siguiente código añadiéndolo a tu sección "Análisis exploratorio", y responde a la pregunta del cuestionario que aparece a continuación:
# Tabular por sexo tabyl(vig_bruta, sexo)
quiz(caption = "Cuestionario - Sexo", question_numeric( "¿Cuántas filas están codificadas como 'Desconocido' en la columna sexo?", answer(length(vig_bruta$sexo[vig_bruta$sexo == "Desconocido"]), correct = T), allow_retry = TRUE, correct = "Correcto, buen trabajo.", min = 1, max = 700, step = 1 ) )
Ten en cuenta que los valores perdidos se han codificado en la columna sexo
con la palabra "Desconocido". Los datos que faltan en R se codifican más adecuadamente como el valor especial NA
. Exploremos esto más a fondo...
Los valores que faltan en R deben representarse mediante un valor reservado (especial) NA
. Esto garantiza que R trate adecuadamente estos valores como ausentes. Esto es importante para determinadas funciones de limpieza y visualización de datos.
Ten en cuenta que NA
se escribe como valor especial sin comillas.
"NA" con comillas es sólo un valor nominal normal (y es una letra de los Beatles de la canción Hey Jude).
Podrás encontrar representada la ausencia de datos de otras formas: con el número 99, un punto, "Falta", "Desconocido", o celdas vacías. A menudo, las celdas vacías de un CSV o XLSX pueden importarse a R como un espacio en blanco: parecerán vacías, pero en realidad hay un espacio de texto como este " ", o incluso un texto sin espacio, así: "".
A continuación te mostramos una breve tabla para que veas cómo se ven estos escenarios.
is.na()
para evaluar si realmente falta algo en R (NA
) y devuelve TRUE
o FALSE
.Sólo el valor final de esta tabla será reconocido como un verdadero "valor perdido" por R.
# create the demo dataset ######################### demo_ausentes <- data.frame( `Como escrito en datos` = c('"Ausente"', '"Desconocido"', '" "', '""', '"."', '"NA"', NA), `Como sale en la columna` = c("Ausente", "Desconocido", " ", "", ".", "NA", NA)) %>% rename( `Como escrito en datos` = `Como.escrito.en.datos`, `Como sale en la columna` = `Como.sale.en.la.columna` ) %>% mutate(`Como lo considera is.na()` = is.na(`Como sale en la columna`)) # # demo_missing <- tribble( # # ~`As written in datos`, ~`As displayed in column`, # '"Missing"', "Missing", # '"Desconocido"', "Desconocido", # '" "', " ", # '""', "", # NA, NA) %>% # # mutate(`As assessed by is.na()` = is.na(`As displayed in column`)) demo_ausentes
Al importar datos desde Excel, a veces puedes tener un valor de texto que parecerá vacío pero que en realidad se referenciaría en R como el siguiente carácter "", sin espacio. Por ejemplo, el código siguiente produce una tabla de los recuentos de valores únicos en la columna fiebre
.
# Tabular fiebre en la base de datos tabyl(vig_bruta, fiebre)
Puedes ver los recuentos de "no", "sí", y de un tercer valor valor representado como un espacio vacío. Esto no se reconoce correctamente como NA
por R, así que tendremos que recodificar este valor en nuestra limpieza.
Revisemos la distribución de la edad utilizando el paquete {ggplot2} que es el más común para la visualización de datos.
No te preocupes ahora por entender este comando: tendremos dos módulos enteros que cubren {ggplot2}. La función ggplot()
utiliza nuestra base de datos vig_bruta
y asigna la columna edad
al eje-x para visualizar los datos como un histograma.
Copia y pega el siguiente código en tu script, en la sección "Análisis exploratorio".
# Histograma de casos por edad ggplot(data = vig_bruta, mapping = aes(x = edad))+ geom_histogram()
Puede aparecer un mensaje de advertencia cuando ejecutes este comando. Un mensaje de "Advertencia" en la Consola significa que R es capaz de ejecutar tu comando, pero hay algo que probablemente deberías saber. Por el contrario, un mensaje de "Error" significa que R no ha podido ejecutar tu comando.
A continuación, el comando se modifica sólo ligeramente, para mostrar un boxplot en lugar de un histograma.
**Copia y pega el código siguiente en tu script de la sección "Análisis exploratorio", ejecútalo en tu consola y responde a la pregunta que sigue: **
# Boxplot de la distribución de etaria en los casos ggplot(data = vig_bruta, mapping = aes(x = edad))+ geom_boxplot()
quiz(caption = "Cuestionario - Distribución etaria", question("La mediana de la edad está:", allow_retry = T, answer("Debajo de 20", correct = T, message = ""), answer("Encima de 20", message = "La mediana de la edad se puede encontrar en el boxplot") ) )
¡ESPERA! Puede que te hayas dado cuenta de algo importante si has mirado las otras columnas. Algunos de los casos han registrado la edad en meses ¡en lugar de en años!
Podemos utilizar la función tabyl()
del paquete {janitor} para resumir rápidamente los valores de unidad edad
de nuestro objeto vig_bruta
. Añade este código a tu sección de "Análisis exploratorio" y ejecútalo:
tabyl(vig_bruta, `unidad edad`)
Observa que tuvimos que escribir el nombre de la columna rodeado de tildes inversas porque tiene un espacio.. Limpiar el nombre de esta columna para eliminar los espacios será una de nuestras primeras acciones de limpieza.
Los espacios NO son comillas simples. Observa la diferencia:
En los teclados de EE.UU. y el Reino Unido, la tecla de la tilde invertida suele estar cerca de la tecla ESC, 1 o ~ (para otras distribuciones de teclado, consulta esta guía.
quiz(caption = "Cuestionario - Unidad de edad", question_numeric( "¿Cuántas filas tienen la edad registrada en meses?", answer(vig_bruta %>% filter(`unidad edad` == "meses") %>% nrow(), message = "", correct = T), allow_retry = TRUE, correct = "Correcto, buen trabajo.", min = 1, max = 700, step = 1 ) )
Al "limpiar" una columna de fecha en R, la primera preocupación es qué "clase" se ha asignado por defecto a la columna. Recuerda que la "clase" representa qué tipo de valores contiene cada columna. Por ejemplo para edad
lo ideal sería que la columna fuera de "clase" numeric
, mientras la columna distrito
sería idealmente de "clase" character
(nominal o texto).
La función class()
de {base} devuelve la clase de un objeto.
Observa la clase del objeto vig_bruta
en nuestra lista. **Añade el siguiente código a tu sección de "Análisis exploratorio" y ejecútalo. **
class(vig_bruta)
Nuestra base de datos es de clase "base de datos".
A continuación, inspeccionamos la clase de la columna edad
. Utiliza el operador $
para especificar que dentro de la base de datos vig_bruta
quieres examinar la columna edad
.
Escribe este comando letra a letra. Cuando escribas el operador $, deberías ver aparecer una lista de todas las columnas de la base de datos (se trata de una función de autocompletar, que se puede ajustar en RStudio -> Herramientas -> Opciones).
Añade el código siguiente a tu sección de "Análisis exploratorio" y ejecútalo.
class(vig_bruta$edad)
Añade el siguiente código a tu sección de "Análisis exploratorio" y ejecútalo.
class(vig_bruta$`fecha inicio sintomas`)
Fíjate que nuevamente son necesarias las tildes invertidas porque el nombre de esta columna contiene espacios. ¡Tendremos que arreglarlo pronto!
Durante la importación de los datos, R interpretó a esta columna como una de valores nominales (por ejemplo, "11/03/2014") y no supuso que se trataba de fechas. De hecho, ¿cómo iba a saber R qué dígitos se referían a meses o a días? El valor "11/03/2014" podría ser el 3 de noviembre, ¡o el 11 de marzo!
A efectos de análisis, es esencial que esta columna se reconozca adecuadamente como clase "fecha" y se identifiquen correctamente los días y los meses. Nos ocuparemos de esto en los pasos de limpieza.
Ahora prueba esto por tu cuenta para ver la "clase" de la columna fecha de notifica
. Utiliza la función class()
igual que antes para ver la columna fecha de notifica
. Recuerda que, como hay espacios en el nombre de la columna, tienes que utilizar tildes invertidas para hacer referencia a la columna en tu código de R.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
class(vig_bruta$`fecha de notifica`)
quiz(caption = "Cuestionario - Clase de columnas", question("¿Cuál es la clase de la columna `fecha de notifica`?", allow_retry = TRUE, answer("Numeric"), answer("Character", correct = T), answer("Proletariat"), answer("Logical"), answer("Date") ) )
Podemos utilizar histogramas para examinar otras columnas numéricas como el peso, la altura, la temperatura, etc.
¿Parece que falta algo? ¿Hay valores que limpiar? ¿Valores imposibles?
Copia y pega el código siguiente en tu sección de "Análisis exploratorio" y ejecútalo para visualizar la distribución de la variable peso:
ggplot(data = vig_bruta, mapping = aes(x = `peso (kg)`))+ geom_histogram()
Copia y pega este código en tu sección de "Análisis exploratorio" y ejecútalo para visualizar la distribución de la variable altura:
ggplot(data = vig_bruta, mapping = aes(x = `alt (cm)`))+ geom_histogram()
Fíjate de nuevo en la necesidad de usar tildes invertidas porque los nombres de las columnas de altura y peso contienen espacios.
Hay dos columnas en la base de datos que informan el distrito del caso:
adm3_nombre_res
- distrito de residencia del casoadm3_nombre_not
- distrito de detección del caso¿En qué medida se alinean? Podemos hacer una tabulación cruzada:
Copia y pega el siguiente código en tu sección de "Análisis exploratorio y ejecútalo. La columna de la primera lista (distrito de residencia) aparecerá en las filas, y el distrito de detección aparecerá en las columnas.
tabyl(vig_bruta, adm3_nombre_res, adm3_nombre_not)
quiz(caption = "Cuestionario - Residencia", question("¿Cuántos casos han sido residentes del distrito Mountain Rural pero que fueron detectados en Central II?", allow_retry = T, answer("12", message = "Lugar de residencia se encuentra en las filas y de detección en las columnas"), answer("4", message = "Lugar de residencia se encuentra en las filas y de detección en las columnas"), answer(vig_bruta %>% filter(adm3_nombre_res == "Mountain Rural" & adm3_nombre_not == "Central II") %>% nrow(), correct = T, message = "Lugar de residencia se encuentra en las filas y de detección en las columnas"), answer("209", message = "Lugar de residencia se encuentra en las filas y de detección en las columnas") ) )
Observa la primera fila y la última columna de la tabulación: parece que, de nuevo, los valores omitidos se importaron a R como un espacio de texto en blanco. ¡Esto puede ser un problema en todas las columnas de clase "nominal"! No te preocupes, lo solucionaremos cuando limpiemos los datos.
r fontawesome::fa("exclamation", fill = "red")
¿Has guardado tu guión últimamente? Si no es así, asegúrate de guardarlo ahora antes de seguir adelante.
Ahora tu script debería ser algo parecido a esto
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy # Cargar paquetes ---------------------------------------------- # El paquete pacman instalará cada paquete de ser necesario y los cargará para # que se puedan utilizar en esta sesión pacman::p_load( rio, # para importar datos here, # para localizar archivos usando rutas relativas skimr, # para revisar los datos janitor, # para limpiar los datos epikit, # para crear catgegorías de edad tidyverse # para la gestión y visualización de datos ) # Importar datos ---------------------------------------------- # Importar datos brutos del brote de ébola vig_bruta <- import(here("datos", "brutos", "listado_vigilancia_20141201.csv")) # Análisis exploratorio ----------------------------------------- # Imprimir los nombres de las columnas names(vig_bruta) # Imprimir la tabulación de la columna sexo tabyl(vig_bruta, sexo) # Imprimir la tabulación de la columna fiebre tabyl(vig_bruta, fiebre) # Crear histograma de la columna edad ggplot(data = vig_bruta, mapping = aes(x = edad))+ geom_histogram() # Crear un boxplot de la columna edad ggplot(data = vig_bruta, mapping = aes(x = edad))+ geom_boxplot() # Imprimir la tabulación de la columna `unidad edad` tabyl(vig_bruta, `unidad edad`) # Imprimir la classe de la base de datos class(vig_bruta) # Imprimir la clase de la columna edad class(vig_bruta$edad) # Imprimir la clase de la columna `fecha inicio sintomas` class(vig_bruta$`fecha inicio sintomas`) # Imprimir la clase de la columna `fecha de notifica` class(vig_bruta$`fecha de notifica`) # Crear histograma de la columna `peso (kg)` ggplot(data = vig_bruta, mapping = aes(x = `peso (kg)`))+ geom_histogram() # Crear histograma de la columna `alt (cm)` ggplot(data = vig_bruta, mapping = aes(x = `alt (cm)`))+ geom_histogram() # Imprimir cros-tabulación de las columnas de ubicación tabyl(vig_bruta, adm3_nombre_res, adm3_nombre_not)
NOTA: ¿Has ido añadiendo comentarios a medida que trabajabas? Recuerda que esto puede explicarte a ti mismo y a los demás lo que hace tu código y por qué.
Un consejo avanzado: Para "minimizar" o "plegar" toda la sección de Análisis Exploratorio, haz clic en la pequeña flecha gris situada a la izquierda del encabezamiento de la sección (# Análisis exploratorio). Esto colapsa la sección y proporciona un botón morado para expandirla más tarde.
Ahora que hemos explorado la base de datos de vigilancia en bruto, puedes empezar a construir un comando que le aplique una serie de acciones de limpieza y produzca una versión "limpia".
%>%
Una secuencia de limpieza de varios pasos a veces se llama "cadena de pipes" (o pipechain) porque el operador "pipe" %>%
se utiliza para enlazar o "encadenar" varias operaciones en un solo comando que alteran la base de datos original.
Puede ser útil pensar en el operador pipe %>%
en estas analogías:
Los programadores de R utilizan el operador pipe con tanta frecuencia que existe un atajo en el teclado. Deberías memorizarlo:
Windows/Linux: Pulsa Ctrl, Mayús y "m" a la vez
ordenador Mac: Pulsa Cmd, Mayúsculas y "m" al mismo tiempo
La "m" es probablemente una referencia al paquete {magrittr} que aloja la pipe. {magrittr} forma parte del tidyverse, por lo que no necesitas cargar este paquete por separado.
A partir de R 4.1.0, también existe una "pipe nativa" de {base} que tiene el siguiente aspecto |>
. Puedes cambiar tu RStudio para utilizar la pipe nativa entrando en Herramientas -> Opciones Globales -> Código, pero te recomendamos que utilices la opción {magrittr} para este curso. Si te interesa, puedes leer sobre las sutiles diferencias aquí.
Para empezar, añade una nueva sección a tu script, debajo de la sección de análisis exploratorio:
# Limpieza de datos de vigilancia -----------------------------------------
Recordatorio: pulsa Ctrl+Mayús+R (o CMD+Mayús+R en un mac) para crear rápidamente una nueva sección en el guión.
Es mejor guardar todos los pasos de limpieza en una sola secuencia conectada de comandos. Cada paso se enlaza mediante operadores "pipe %>%
. Construirás este comando paso a paso, añadiendo funciones al final. Sólo deberías escribir UN comando de limpieza.
NO escribas el código siguiente. Simplemente, visualízalo como un ejemplo. Éste es el comando que escribirás en las dos próximas sesiones. Fíjate en los operadores de pipes %>%
resaltados después de cada paso.
knitr::include_graphics("images/cleaning_pipe_chain.png", error = F)
Como todos los pasos están enlazados con operadores pipes %>%
, toda la secuencia de acciones se puede ejecutar de una sola vez.
Ten en cuenta que no hay ninguna pipe después de la última línea del comando.
La primera línea de un comando de limpieza de varios pasos empieza por:
1) El nombre de la base de datos "limpia" que quieres crear
2) El operador de asignación <-
3) El nombre de la base de datos "en bruto" del que partir
En efecto, esto hace una copia de los datos brutos y los guarda como vig
.
vig <- vig_bruta # hacer una copia de los datos brutos
Ejecuta este comando y observa vig
en tu Entorno. Haz clic en él para ver los valores. Es una copia exacta de vig_bruta
.
El siguiente paso es modificar vig_bruta
antes que se guarde como vig
.
Esto se hace añadiendo un operador de pipe, que "pasa" vig_bruta
a otra función que realice un cambio, como renombrar columnas, eliminar duplicados, recodificar valores o filtrar las filas por criterios lógicos.
¿Recuerdas lo molestos que son los espacios en los nombres de las columnas? Normalmente, la primera función de un comando de limpieza es normalizar los nombres de las columnas.
Empieza añadiendo una pipe después de vig_bruta
y en la línea siguiente escribe la función clean_names()
. No pongas nada dentro de los paréntesis.
vig_bruta
se pasa a (y es procesado por) clean_names()
antes de guardarlo como vig
limpio. clean_names()
normaliza los nombres de las columnas como minúsculas, con los espacios convertidos en guiones bajos y los caracteres especiales eliminados.
Añade clean_names()
a tu comando y vuelve a ejecutarlo.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Comando de limpieza vig <- vig_bruta %>% # la base de datos en bruto clean_names() # estandarizar los nombres de las columnas
Ver vig
pulsando sobre ella en RStudio. ¡Observa que algunos nombres de columnas han cambiado! Ahora hay una diferencia entre vig_bruta
y vig
.
quiz(caption = "Cuestionario - clean_names()", question("¿Cómo ha quedado escrita la columna 'alt (cm) luego de usar clean_names()?", allow_retry = T, answer("altura"), answer("alt_cm", correct = T), answer("ht_(cm)"), answer("HT-CM") ), question("¿Cuál es el nuevo nombre de la columna que contiene la fecha de inicio de los síntomas?", allow_retry = T, answer("fecha inicio sintomas"), answer("fecha-inicio-sintomas"), answer("fecha_inicio_sintomas", correct = T), answer("onset") ) )
Nunca dejes una pipe al final de tu comando. R asumirá que la orden no está completa y pueden producirse resultados inesperados.
Este es el principio de tu orden de limpieza, pero hay mucho más que hacer...
A continuación, realiza cambios manuales en los nombres de las columnas utilizando la función rename()
. Esta función es del paquete {dplyr} que has cargado dentro del mega-paquete {tidyverse}.
Algunas cosas a tener en cuenta sobre la sintaxis en rename()
:
Mira este ejemplo genérico. No ejecutes este código ya que no funcionará tal y como está escrito. Sustituiremos el OLDname
y NEWname
por los nombres de columna adecuados.
vig <- vig_bruta %>% # la base de datos en bruto clean_names() # estandarizar los nombres de las columnas rename( # hacer cambios manuales en los nombres de columnas NEWname = OLDname, # primer cambio NEWname = OLDname # segundo cambio ) # final de la función rename()
ATENCIÓN Piensa por un momento: ¿y si quisieras editar el nombre de la columna que contiene la fecha de inicio de los síntomas? rename()
función?
quiz(caption = "Cuestionario - rename()", question("En vig_bruta, ¿Cuál es el nombre de la columna que contiene la fecha de inicio de los síntomas?", allow_retry = T, answer("fecha_sintomas"), answer("fecha_inicio_sintomas"), answer("fecha inicio sintomas", correct = T), answer("fecha de inicio de los sintomas") ), question("Cuando vig_bruta se conecta con la función rename() usando el operado %>%, ¿Cuál es el nombre de la columna que contiene la fecha de inicio de los síntomas?", allow_retry = T, answer("fecha_sintomas"), answer("fecha_inicio_sintomas", correct = T), answer("fecha inicio sintomas"), answer("fecha de inicio de los sintomas") ) )
En vig_bruta
este nombre de columna contiene un espacio. Sin embargo, como conectamos el objeto vig_bruta
a través de clean_names()
antes de pasarla a rename()
sus nombres de columna tienen guiones bajos en lugar de espacios. Por tanto, el rename()
debe utilizar el nombre de columna con guión bajo.
Recuerda: una pipe toma la salida de una función y la pasa a la siguiente.
En tu comando de limpieza, añade la función rename()
después de clean_names()
y cambia los nombres de las columnas siguientes
Cambia las columnas de fecha para que "fecha" esté al principio de su nombre (esto será útil más adelante):
fecha_inicio_sintomas
a fecha_sintomas
fecha_de_notifica
a fecha_notifica
Simplificar los nombres de las columnas de ubicación para facilitar la escritura:
adm3_nombre_res
por distrito_res
adm3_nombre_not
a distrito_not
Finalmente, comprueba la ubicación de tus pipes. Mira la imagen de abajo y considera dónde deberías ver las pipes en el código (posición A,B,C, y/o D).
knitr::include_graphics("images/pipe_placement.png", error = F)
quiz(caption = "Cuestionario - Ubicación del operador pipe", question("Luego de observar la imagen de arriba, ¿qué ubicación debería tener el operador pipe? (Selecciona todas las que consideres apropiadas)", allow_retry = T, answer("A", correct = T), answer("B", correct = T), answer("C", message = "No debería haber un operador pipe dentro de una función."), answer("D", message = "No debería haber un operador pipe al final.") ) )
Vuelve a ejecutar el comando de limpieza para aplicar los cambios. Ver vig
para ver los nombres de columna actualizados.
Sólo deberías tener un (1) comando de limpieza que se esté "alargando". Debería parecerse a esto
# Comenzar comandos de limpieza vig <- vig_bruta %>% # datos en bruto # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not)
Ejecutar un comando de varios pasos con pipes puede tener sus matices. Elige la estrategia que mejor te funcione.
Asegúrate siempre de no tener una pipe de más al final de tu comando.
knitr::include_graphics("images/run_pipe_hang.png", error = F)
Puedes ejecutar el comando de limpieza completo resaltándolo -desde arriba- y pulsando "Ejecutar" (o utilizando un atajo de teclado para ejecutar)
knitr::include_graphics("images/run_highlight_top.png", error = F)
Ten cuidado de no saltarte el último paréntesis.
knitr::include_graphics("images/run_highlight_top_miss.png", error = F)
También puedes ejecutar todo el comando de limpieza colocando el cursor en la línea superior y pulsando "Ejecutar" (o utilizando un atajo de teclado para ejecutar)
knitr::include_graphics("images/run_cursor_top_ok.png", error = F)
Puedes colocar el cursor en cualquier parte de la secuencia y pulsar "Ejecutar", siempre que estén presentes todas las pipes necesarias.
knitr::include_graphics("images/run_cursor_middle_ok.png", error = F)
¡PERO! no puedes ejecutar una parte resaltada de tu comando sin la línea superior. Los comandos inferiores no funcionan a menos que la base de datos se introduzca en ellos desde el principio.
knitr::include_graphics("images/run_highlight_middle.png", error = F)
Si sólo ejecutas una parte del comando, empieza por arriba y no resaltes una pipe final Sólo se guardarán en la base de datos limpia los cambios que hayas resaltado.
knitr::include_graphics("images/run_highlight_some_ok.png", error = F)
Si accidentalmente resaltas y ejecutas con una "pipe colgando", se producirán resultados inesperados. Pulsa la tecla "esc" para reiniciar la Consola.
knitr::include_graphics("images/run_highlight_some_bad.png", error = F)
quiz(caption = "Cuestionario - Ejecutando el comando de limpieza", question("¿Cuáles de estos métodos funciona para ejecutar los comandos de limpieza? (Selecciona todos los que consideres apropiados)", allow_retry = T, answer("Resaltar el comando completo y luego hacer click en el botón 'ejecutar'", correct = T), answer("Resalta el comando completo y presionar Ctrl e Intro", correct = T), answer("Ubicar el cursor en cualquier parte de la secuencia del comnado y hacer click en el botón 'ejecutar'", correct = T), answer("Ubicar el cursor en cualquier parte de la secuencia del comnado y presionar Ctrl e Intro", correct = T) ), question("¿Cuáles de los siguientes son errores comunes que se cometen al ejecutar un comando de limpieza de múltiples pasos? (Selecciona todos los que consideres apropiados)", allow_retry = T, answer("Resaltar y ejecutar sin comenzar desde la primera línea del comando.", correct = T), answer("Ejecutar el comando en reversa.", correct = F), answer("Borrar el comando por accidente.", correct = F), answer("Resaltar un operador pipe extra por accidente", correct = T) ) )
Añade una nueva sección llamada "Área de pruebas" debajo de tu sección de "Limpieza de los datos de vigilancia". Se utilizará para ejecutar comandos que comprueben el impacto de tu código de limpieza.
# Área de pruebas -----------------------------------------
Piensa en esto como un "patio de recreo" para albergar comandos autónomos e independientes que no formen parte de tu comando de limpieza.
Por ejemplo, utiliza el comando names()
en tu "Área de pruebas" para revisar los nombres de columnas devig
y asegurarte de que los cambios hechos con rename()
han sido los correctos.
# Área de pruebas ----------------------------------------- names(vig_bruta) # columnas de los datos en bruto names(vig) # columnas de los datos limpios
En vig
, los nombres de las columnas ya no tienen espacios (sustituidos por guiones bajos) ni caracteres especiales como los paréntesis, ¡y están todos en minúsculas! Además, fíjate en los nombres de las columnas de fecha y distrito.
La función select()
es versátil y se utiliza a menudo para
El objetivo principal de select()
es seleccionar las columnas que se conservarán en la base de datos. Cualquier nombre de columna que no esté entre paréntesis se eliminará.
Practica el uso de select()
y ejecuta este comando en tu "Área de pruebas".
vig %>% select(epilink, edad, sexo)
quiz(caption = "Cuestionario - Select", question("¿Es esta función parte de tu comando de limpieza?", allow_retry = T, answer("No, está separada en el 'Área de pruebas'", correct = T), answer("Sí, porque he utilizado el operador pipe", correct = F, message = "No, solo porque utilices el operador pipe no signfica que sea parte de tu comando de limpieza. El operador pipe puede ser utilizado en múltiples circunstancias.") ), question("¿Es un comando de IMPRESIÓN o de GUARDADO?", allow_retry = T, answer("Impresión", correct = T, message = "Este es un comando de impresión porque no hay un operador de asignación presente. Los cambios no fueron guardados en la base de datos."), answer("Guardado", correct = F, message = "Este es un comando de impresión porque no hay un operador de asignación presente. Los cambios no fueron guardados en la base de datos.") ), question("¿Dónde ha aparecido la base de datos modificada?", allow_retry = T, answer("En el Entorno", correct = F, message = "No, los cambios no fueron guardados, por lo tanto no hay cambios en el Entorno."), answer("En la Consola", correct = T, message = "Debido a que los cambios no fueron guardados, los resultados fueron impresos en la Consola.") ) )
Observa cómo select()
también reordena las columnas en el orden en que las escribes.
select()
Puede ser agotador escribir individualmente cada columna que quieras conservar. Puedes utilizar dos puntos ( : ) para conservar dos columnas y todas las que estén ubicadas entre ellas.
Modifica el código de tu "Área de pruebas" como sigue:
# Conserva fiebre, vomito, y las columnas entre ellas vig %>% select(fiebre:vomito)
Sólo verás las columnas entre fiebre
y vomito
impresas.
Otro consejo es utilizar select()
para ajustar el orden de las columnas.
Escribe unas cuantas columnas al principio, y luego escribe everything()
que incluye todas las demás columnas (fíjate en los paréntesis vacíos al final de everything()
).
everything()
es una función de ayuda de "tidyselect". Otras son contains()
y starts_with()
(más información en este capítulo del Manual de Epi).
Modifica tu comando del Área de Pruebas para que sea como sigue:
# Usar everything() vig %>% select(fiebre, sexo, starts_with("fecha"), everything())
Para eliminar columnas concretas, puedes añadir un signo menos (-
) delante del nombre de una columna en la función select()
. El resto de las columnas se conservarán.
En el "Área de pruebas", practica utilizando el símbolo menos ( - ) para eliminar la columna epilink
. El resto de las columnas se mantienen.
vig %>% select(-epilink)
A continuación, actualiza el código para practicar la eliminación de varias columnas envolviendo todos sus nombres con c()
. Este método aplicará el signo negativo a todas ellas. Hemos aprendido la función {base} c()
en el módulo anterior.
# Remueve estas tres columnas vig %>% select(-c(epilink, distrito_not, edad)) # Una alternativa al código de arriba vig %>% select(-epilink, -distrito_not, -edad)
Una maniobra más avanzada es utilizar select()
¡para renombrar columnas! Si lo intentas, utiliza la misma sintaxis que rename()
pero recuerda que sólo conserva las columnas que incluyas en la función select()
.
select()
a la secuencia de limpiezaVale, ya has practicado bastante con select()
¡Ahora, añade un select()
a tu comando de limpieza!
1) Vuelve a la sección "Limpieza de los datos de vigilancia" de tu script.
2) Añade un paso a tu comando que elimine la columna num_fila
.
3) Vuelve a ejecutar el comando de limpieza para ver el efecto en vig
.
Si lo has olvidado, el atajo de teclado para un operador pipe (%>%
) es Ctrl, Mayús y m para Windows y CMD, Mayús y m para ordenador Mac.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
Ahora tu comando de limpieza debería tener este aspecto
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # Remueve la columna innecesaria select(-num_fila)
Recuerda que sólo debe haber un comando de limpieza. Añádele líneas adicionales a medida que completes este ejercicio.
Llegados a este punto, debemos comprobar si hay duplicados en nuestros datos. Esto puede hacerse con la función distinct()
.
Si distinct()
se ejecuta con los paréntesis vacíos, eliminará las filas que estén duplicadas al 100% (sólo conservará la primera fila de las que tengan exactamente los mismos valores en todas las columnas).
Añade la función distinct()
al comando de limpieza en la sección de "Limpieza de datos de vigilancia" de tu script, y vuelve a ejecutar tu comando de limpieza.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
Ahora tu comando de limpieza debería tener este aspecto
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct()
¿Se eliminó alguna fila?
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
Compara el número de filas en vig
con el número de filas de vig_bruta
(en el panel Entorno). ¿Cuántas filas (observaciones) hay en vig
en comparación con vig_bruta
?
distinct()
puede utilizarse de forma avanzada, como para tener en cuenta sólo determinadas columnas al evaluar los duplicados (por ejemplo, eliminar sólo las filas que tengan la misma edad, sexo y nombre).
Por ejemplo, ajustando tu comando para que diga distinct(id_caso)
sólo considerará una fila como "duplicada" si la columna id_caso
son idénticos (los valores de TODAS las columnas ya no tienen que ser idénticos para considerarse un "duplicado").
Observa, sin embargo, el comportamiento por defecto de la función si ejecutas distinct(id_caso)
. ¡Todas las demás columnas se eliminan de la base de datos! Puedes añadir el argumento .keep_all = TRUE
y se conservarán todas las demás columnas.
distinct(id_caso, .keep_all = TRUE)
Puedes leer más sobre la función distinct()
en su documentación y en los capítulos limpieza de datos o des-duplicación del Manual de Epi R.
Antes de continuar, asegúrate de que tu línea de des-duplicación está escrita como distinct()
sin incluir nada entre los paréntesis.
distinct()
r fontawesome::fa("exclamation", fill = "red")
¿Has guardado tu guión últimamente?
class()
Cada columna de la base de datos tiene una "clase" específica. Las clases reflejan cómo entiende R a los valores de las columnas.
Anteriormente, revisaste las clases de cada columna con la función skim()
del paquete {skimr}. También puedes ver la clase de una sola columna utilizando la función class()
de {base} R.
Escribe y ejecuta estos comandos en el "Área de pruebas" de tu script de R:
# Imprimir la clase de una columna class(vig$sexo)
# Imprimir la clase de una columna class(vig$fecha_sintomas)
¡Repasemos lo que hemos aprendido hasta ahora!
¿En qué se diferencia este comando de tu comando de limpieza / "cadena de pipes"?
vig
está escrito en el propio comando$
se utiliza para indexar vig
y devolver la clase sólo de la columna sexo
En un comando de limpieza largo con pipes, el operador $
no es necesario porque la base de datos se transmite de una función a la siguiente con el operador pipe. Cuando no se utiliza un operador pipe, debemos escribir la base de datos explícitamente.
¿Cuáles son las posibles clases de columnas?
Las clases de columna más comunes son:
Una parte inicial (¡e importante!) del proceso de limpieza de datos es asegurarse de que R entiende correctamente la clase de cada columna. En general, R es bueno adivinando esto, pero a veces necesita tu ayuda.
Más arriba, reconociste que la columna fecha_sintomas
es reconocida por R como clase "carácter" (nominal). Las fechas de las hojas de cálculo de Excel importadas a R suelen reconocerse así, o en numérico (por ejemplo, 48256), o en una extraña clase llamada "POSIXct".
Queremos designar esta columna como clase "Fecha".
En primer lugar, tenemos que entender cómo se escribe la clase fecha en los datos.
En tu "Área de pruebas", ejecuta el siguiente comando para mostrar los seis primeros valores de la columna fecha_sintomas
en la base de datos vig
:
head(vig$fecha_sintomas)
quiz(caption = "Cuestionario - Formato crudo de la columna fecha", question("¿En qué formato se encuentra la columna fecha_sintomas?", allow_retry = T, answer("YYYY-MM-DD"), answer("DD/MM/YYYY"), answer("MM/DD/YYYY", correct = T) ) )
{lubridate} tiene varias funciones diseñadas para convertir valores de texto en clases "Fecha" de forma intuitiva.
Las 3 funciones ymd()
, mdy()
y dmy()
corresponden al formato de los valores de fecha BRUTOS, antes de utilizar la función. Afortunadamente, son inteligentes, ya que admiten diversos separadores (guiones, barras, espacios) y sinónimos de fecha (01 vs Ene vs Enero):
En dmy()
debe utilizarse para valores brutos escritos como: día, mes, año. Copia y pega el siguiente código en tu "Área de pruebas" para echarle un vistazo:
dmy("11 October 2020") dmy("11/10/2020")
En mdy()
hace lo mismo pero para valores brutos escritos como: mes, día, año. Copia y pega el siguiente código en tu "Área de pruebas" para echarle un vistazo:
mdy("Oct 11 20") mdy("10/11/20")
ymd()
debe utilizarse para convertir los valores brutos que se escriben como: año, mes, día. Copia y pega el siguiente código en tu "Área de pruebas" para echar un vistazo:
ymd("2020-10-11") ymd("20201011")
En TODOS los comandos anteriores, el resultado son valores de clase "fecha" que se muestran como AAAA-MM-DD.
quiz(caption = "Cuestionario - Formato de fechas", question("¿Qué formato debería ser usado para la columna fecha_sintomas?", allow_retry = T, answer("ymd()"), answer("dmy()"), answer("mdy()", correct = T) ) )
Lo más importante que debes recordar es utilizar la función que coincida con el formato de fecha en bruto.
Estos ejemplos aplican funciones de {lubridate} a valores individuales. En la siguiente sección aprenderemos a aplicar estas funciones a toda una columna.
mutate()
La función mutate()
se utiliza para cambiar una columna entera o crear una columna nueva. Puedes introducir una base de datos en mutate()
usando el operador pipe.
En mutate()
la sintaxis es la siguiente
mutate(nombre de columna = una función que utilizas para crear/modificar la columna)
Así, para convertir la columna fecha_sintomas
a la clase "Fecha", tendrías que añadir esta línea al comando de limpieza con un operador pipe:
mutate(fecha_sintomas = mdy(fecha_sintomas))
El código dice "cambia (muta) el fecha_sintomas
para que contenga los valores de la columna fecha_sintomas
modificados por la función mdy()
."
Añade dos líneas mutate()
a tu comando de limpieza, para convertir fecha_sintomas
y fecha_notifica
a la clase "Fecha".
Recuerda volver a ejecutar todo el comando una vez que hayas añadido estas líneas.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica))
Ahora puedes utilizar esta columna como variable continua y producir un simple histograma (curva epidémica) utilizando el siguiente código. Esto no sería posible con las fechas como valores de texto.
Pega el código siguiente en tu "Área de pruebas" y ejecútalo.
# Crea un histograma de fechas con la base de datos limpia ggplot(data = vig, mapping = aes(x = fecha_sintomas))+ geom_histogram()
quiz(caption = "Cuestionario - Fecha", question("En las semanas más recientes, ¿cuál es la aperente tendencia en el número de casos reportados?", allow_retry = T, answer("Creciente"), answer("Decreciente", correct = T) ) )
Hay otra columna de la que debemos comprobar la clase: edad
. ¿Cuál es la clase de esta columna?
Escribe y ejecuta este comando en tu sección de "Área de pruebas:
class(vig$edad)
El comando anterior devuelve "entero" (valores enteros). Aunque esto está bien por el momento, puede que queramos realizar cálculos que requieran que esta columna sea de clase "numérica" para aceptar valores decimales.
Por suerte en {base} R existen funciones como as.numeric()
, as.character()
y as.integer()
para convertir fácilmente columnas de una clase a otra.
En tu comando de limpieza, añade otra línea mutate()
que convierta la columna edad
utilizando la función as.numeric()
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
En la función mutate()
, redefine edad
como clase numérica envolviendo edad
dentro de la función as.numeric()
y haciéndola igual a una "nueva" columna llamada edad
.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad))
r fontawesome::fa("exclamation", fill = "red")
¿Has guardado tu guión últimamente?
Hay varias funciones para realizar recodificaciones simples de valores.
Como ya se ha indicado anteriormente, en los datos "brutos" hay un valor "Desconocido" en la columna sexo
. Los datos que faltan deben almacenarse en R como NA
, lo que tendrá ventajas más adelante en el curso.
Ejecuta el código siguiente en tu "Área de pruebas" y crea una tabla de recuento de los valores únicos de la columna sexo
usando la base limpia.
# Tabulación de la columna sexo tabyl(vig, sexo)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% tabyl(sexo)
Queremos decirle a R que si el valor en sexo
es "Desconocido", R debe cambiar el valor a NA
. Para ello, añadiremos un paso a nuestro comando de limpieza que utilice mutate()
y la función na_if()
para cambiar la columna sexo
.
La función na_if()
sustituye un valor especificado por NA
:
sexo
)NA
("Desconocido"). Nota: debes incluir comillas alrededor de "Desconocido" para que R pueda reconocerlo como un valor nominal.Vuelve a la sección "Limpiar datos de vigilancia" de tu script de R.
Al final de tu comando de limpieza, añade una pipe y otra línea mutate()
.
Dentro de mutate()
establece la columna sexo
igual a la función na_if()
. En el paréntesis de na_if()
escribe de nuevo sexo
, luego una coma, y después "Desconocido" entre comillas:
mutate(sexo = na_if(sexo, "Desconocido"))
Este código está diciendo "Cambia la columna sexo
de forma que sus valores sean iguales a los de la columna actual sexo
excepto cuando el valor sea "Desconocido", en cuyo caso ese valor debe cambiarse a NA
".
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% # convertir "Desconocido" en la columna sexo a NA mutate(sexo = na_if(sexo, "Desconocido"))
Nota: También es útil conocer la función opuesta, replace_na()
que sustituye a NA
por un valor específico.
Ahora vuelve a la "Zona de pruebas" y vuelve a ejecutar la función tabyl()
en la columna sexo
utilizando la base de datos limpia.
# Tabular los valores de la columna sexo tabyl(vig, sexo)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% # convertir "Desconocido" en la columna sexo a NA mutate(sexo = na_if(sexo, "Desconocido")) %>% tabyl(sexo)
Los valores que faltan deben están representados ahora como NA
.
across()
Como ya se ha mencionado anteriormente, cuando R importa bases de datos con formatos CSV o XLSX, a veces registra los valores que faltan en las columnas de texto como un espacio de caracteres vacío ""
en lugar de como el símbolo especial NA
.
Ejecuta el código siguiente en tu "Área de pruebas", que crea una tabla de recuento de los valores únicos de la columna fiebre
.
tabyl(vig, fiebre)
Tenemos que decirle a R que si el valor en fiebre
es el espacio vacío "" entonces R debe cambiar el valor a NA
. Sabemos que podemos conseguirlo añadiendo esto a nuestro comando de limpieza con un operador pipe:
mutate(fiebre = na_if(fiebre, ""))
Pero hay muchas columnas en las que los datos que faltan se registran como "". ¿Cómo podemos realizar todos estos cambios de forma eficaz?
El comando siguiente utiliza la función across()
dentro de mutate()
para cambiar varias columnas a la vez. Éste es un comando intermedio de R, así que no dediques mucho tiempo a intentar entenderlo. Más adelante, puedes preguntar a un/a instructor/a, o leer más sobre across()
en la sección correspondiente del Manual de Epi R. En resumen, el siguiente comando dice "muta todas las columnas que sean de clase carácter (texto), y utiliza la función na_if()
para convertir cualquier instancia de espacio vacío como "" en NA
".
Añade la siguiente línea en tu comando de limpieza. Recuerda volver a ejecutar todo el comando de limpieza.
mutate(across(.cols = where(is.character), .fns = ~na_if(.x, "")))
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% # convertir "Desconocido" en la columna sexo a NA mutate(sexo = na_if(sexo, "Desconocido")) %>% # registrar valores ausentes apropiadamente en varias columnas de clase nominal mutate(across(.cols = where(is.character), .fns = ~na_if(.x, "")))
Mientras hablamos de los valores ausentes, vamos a presentarte las funciones básicas de R que se utilizan para evaluar la ausencia.
Tenemos dos columnas de fecha en la base de datos: fecha_sintomas
y fecha_notifica
. La primera es la fecha de aparición de los síntomas, registrada por el equipo de investigación del caso. La segunda es la fecha en que se comunicó el caso al equipo de vigilancia.
¿Cuál deberíamos utilizar principalmente?
Hay muchos factores que intervienen en una decisión como ésta, como la finalidad del análisis, la audiencia, etc. Un factor importante es la calidad y la ausencia (o pérdida) de datos.
La ausencia de datos en R se representa mediante NA
(o al menos debería serlo, como ya hemos dicho). La función para evaluar la ausencia es is.na()
. Del mismo modo, la expresión !is.na()
se utiliza para comprobar la no ausencia: el signo de exclamación es un operador de negación. En ambas funciones, el objeto que quieres evaluar se coloca dentro del paréntesis.
Recuerda los siguientes comandos relacionados con la ausencia de datos en R.
is.na()
se utiliza para evaluar si un valor es NA
!is.na()
se utiliza para evaluar si un valor NO es NA
Ejecuta este comando en el "Área de pruebas":
is.na(vig$fecha_sintomas)
En tu consola, deberías ver un VERDADERO o FALSO por cada fila de la columna. No es muy útil.
Para contar el número de filas que son TRUE, puedes envolver este comando en sum()
.
Ejecuta estos dos comandos en el "Área de pruebas" de tu script:
sum(is.na(vig$fecha_sintomas)) # número de filas donde is.na(vig$fecha_sintomas) retorna TRUE sum(is.na(vig$fecha_notifica)) # número de filas donde is.na(vig$fecha_notifica) retorna TRUE
quiz(caption = "Cuestionario - Fecha", question("¿Qué columna tiene más valores ausentes?", allow_retry = T, answer("fecha_sintomas", correct = T), answer("fecha_notifica") ), question("¿Qué porcentaje de la columna 'fecha_sintomas' está ausente? Pista: tienes que contar el número de NAs en fecha_sintomas y ya sabes el número total de filas en tu base de datos...", allow_retry = T, answer("16.4 %"), answer("10.2 %"), answer("1 %"), answer(paste(round(sum(is.na(vig$fecha_sintomas))/length(vig$fecha_sintomas), 3) * 100, "%"), correct = T) ) )
¡Nuestro comando de limpieza es ahora bastante largo! Cada paso es importante y, afortunadamente, todo está bien documentado en nuestro código. Esta transparencia es útil para nosotros mismos, para cualquiera que revise nuestro código más adelante, ¡y para la integridad científica!
Veamos ahora la columna hospital
.
Escribe y ejecuta este código en tu "Área de pruebas". Imprimirá una tabulación de todos los valores de esta columna:
tabyl(vig, hospital)
¡Parece que hay que hacer limpieza! Observa las diferentes escrituras de "hospital" y "hopital", de "Militar" y "Mitilario", y de "Puerto" y "Hospital del Puerto". Hay que sincronizarlas.
Una opción fácil es recode()
. Revisa la sintaxis genérica que aparece a continuación. El código puede entenderse como "Redefine hospital
como hospital
con estas re-codificaciones aplicadas". Ten en cuenta que esto utiliza el orden sintáctico opuesto al de rename()
que era NUEVO = VIEJO. recode()
utiliza VIEJO = NUEVO.
mutate(hospital = recode(hospital, "VIEJO valor" = "NUEVO valor", "VIEJO valor" = "NUEVO valor", "VIEJO valor" = "NUEVO valor"))
Utilizando el código anterior como plantilla, añade una línea al FINAL de tu comando de limpieza que alinee todos los nombres de los hospitales con lo siguiente:
Dejaremos los hospitales con NA
como están. No olvides entrecomillar los nombres de los hospitales (nombres VIEJO y NUEVO), ya que son valores nominales.
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
Comienza agregando una nueva función mutate()
. Dentro de los paréntesis de mutate()
, escribe el nombre de la columna a redefinir, hospital
y un signo igual. A la derecha del signo igual, escribe la función recode()
. Entre los paréntesis de recode()
el primer argumento vuelve a ser el nombre de la columna a redefinir.
Después de una coma, continúa con una línea por cambio, con el valor antiguo a la izquierda y el nuevo a la derecha. Como se trata de valores nominales, ponlos entre comillas. No olvides poner comas entre cada línea, y cerrar con el número correcto de paréntesis.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% # convertir "Desconocido" en la columna sexo a NA mutate(sexo = na_if(sexo, "Desconocido")) %>% # registrar valores ausentes apropiadamente en varias columnas de clase nominal mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% # re-codificar la columna hospital mutate(hospital = recode(hospital, # freferencia: VIEJO = NUEVO "Other" = "Military Hospital", "Port" = "Port Hospital", "Port Hopital" = "Port Hospital", "St. Mark's Maternity Hospital (SMMH)" = "SMMH"))
Utilizando el mismo método anterior, añade otra línea a tu comando de limpieza para recodificar, recode()
, los valores de la columna sexo
de "m" a "hombre" y de "f" a "mujer".
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% # convertir "Desconocido" en la columna sexo a NA mutate(sexo = na_if(sexo, "Desconocido")) %>% # registrar valores ausentes apropiadamente en varias columnas de clase nominal mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% # re-codificar la columna hospital mutate(hospital = recode(hospital, # referencia: VIEJO = NUEVO "Other" = "Military Hospital", "Port" = "Port Hospital", "Port Hopital" = "Port Hospital", "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% # re-codificar sexo mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer"))
¡Enhorabuena! ¡Has terminado la primera parte de la limpieza de datos de este curso!
Guarda tu script y comunícate con tu instructor/a para informarle de que has terminado.
Si dispones de tiempo extra, asegúrate de que tu script coincide con el que aparece a continuación. ¿Se parecen?
¡Considera cómo podrías limpiar tu script! Pide a tus instructores/as que revisen la organización del script si tienes dudas sobre el formato o la fluidez.
NOTA: el script que aparece a continuación también se encuentra en la carpeta "ebola/scripts/backups". Puedes observar que la sección "Análisis exploratorio" y la sección "Área de pruebas" de los scripts de copia de seguridad están vacías, porque el contenido puede variar.
# Acerca de este script ---------------------------------------------- # Propósito: Análisis de brote de ébola # Autor: tu nombre # Fecha: la fecha de hoy # Cargar paquetes ---------------------------------------------- # El paquete pacman instalará cada paquete de ser necesario y los cargará para # que se puedan utilizar en esta sesión pacman::p_load( rio, # para importar datos here, # para localizar archivos usando rutas relativas skimr, # para revisar los datos janitor, # para limpiar los datos epikit, # para crear catgegorías de edad tidyverse # para la gestión y visualización de datos ) # Importar datos ---------------------------------------------- # Importar datos brutos del brote de ébola vig_bruta <- import(here("datos", "brutos", "listado_vigilancia_20141201.csv")) # Análisis exploratorio ----------------------------------------- # El contenido variará # Limpiar datos de vigilancia ----------------------------------------- vig <- vig_bruta %>% # estandarizar los nombres de las columnas automáticamente clean_names() %>% # limpiar los nombres de las columnas manualmente rename( # Nuevo nombre # Viejo nombre fecha_sintomas = fecha_inicio_sintomas, fecha_notifica = fecha_de_notifica, distrito_res = adm3_nombre_res, distrito_not = adm3_nombre_not) %>% # remueve la columna innecesaria select(-num_fila) %>% # eliminar filas duplicadas distinct() %>% # convertir fecha_sintomas a la clase fecha mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% mutate(fecha_notifica = mdy(fecha_notifica)) %>% # convertir edad a la clase numérica mutate(edad = as.numeric(edad)) %>% # convertir "Desconocido" en la columna sexo a NA mutate(sexo = na_if(sexo, "Desconocido")) %>% # registrar valores ausentes apropiadamente en varias columnas de clase nominal mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% # re-codificar la columna hospital mutate(hospital = recode(hospital, # referencia: VIEJO = NUEVO "Other" = "Military Hospital", "Port" = "Port Hospital", "Port Hopital" = "Port Hospital", "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% # re-codificar sexo mutate(sexo = recode(sexo, "m" = "hombre", "f" = "mujer")) # Area de prueba ----------------------------------------- # Contents will vary
Si te sobra tiempo después de limpiar tu script, sigue adelante con los extras de la página siguiente.
Una alternativa útil a la ruta de archivo es utilizar la función file.choose()
de {base} R. Esto hace que aparezca una ventana emergente, desde la que puedes seleccionar manualmente el archivo a importar.
Tu comando de importación tendría este aspecto (nota el paréntesis vacío al final de file.choose()
):
vig_bruta <- import(file.choose())
Esto podría no funcionar con algunas de las versiones más recientes de RStudio, debido a un error.
Este enfoque tiene la desventaja de no estar tan bien documentado (para la reproducibilidad), pero puede ser extremadamente útil si recibes habitualmente datos actualizados por correo electrónico y simplemente necesitas seleccionar la versión más actualizada guardada en una carpeta local.
En el capítulo sobre importación de datos del Manual de Epi R hay un código de ejemplo sobre cómo escribir código para importar automáticamente la base de datos más reciente de una carpeta. Sin embargo, file.choose()
a menudo puede ser una alternativa más sencilla.
Intenta escribir un comando con file.choose()
en tu script de R y prueba este proceso. Si no ves la ventana emergente, comprueba detrás de tu ventana de RStudio.
Si tienes tiempo extra, intenta instalar el programa {tidylog} insertándolo en tu pacman::p_load()
y volviéndolo a ejecutar.
Una vez cargada, esta función registra cada paso de limpieza de datos que ejecutes (por ejemplo filter()
, mutate()
, select()
, etc.) e imprimirá un registro de los cambios en la Consola.
Vuelve a ejecutar tu comando de limpieza para ver el impacto de cada paso del proceso impreso en la Consola.
La desduplicación es un tema importante que suele ser más complejo que el sencillo ejemplo de este ejercicio. Hemos escrito sobre escenarios de desduplicación más complejos en este capítulo del Manual de Epi R.
La fecha y la hora implican muchas circunstancias especiales. Familiarízate con este capítulo del Manual de Epi R sobre trabajar con fechas.
En particular, revisa el paquete {parsedate} y su función parse_date()
. Se utiliza para limpiar esas columnas de fecha tan desordenadas para las que las funciones mdy()
, dmy()
y ymd()
de {lubridate} no funcionarán porque las fechas no tienen el mismo formato general en todas las filas. parse_date()
evaluará cada fila individualmente, adivinando de forma inteligente el formato de cada celda. Esto lleva más tiempo, pero es útil si las fechas están muy desordenadas.
Ver también excel_numeric_to_date()
función del paquete {janitor}. También existe la función convert_to_date()
que ayuda cuando las fechas numéricas de Excel se mezclan con otros formatos.
La denominación de los archivos puede parecer algo insignificante, pero sus ramificaciones pueden ser importantes. Una denominación adecuada de los archivos puede ahorrarte tiempo y quebraderos de cabeza, y cambiar de método puede ser fácil.
Imagina el poder de R: ¡puedes escribir código para importar automáticamente los archivos de datos más recientes por la fecha de su nombre de archivo! Pero esto puede verse obstaculizado por malas prácticas de denominación de archivos.
Aunque nunca pruebes codificar algo así, seguro que sabes que las malas prácticas de denominación de archivos pueden causar problemas importantes para el control de versiones y el archivado.
¿Has visto alguna vez archivos como éstos?
r emo::ji("cross mark")
notas.docx
r emo::ji("cross mark")
a.R
r emo::ji("cross mark")
2b.xlsx
r emo::ji("cross mark")
notas.txt
¿Qué te parece algo así?
r emo::ji("cross mark")
control del caso fábrica de San José.pptx
r emo::ji("cross mark")
¿abstracto para Mark?.xlsx
r emo::ji("cross mark")
Actualización del 20 de diciembre.R
r emo::ji("cross mark")
proyecto de informe "estudio sobre la malaria"(1).docs
Es muy difícil trabajar con ellos. Intenta seguir estos principios a la hora de nombrar los archivos (¡tu futuro yo te lo agradecerá!):
Hablemos de consejos para conseguir cada uno de estos principios.
No utilices espacios en blanco (vacíos) en los nombres de los archivos (las máquinas a veces se confunden con los espacios)
r emo::ji("cross mark")
Borrador del equipo de divulgación de Vax.docx
r emo::ji("check")
proyecto_publicacion_vacunacion01.docx
Utiliza sólo letras, números, guiones (-) y guiones bajos (_) (los caracteres especiales pueden tener significados especiales y confundir a las máquinas, por ejemplo ^.*?+|$ )
r emo::ji("cross mark")
Informe del Ministerio ?.docx
r emo::ji("check")
informe_ministerio_borrador01.docx
Nunca tengas dos nombres de archivo que sólo se diferencien por las mayúsculas y minúsculas (algunos sistemas operativos tratan b y B igual, mientras que otros los tratan como diferentes), y sé coherente con las mayúsculas y minúsculas (normalmente es mejor minúsculas)
r emo::ji("cross mark")
estudio.docx
r emo::ji("cross mark")
Estudio.docx
r emo::ji("cross mark")
Bélgica y Francia.docx
r emo::ji("check")
estudio.docx
r emo::ji("check")
bélgica y francia.docx
Utiliza guiones y guiones bajos de forma diferente
r emo::ji("check")
20200316_goma_linelist.xlsx
r emo::ji("check")
transmisión-análisis_california_mmwr.docx
Las fechas deben escribirse como AAAA-MM-DD (la norma ISO 8601). Normalmente, es mejor colocar las fechas antes de otros componentes del nombre para preservar la cronología.
Los nombres de archivo con este estilo de fechas no se ordenan cronológicamente.
r emo::ji("cross mark")
1-April-2012_linelist.xlsx
r emo::ji("cross mark")
1-Jan-2009_linelist.xlsx
r emo::ji("cross mark")
1-Jan-2012_linelist.xlsx
r emo::ji("cross mark")
12-Jan-2012_linelist.xlsx
r emo::ji("cross mark")
2-Jan-2012_linelist.xlsx
r emo::ji("cross mark")
31-Dec-2009_linelist.xlsx
¡Pero este estilo sí ordena correctamente!
r emo::ji("check")
2009-01-01_linelist.xlsx
r emo::ji("check")
2009-12-01_linelist.xlsx
r emo::ji("check")
2009-12-31_linelist.xlsx
r emo::ji("check")
2012-01-01_linelist.xlsx
r emo::ji("check")
2012-01-02_linelist.xlsx
r emo::ji("check")
2012-04-01_linelist.xlsx
Para ordenar archivos sin fechas utiliza números como prefijos (comienza con 0 para que todos los números tengan la misma longitud)
r emo::ji("check")
01_introducción.docx
r emo::ji("check")
02_métodos.docx
r emo::ji("check")
03_análisis.docx
... (más capítulos)...
r emo::ji("check")
19_apéndice-04.docx
r emo::ji("check")
20_apéndice-05.docx
r emo::ji("check")
21_apéndice-06.docx
Al final, la nomenclatura de los archivos no tiene reglas rígidas, sino que es una elección personal. Sin embargo, ciertas elecciones pueden provocar más frustración, archivos perdidos y dolores de cabeza de codificación más adelante.
quiz(caption = "Cuestionario - Nombres de archivos", question("¿Qué componentes de este nombre de archivo no siguen las guías anteriores? (Selecciona todas las que consideres apropiadas):\n\nSARS.Exposure.listing 1stdraft 01032016.csv", allow_retry = T, answer("La fecha está ordenada incorrectamente", correct = T, message = "La fecha debería ser AAAA-MM-DD"), answer("Es en realidad para sarampión, no SARS", message = "¡No lo sabes! Esto es un ejemplo :-)"), answer("Utiliza caracteres esperciales", correct = T, message = "Los nombres de archivos deberían evitar puntos"), answer("Usa espacios", correct = T, message = "Evita los espacios en los nombres de archivos"), answer("La fecha debería estar al comienzo", correct = T, message = "Para un orden adecuado, la fecha debería estar al comienzo") ) )
Los conceptos de esta sección sobre los nombres de los archivos se han tomado prestados de estas diapositivas sobre la estructura de proyectos de DJ Navarro.
Si tienes tiempo extra, te animamos a que revises lo siguiente para obtener más consejos sobre la limpieza de datos:
El capítulo del Manual de Epi R sobre Limpieza de datos y funciones {base} R
La sección del Manual de Epi R sobre el uso de across()
para limpiar varias columnas a la vez
Si tienes preguntas sobre alguno de los temas anteriores, ponte en contacto con tu instructor/a y, si no está ocupado/a ayudando a otros/as alumnos/as, podrá ayudarte.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.