# load packages ---------------------------------------------------------------- library(introexercises) library(learnr) library(gradethis) library(flair) library(dplyr) library(ggplot2) library(lubridate) library(janitor) library(fontawesome) library(gtsummary) library(scales) # 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 <- rio::import(system.file("dat/listado_vigilancia_limpio_20141201.rds", package = "introexercises"))
Bienvenido al curso "Introducción a R para epidemiología aplicada", ofrecido por Epidemiología - una organización sin fines de lucro y el principal proveedor de formación, apoyo y herramientas de R para profesionales de primera línea de la salud pública.
knitr::include_graphics("images/logo.png", error = F)
Este ejercicio se centra en agrupar y resumir datos en tablas descriptivas.
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 al instructor/facilitador de tu curso en directo 3) Programa una llamada 1 a 1 con un instructor para "Tutoría del curso". 4) Publica una pregunta en La Comunidad de Applied Epi
Este es el aspecto que tendrá 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!)
linelist %>% 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 ansioso estás por comenzar este tutorial? En una escala del 1 (menos ansioso) al 10 (más ansioso).?", 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 compartir.", 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.
tabyl()
y adorn_()
group_by()
, summarise()
y count()
tbl_summary()
r fontawesome::fa("window-restore", fill = "darkgrey")
Haz doble clic en el archivo del proyecto RStudio "ebola" para abrirlo (como en el módulo anterior).
Vuelve a abrir tu script localizado en la carpeta "ebola_análisis.R" donde escribiste el código de limpieza del módulo anterior.
Asegúrate de que los siguientes paquetes están incluidos en el directorio pacman::p_load()
en el comando Cargar paquetes de tu script:
Sin embargo, asegúrate de que, cuando añadas paquetes, coloca el paquete {tidyverse} de último lugar en este comando. Esto es para que las funciones de {tidyverse} tengan prioridad sobre las funciones de otros paquetes con el mismo nombre.
Cuando ejecutes este comando, estos paquetes se instalarán (si estos todavía no están instalados) y se cargarán para su uso.
Recuerda que sólo debes tener un solo comando de pacman::p_load()
en la parte superior de tu script.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
pacman::p_load( rio, # para cargar bases de datos here, # para localizar archivos janitor, # para limpieza lubridate, # para limpieza de fechas epikit, # para crear categoría de edades gtsummary, # para crear tablas scales, # para añadir formato de porciento flextable, # para hacer tablas con formato tidyverse # para manejo de datos y visualización )
Ejecuta todo el código de tu script. Si has terminado con éxito los módulos anteriores, el script hará lo siguiente:
vig_bruta
mediante una "cadena de operadores pipe", y guarda los datos "limpios" en un nuevo objeto llamado vig
Ahora puedes escribir más código en la parte inferior del script, utilizando el dataframe o base de datos vig
.
Si tu script R provoca demasiados errores, o no has terminado el último módulo, avisa a tu instructor. Puedes importar una versión limpia de la base de datos de vigilancia con el siguiente comando y utilizarla para este ejercicio:
vig <- import(here("datos", "limpios", "copia_seguridad", "listado_vigilancia_limpio_20141201.rds"))
Asegúrate de que tu script está bien comentado (#) para que sea fácil saber qué ocurre en cada parte del mismo.
Añade una nueva sección a tu script para las "Tablas resumen", justo encima del "Área de pruebas". Coloca el cursor donde deba empezar la nueva sección y pulsa Ctrl+Shift+R al mismo tiempo (o Cmd Mayús R en un Mac) para crear un nuevo encabezado de sección.
# Tablas resumen ----------------------------------------------
Recuerda que puedes navegar por tu script utilizando estas secciones con el botón "Esquema" (outline) situado en la esquina superior derecha del script R.
A menudo, sólo queremos una tabla sencilla de los totales de una variable categórica o nominal, o simplemente saber "cuáles son los valores únicos" de una columna concreta.
La función tabyl()
de {janitor} lo hace rápidamente tabulando los valores únicos de la variable que especificamos. Luego puedes personalizarla fácilmente (agregando porcentajes, totales por ejemplo), utilizando las funciones adorn_()
del mismo paquete.
Los operadores pipe no sólo sirven para "limpiar". La función de estos conectores es pasar un comando a otro para partiendo desde una base de datos u objeto, como una cadena.
El hacer tablas con {janitor} puede requerir varios pasos, y puedes utilizar este operador %>%
para pasar o conectar los comandos de un paso al siguiente (Ejemplo: Hago esto y "entonces" (operador pipe) hago esto).
Escribe un código en tu sección Tablas resumen que conecte el objeto vig
con la función tabyl()
y especifica la columna distrito
en la función tabyl()
.
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
Escribre el nombre del objeto vig
conectado con la función tabyl()
y escribe la variable distrito
entre paréntesis.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
vig %>% tabyl(distrito)
quiz(caption = "Cuestionario - tabyl()", question("¿Este comando es un comando para PRESENTAR o GUARDAR?", allow_retry = T, answer("Un comando para presentar", correct = T, message = "Este es un comando presentación porque no se usó el operador de asignación '<-'. Los datos se convierten en una tabla y simplemente se presentan en la consola. No hay cambios permanentes en la base de datos."), answer("Un comando para guardar", correct = F, message = "Este es un comando presentación porque no se usó el operador de asignación '<-'. Los datos se convierten en una tabla y simplemente se presentan en la consola. No hay cambios permanentes en la base de datos."), answer("Un comando universal", correct = F, message = "No existe tal cosa como un comando universal") ), question("¿Cuál es el distrito con más casos registrados?", allow_retry = T, answer("Mountain Rural", correct = T), answer("West II"), answer("Central II"), answer("Manhattan") ), question("¿Cuántas filas faltan en el distrito (NA)?", allow_retry = T, answer("203"), answer("37"), answer("None"), answer(vig %>% filter(is.na(distrito)) %>% nrow(), correct = T) ) )
show_na = FALSE
Con la función tabyl()
cuando en una variable con valores vacíos o NA, contiene por defecto una columna "porcentaje_válido" que muestra las proporciones calculadas al excluir los valores perdidos.
Ejecuta la función tabyl()
pero especificando el argumento show_na = FALSE
. ¿Cómo cambia la salida?
vig %>% tabyl(distrito, show_na = FALSE)
¿Cómo sabrías ejecutar este argumento, o cuáles son los valores por defecto? Esta información está escrita en la documentación disponible de la función
?tabyl
otabyl
en la barra de búsquedaEn esta documentación, puedes ver que el valor por defecto para este argumento es TRUE
pero que si lo ajustas a FALSE
se eliminará NA
valores de la tabla.
knitr::include_graphics("images/show_na_argument.png", error = F)
El paquete {janitor} incluye una serie de funciones de soporte para el "adorno" en las que puedes canalizar en una tabla, que harán que el formato sea más presentable y detallado.
Añade las siguientes funciones al código, utilizando un operador pipe *en este orden*. Deja sus paréntesis vacíos, no necesitan parámetros. Observa cómo cambia la salida de tabla con cada función.
adorn_totals()
adorn_pct_formatting()
Actualmente, la tabla de distritos está ordenada alfabéticamente por el nombre del distrito. Cambia esto introduciendo al final la función arrange()
del paquete {dplyr} y especificando que se ordene por la columna n
de forma descendente.
Para especificar descendente, puedes envolver el nombre de la variable con desc()
o poner un símbolo - (menos) delante del nombre de la variable.
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
vig %>% tabyl(distrito) %>% adorn_totals() %>% adorn_pct_formatting() %>% arrange(desc(n))
o
vig %>% tabyl(distrito) %>% adorn_totals() %>% adorn_pct_formatting() %>% arrange(-n)
Experimenta con el orden de los pasos... ¿Qué ocurre si añades adorn_totals()
antes de ordenar por columnas n
?
Reordena tus pasos para que las filas estén en orden descendente por la variable n
y manteniendo la fila "Total" en la parte inferior.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig %>% tabyl(distrito) %>% arrange(desc(n)) %>% adorn_totals() %>% adorn_pct_formatting()
Ahora haz una tabulación cruzada del distrito y el hospital, colocando los nombres de ambas columnas en la columna tabyl()
separados por una coma.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig %>% tabyl(distrito, hospital)
quiz(caption = "Cuestionario - tablas de hospitales", question("Según esta tabla cruzada, ¿En qué distrito probablemente se encuentre SMMH?", allow_retry = T, answer("West II"), answer("Central II"), answer("Mountain Rural", correct = T), answer("East I") ), question("¿Cómo afecta el argumento show_na = FALSE a esta tabulación cruzada?", allow_retry = T, answer("Sin efecto"), answer("Elimina NA de las filas"), answer("Elimina NA de filas y columnas", correct = T), answer("Elimina NA de las columnas") ) )
Intenta añadir funciones de adorn_()
al comando de la tabla con tabyl()
. Observa cómo se comportan de forma diferente en las tablas cruzadas que en las tablas de una sola variable.
adorn_totals()
adorn_percentages()
(convierte los recuentos en proporciones decimales)adorn_pct_formatting()
(convierte las proporciones decimales al formato %. Inténtalo también con el argumento digits = 0
)Ahora, intenta añadir estas funciones (sólo una cada vez):
adorn_ns()
o adorn_ns("rear")
o adorn_ns("front")
Explora el objeto vig
con la función tabyl()
.
Elige 2 tablas para guardar en tu script ebola_analysis.R en la sección "Tablas resumen que muestren tus nuevas habilidades sobre el uso de la función tabyl()
. Elimina todas las demás tablas que hayas creado (o muévelas al "Área de pruebas" si quieres conservarlas como referencia).
Añade una sub-sección en tu sección "Tablas resumen" que se llame "Tablas con dplyr". Asegúrate de que tiene dos símbolos hash (#) para que aparezca correctamente el indentado en el esquema del script (ver el botón gris "Esquema" (Outline) en la esquina superior derecha del script).
# Tablas resumen ---------------------------------------------- ## Tablas con janitor ---------------------------------------- ## Tablas con dplyr ------------------------------------------
count()
La función count()
del paquete {dplyr} proporciona una forma alternativa y sencilla de hacer tablas de frecuencia.
Prueba a ejecutar este código en tu sección "Tablas resumen":
## Tablas con dplyr vig %>% count(hospital)
¿Cómo se compara con la función tabyl()
usando la misma variable?
Ahora, ajusta el comando para que, en lugar de presentar la tabla en la consola de R, la tabla se guarde como un nuevo objeto dataframe llamado hospital_recuento
o conteo_hospital
.
Una vez hecho esto, abre el objeto recién creado haciendo clic sobre él en el Entorno R, o ejecutando View(hospital_recuento)
o View(conteo_hospital)
.
Este paso es para mostrarte que, de hecho, ¡Estás creando otro objeto dataframe o base de datos! Podrías realizar análisis sobre este, o incluso exportarlo como un archivo csv o xlsx.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
hospital_recuento <- vig %>% count(hospital)
Ahora prueba a introducir 2 variables categóricas en la función count()
como distrito
y sexo
(separados por una coma).
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
hospital_recuento <- vig %>% count(hospital, sexo)
¿Qué aspecto tiene esta tabla? ¿Cómo se compara con la función de tabulación cruzada de tabyl()
? ¿Cuáles podrían ser algunas ventajas o desventajas de este formato "largo" o vertical?
¿Cuál de los dos formatos se ajusta mejor a las directrices de "datos ordenados" explicadas en los Extras del módulo anterior? (Recuerda, en los datos ordenados, cada variable tiene su propia columna y cada observación su propia fila...)
NOTA: La estructura ordenada de los datos se trató en la sección Extras del 2º módulo sobre limpieza de datos, ¡Vuelve cuando tengas tiempo para revisar en profundidad el formato "datos ordenados"!
group_by()
y summarise()
Las funciones group_by()
y summarise()
juntas son la herramienta más versátil para crear un nuevo objeto dataframe que contenga resultados de estadísticas.
Una gran ventaja de summarise()
es la posibilidad de devolver resúmenes estadísticos más personalizados como median()
, mean()
, max()
, min()
, sd()
(desviación estándar), percentiles y el número/porcentaje de filas que cumplen determinados criterios lógicos.
Prueba este comando. Debería producir exactamente la misma tabla que hiciste con count()
.
vig %>% group_by(hospital) %>% summarise(n_casos = n()) # Cantidad de filas por grupo
Puede que te preguntes para que escribir el comando de esta forma, cuando podría escribir simplemente count()
? La respuesta es que puedes añadir más líneas dentro de summarise()
que crearán nuevas columnas del resumen calculadas.
n_casos
o n_casos
es el nombre que estamos designando para una nueva variable en el base de datos resumen= n()
es una función n()
que cuenta el número de filas dentro del grupo (en este caso hospital)Observa a continuación cómo se amplía el comando para crear 3 columnas de resumen, cada una con sus respectivos cálculos.
vig %>% group_by(hospital) %>% summarise( n_casos = n(), # Cantidad de filas por grupo media_edad = mean(edad_anios, na.rm = T), # Promedio de edad por grupo max_sintomas = max(fecha_sintomas, na.rm = T) # Última fecha de inicio de sintomas por grupo )
En las dos líneas que crean las variables o columnas media_edad
y max_sintomas
, se utilizan las funciones matemáticas mean()
y max()
. En estas funciones, el primer argumento es la columna de la base de datos original que se está resumiendo (por ejemplo edad_anios
). A esto le sigue cualquier otro argumento relevante (por ejemplo na.rm = TRUE
para la mayoría de las funciones matemáticas).
na.rm = TRUE
Hemos mencionado antes cómo en la mayoría de las funciones matemáticas debes incluir el argumento na.rm = TRUE
. Esto se debe a que R quiere avisarte de cualquier valor que falte en el cálculo, y devolverá NA
si hay alguno. Configurar na.rm = TRUE
(NA
"eliminar") desactiva este comportamiento.
Como experimento, vuelve a ejecutar temporalmente tu código anterior pero sin na.rm = T
en el campo max_sintomas
cálculo. ¿Qué cambia? ¿Ves cómo influye en el resultado el hecho de que falten valores de fecha_sintomas
dentro del max()
cálculo?
sum()
De forma similar a como utilizamos max()
y mean()
en summarise()
puedes utilizar sum()
para devolver el número de filas por grupo que cumplen criterios lógicos. La fila del base de datos original se "cuenta" si esta expresión lógica se evalúa como TRUE para esa fila. Por ejemplo
vig %>% group_by(hospital) %>% summarise(num_adultos = sum(edad_anios >= 18, na.rm = T))
Otros ejemplos son:
sum(sexo == "hombre", na.rm=T)
sum(distrito %in% c("West I", "West II"))
Observa el uso de na.rm = TRUE
en sum()
por las razones descritas anteriormente.
Observa también la diferencia entre sum()
y summarise()
:
sum()
es una función matemática básica de base Rsummarise()
(o summarize()
) es de la {dplyr} y te permite resumir los datos por grupossummarise()
que devuelva, para cada hospital
, el número de caso femeninos.r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig %>% group_by(hospital) %>% summarise(fem = sum(sexo == "mujer", na.rm = TRUE))
Puede que esto te resulte más claro de leer, escrito como
vig %>% group_by(hospital) %>% summarise( fem = sum( # nombre de columna sexo == "mujer", # columna usada para calcular la sumatoria na.rm = TRUE)) # remueve NAs para un calculo preciso
¿Qué cambiarías en el cálculo para resumir el número de casos masculinos?
¿Qué cambiarías para resumir el número de casos con sexo desconocido/ausente? Consejo: implica utilizar la función is.na()
Cuando empieces a calcular estas columnas o variables, puede que quieras dividir una por otra, para crear un una proporción.
Una vez que hayas definido una columna o variable dentro de summarise()
puedes hacer referencia a ella al final dentro del comando summarise()
. Por ejemplo:
1) Calculas el número total de casos por hospital, y lo llamas n_casos
2) Calcula el número de casos masculinos por hospital, y llámalo hombres
3) Calcula la proporción de varones utilizando los dos
vig %>% group_by(hospital) %>% # agrupar por hospital summarise( n_casos = n(), # numero de casos por grupo hombres = sum(sexo == "hombre", na.rm=T), # conteo de masculinos por grupos hombres_pct = hombres / n_casos # porcentaje hombres del total, usando las dos columnas previamente creadas )
¿Qué aspecto tiene este porcentaje? ¿Es realmente un porcentaje? ¿O más bien una proporción (decimal)?
Hay una función que puedes utilizar para transformar rápidamente esta proporción en un porcentaje: es la función percent()
del paquete {scales}.
Prueba ahora
vig %>% group_by(hospital) %>% # agrupar por hospital summarise( n_casos = n(), # numero de casos hombres = sum(sexo == "hombre", na.rm=T), # conteo de casos masculinos hombres_pct = percent(hombres / n_casos) # porcentaje de casos masculinos )
Asegúrate de que tienes el paquete {scales} cargado (escrito en tu comando pacman al inicio del script).
Para más detalles, consulta el capítulo sobre Tablas descriptivas en el Manual de Epi.
Si necesitas redondear un número producido por summarise()
envuélvelo en la función de R {base} round()
y utiliza el argumento digits =
para ajustar el número de decimales.
Prueba a ejecutar este código, con y sin el argumento función round()
vig %>% group_by(hospital) %>% summarise( media_edad = round(mean(edad_anios, na.rm = T), digits = 0) )
Al eliminar round()
no olvides eliminar también la coma y digits = 0
que son el segundo argumento de esa función.
Desanidar las funciones y escribir el comando "más largo" con sangrías estratégicas puede ayudar a algunos programadores a entender qué argumentos pertenecen a cada función. Esto puede ser una preferencia personal.
vig %>% group_by(hospital) %>% summarise( media_edad = round( mean( edad_anios, na.rm = T), digits = 0 ) )
Una de las opciones al momento de hacer un cálculo con la función de summarise()
que puedes utilizar son los corchetes de subconjunto [ ]
. Estos símbolos se pueden utilizar después de una columna o variable para aplicar un filtro según los criterios lógicos que escribas dentro.
Por ejemplo, colocado dentro de summarise()
la declaración max_temp_fvr = max(temp[fiebre == "si"], na.rm = T)
devolverá la temperatura máxima registrada en el grupo, pero sólo entre los casos que sí declararon tener fiebre.
Es un comando complicado: pregunta a tu facilitador si no lo entiendes.
Crea un objeto dataframe que resuma lo siguiente, para cada hospital:
Asegúrate de escribir este comando en tu script R, y ajusta tu comando para guardar este marco de datos en tu Entorno R como hospital_table
para guardarlo más adelante.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
hospital_table <- vig %>% group_by(hospital) %>% # Obtener los resultados por cada hospital summarise( n_casos = n(), # número de filas (casos) max_sintomas = max(fecha_sintomas, na.rm = T), # última fecha de aparición del caso menos_5 = sum(edad_anios <= 5, na.rm = T), # número de niños menos de 5 vomit_n = sum(vomito == "si", na.rm=T), # número de casos con vómitos vomit_pct = percent(vomit_n / n_casos), # porciento de casos con vómitos max_peso_hombres = max(peso_kg[sexo == "hombre"], na.rm = T)) # peso máximo para los hombres
Al hacer una tabla resumen compleja con {dplyr} asegúrate de leer el capítulo Manual de Epi ya que hay detalles que no hemos tenido tiempo de cubrir aquí.
¡Limpiemos ahora nuestro código! Mantén sólo una versión del hospital_table
{dplyr}-en tu sección "Tablas resumen". Elimina el código {dplyr} de otras prácticas o muévelo al "Área de pruebas".
Tu sección "Tablas resumen" puede tener este aspecto:
# Tablas resumen ---------------------------------------------- ## Tablas con {janitor} ---------------------------------------- vig %>% tabyl(distrito, hospital) vig %>% tabyl(distrito) %>% arrange(desc(n)) %>% adorn_totals() %>% adorn_pct_formatting() ## Tablas con {dplyr}------------------------------------------ hospital_table <- vig %>% group_by(hospital) %>% # Obtener los resultados por cada hospital summarise( n_casos = n(), # número de filas (casos) max_sintomas = max(fecha_sintomas, na.rm = T), # última fecha de aparición del caso menos_5 = sum(edad_anios <= 5, na.rm = T), # número de niños menos de 5 vomit_n = sum(vomito == "si", na.rm=T), # número de casos con vómitos vomit_pct = percent(vomit_n / n_casos), # porciento de casos con vómitos max_peso_hombres = max(peso_kg[sexo == "hombre"], na.rm = T)) # peso máximo para los hombres
¡Fíjate en lo clara y limpia que parece la sección!
Con el paquete {flextable} puedes convertir un objeto dataframe o exportarlo en una tabla con una mejor estética en formato HTML.
Esto es útil si creas una tabla con {janitor} o {dplyr} pero quieres incluirla en un informe o imprimirla como imagen PNG.
El comando más sencillo de {flextable} es qflextable()
que significa "tabla flexible rápida". Convertirá una tabla en una imagen HTML, tras realizar algunos ajustes rápidos en relación de visualización y formato. Busca su aparición en el panel Visor de RStudio.
Añade el siguiente código a la sección "Tablas con dplyr" de tu código:
vig %>% group_by(hospital) %>% # Obtener los resultados por cada hospital summarise( n_casos = n(), # número de filas (casos) max_sintomas = max(fecha_sintomas, na.rm = T), # última fecha de aparición del caso menos_5 = sum(edad_anios <= 5, na.rm = T), # número de niños menos de 5 vomit_n = sum(vomito == "si", na.rm=T), # número de casos con vómitos vomit_pct = percent(vomit_n / n_casos), # porciento de casos con vómitos max_peso_hombres = max(peso_kg[sexo == "hombre"], na.rm = T) # peso máximo para los hombres ) %>% qflextable()
Revisa en el panel Visor de RStudio.
Prueba a hacer lo mismo con una das las tablas que creaste con las funciones del paquete {janitor} (por ejemplo, añade qflextable()
al final con un operador pipe).
Hay muchos ajustes que puedes hacer en un objeto tabla flextable. Consulta el Capítulo Tablas de presentación del Manual de Epi R.
Aquí tienes un ejemplo de una tabla diferente, utilizada en nuestro Manual de Epi R:
knitr::include_graphics("images/flextable.png", error = F)
La sección Extras de este ejercicio contiene más práctica sobre el uso del paquete {flextable}.
Una vez convertida tu tabla en objeto flextable
puedes exportarla a Word, PowerPoint o HTML o como un archivo de imagen (PNG).
Para ello:
1) Guarda la tabla como un objeto con nombre (por ejemplo hospital_table
) utilizando el operador de asignación <-
hospital_table <- vig %>% group_by(hospital) %>% # Obtener los resultados por cada hospital summarise( n_casos = n(), # número de filas (casos) max_sintomas = max(fecha_sintomas, na.rm = T), # última fecha de aparición del caso menos_5 = sum(edad_anios <= 5, na.rm = T), # número de niños menos de 5 vomit_n = sum(vomito == "si", na.rm=T), # número de casos con vómitos vomit_pct = percent(vomit_n / n_casos), # porciento de casos con vómitos max_peso_hombres = max(peso_kg[sexo == "hombre"], na.rm = T)) %>% # peso máximo para los hombres qflextable()
Ten en cuenta que se trata de un comando de guardar no un comando presentar. Por lo tanto, para ver esta nueva tabla en el panel Visor, tienes que ejecutar el comando hospital_table
.
2) En un comando separado escribe uno de las funciones del paquete {flextable} para exportar. No necesitas conectar los comandos de las tablas con estas funciones, son independientes.
save_as_docx()
save_as_pptx()
save_as_image()
save_as_html()
Dentro del paréntesis de la función, indica primero el nombre del objeto flextable que has guardado (por ejemplo my_table
). A continuación, introduce el argumento path =
y proporciona el nombre de archivo deseado, entre comillas, en el que quieras guardar (incluyendo la extensión del archivo), por ejemplo:
# Guarda la el objeto tabla como un documento de word "hospital_table.docx" save_as_docx(hospital_table, path = "hospital_table.docx")
Como en el ejemplo anterior no se han especificado subcarpetas, el archivo se guardará en la carpeta raíz/principal de tu proyecto RStudio, la carpeta "ebola.
¿Qué debemos hacer para guardar la tabla en la subcarpeta "resultados" de la carpeta "ebola"? Recuerda el uso de la función here()
Actualiza tu comando de exportación hospital_table
para que se guarde en la carpeta correcta:
# Guarda la el objeto tabla como un documento de word "hospital_table.docx" save_as_docx(hospital_table, path = here("resultados", "hospital_table.docx"))
Para guardar la tabla flextable como imagen PNG, necesitarás instalar Phantom JS (gratuito) para el uso de la función save_as_image()
Puedes hacerlo instalando el paquete {webshot} (agregalo en el comando pacman), y luego ejecutando el comando webshot::install_phantomjs()
. Considera la posibilidad de hacerlo después de terminar este ejercicio.
# Guarda la tabla como hospital_table.png en la carpeta "resultados" dentro de la carpeta de tu proyecto save_as_image(my_table, path = here("resultados", "hospital_table.png"))
Ahora vamos a comenzar a aprender a usar la función tbl_summary()
de {gtsummary} para hacer tablas presentables
Este paquete nos permite crear tablas listas para publicar con un código muy sencillo y breve. De hecho, tú puedes hacer ajustes más complejos y detallados en estas tablas, pero también es fácil obtener una tabla con mucha estética y con muy poco código.
La diferencia con los métodos anteriores es que primero debes aplicar la función select()
para seleccionar las columnas o varibles que quieres resumir en una tabla.
aplica al objeto vig
con un operador pipe la función select()
y escoge dos columnas y y luego con otro operador pipe conecta la función tbl_summary()
¿Qué se produjo para la(s) columna(s) categórica(s)?
¿Qué se produjo para la(s) columna(s) continua(s)?
También puedes añadir el parámetro by =
en la función tbl_summary()
y designar una columna para estratificar la tabla (en columnas). No olvides incluir esta columna en el select()
¡El comando anterior!
Intenta hacer una tabla que evalúe distrito
, edad_cat
, todas las columnas de síntomas, peso y altura
Ahora haz la misma tabla, estratificada por sexo
.
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
vig %>% select(distrito, edad_cat, fiebre, escalofrios, tos, dolor, vomito, peso_kg, alt_cm, sexo) %>% tbl_summary(by = sexo)
Una nota importante con {gtsummary} es que no puedes exportar directamente la tabla a un documento Word. Para ello, conecta con un operador pipe la tabla con la función as_flex_table()
de {flextable}. Esto permite que {gtsummary} transforme la tabla a un objeto flextable y poder ser exportado a un documento Word.
gt_a_flex <- vig %>% select(distrito, edad_cat, fiebre, escalofrios, tos, dolor, vomito, peso_kg, alt_cm, sexo) %>% tbl_summary(by = sexo) %>% as_flex_table() save_as_docx(gt_a_flex, "mi_tabla.docx")
{gtsummary} facilita la realización de pruebas estadísticas. Por ejemplo, la función add_p()
se puede añadir si tienes la configuración adecuada de la tabla.
Lee más sobre las muchas formas de personalizar una tabla con {gtsummary} aquí.
¡Felicidades! ¡Has terminado el módulo sobre tablas resumen!
{flextable} es una herramienta muy útil para crear tablas listas para ser presentadas.
Empieza creando un nuevo script, llamado "ebola_flextable.R".
Ésta es la tabla que pretendemos crear:
knitr::include_graphics("images/table_final.png", error = F)
Al igual que con el script "ebola_analyses.R", escribe alguna información descriptiva en la parte superior del script.
############################################# # Ejemplo de Ebola con Flextable # Seccion Extra # Tu NOMBRE aqui #############################################
A continuación, añade una sección y carga estos paquetes utilizando pacman::p_load()
:
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Load packages ---------------------------------------------------------------- pacman::p_load( rio, # para importar/exportar here, # para especificar rutas de archivos flextable, # para crear tablas HTML scales, # para funciones de ayuda con flextable officer, # para funciones de ayuda para tablas con formato tidyverse) # para la gestión, resumen y visualización de datos
A continuación, crea una sección de importación e importa listado de casos de ébola depurada, que debes de tener guardadado en tu carpeta "ebola/datos/limpios" como "listado_vigilancia_limpio_20141201.rds". Como copia de seguridad, puedes utilizar la versión de la carpeta "ebola/datos/limpios/copia_seguridad".
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Importando los datos ------------------------------------------------------------- # El listado guardado creado por ti vig <- import(here("datos", "limpios", "listado_vigilancia_limpio_20141201.rds")) # La versión de copia de seguridad vig <- import(here("datos", "limpios", "copia_seguridad", "listado_vigilancia_limpio_20141201.rds"))
A continuación, define el estilo del borde de la tabla en formato flextable mediante la función fp_border()
del paquete {officer}. Utiliza la función para especificar el color del borde de la tabla (negro) y el ancho de la línea del borde (fijado en 1). Siéntete libre de jugar con esta función más adelante.
# define el estilo del borde de la tabla ----------------------------------------------------- border_style = officer::fp_border(color="black", width=1)
Ahora empieza a crear tu tabla, con cambios paso a paso.
Primero, crea una tabla utilizando group_by()
y summarise()
que agrupe las filas del listado por hospital. Crea columnas de resumen que incluyan
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
Al objeto dataframe del listado aplica la función group_by()
con un operador pipe y especifica que se agrupe por la columna hospital
.
Luego con otro operador pipe conecta la función summarise()
y dentro de ésta, especifica tres nuevas columna:
n()
sum()
para contar los casos en los que vomito == "si"
(!no olvides na.rm = TRUE
!
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
# Crea la tabla ----------------------------------------------------------- vig %>% group_by(hospital) %>% # obten la estadística de cada hospital summarise( n_casos = n(), # numero de filas (casos) vomit_n = sum(vomito == "si", na.rm=T), # total de casos con vomito vomit_pct = percent(vomit_n / n_casos)) # proporción de casos con vomito
En este curso hemos explicado que los valores que faltan se registran más adecuadamente en R como NA
. Esto es cierto, pero ahora que estamos preparando el análisis para su presentación, podemos transformar estos valores en palabras más apropiadas. Aún así, este cambio debería producirse sólo en la tabla de salida, no en la base datos subyacente.
Añade la función mutate()
antes group_by()
y transforma la columna hospital
con la función fct_na_value_to_level()
del paquete {forcats} , añadiendo el valor explícito como "Desconocido".
Por último, canaliza la tabla con la función qflextable()
y luego guarda la tabla como un objeto llamado vom_table
.
r fontawesome::fa("lightbulb", fill = "gold")
Haz clic para leer una sugerencia
Intenta buscar en la documentación de Ayuda la función fct_na_value_to_level()
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Create table ----------------------------------------------------------- vom_table <- vig %>% mutate(hospital = fct_na_value_to_level(hospital, "Desconocido")) %>% # cambiar los valores NA a otro nombre group_by(hospital) %>% # obten la estadistica de cada hospital summarise( n_casos = n(), # numero de filas (casos) vomit_n = sum(vomito == "si", na.rm=T), # total de casos con vomito vomit_pct = percent(vomit_n / n_casos)) %>% # proporción de casos con vomito # convert to flextable qflextable()
Recuerda, cuando asignas y guardas un objeto con el operador de asignación <-
para poder presentarlo o verlo debes de ejecutarlo (ejemplo escribir en la consola vom_table
y presionar Enter) para ver la tabla actualizada `.
¡Empecemos a dar formato la tabla para mejor presentación!
Primero, utiliza la función {flextable} función add_header_row()
para añadir una fila de títulos sobre la fila actual. Aquí tienes el código que debes añadir a tu comando (con una tubería).
add_header_row( top = TRUE, # un nuevo encabezado encima del encabezado anterior values = c("Hospital", # valores para un nuevo encabezado "Total casos", "Casos con vomito"), # Este encabezado tendrá un ancho de dos columnas colwidths = c(1,1,2)) # abacar las 4 columnas
Esta sección establece tres nuevos valores de encabezado (el texto) y cuántas de las cuatro columnas subyacentes debe abarcar cada uno. El último valor de encabezado debe abarcar dos columnas subyacentes.
r fontawesome::fa("exclamation", fill = "red")
¡Alerta! si añades una fila de encabezado con menos nombres de columna que el número de columnas subyacentes, verás un error como éste:
Error en inverse.rle(structure(list(lengths = colwidths, values = valores), : estructura 'rle' no válida
Ejecuta el comando vom_table
y observa los cambios en tu visor de RStudio. Deberías ver el segundo encabezado y cómo los "Casos con vómitos" abarcan las dos columnas de la derecha.
Hagamos algunos ajustes manuales en los valores de encabezado. Pasa la tabla a este código:
# Renombra los encabezados secundarios set_header_labels(n_casos = "No.", vomit_n = "No.", vomit_pct = "Porcentaje del total")
Ejecuta el comando vom_table
y observa los cambios en tu visor de RStudio.
Tu comando para la tabla anterior debería tener este aspecto
r fontawesome::fa("check", fill = "red")
Haz clic para ver la solución (¡pruébalo tú primero!)
# Crear tabla ----------------------------------------------------------- vom_table <- vig %>% mutate(hospital = fct_na_value_to_level(hospital, "Desconocido")) %>% # cambiar los valores NA a otro nombre group_by(hospital) %>% # obtén la estadística de cada hospital summarise( n_casos = n(), # numero de filas (casos) vomit_n = sum(vomito == "si", na.rm=T), # total de casos con vómito vomit_pct = percent(vomit_n / n_casos)) %>% # proporción de casos con vómito # convierte a formato flextable qflextable() %>% add_header_row( top = TRUE, # un nuevo encabezado encima del encabezado anterior values = c("Hospital", # valores para un nuevo encabezado "Total casos", "Casos con vómito"), # Este encabezado tendrá un ancho de dos columnas colwidths = c(1,1,2)) %>% # span the 4 columns # Re-label secondary headers set_header_labels(n_casos = "No.", vomit_n = "No.", vomit_pct = "Porcentaje del total")
A continuación, ajustaremos la alineación de los valores en las celdas y combinaremos algunos de los valores.
Para realizar ediciones específicas de formato en diferentes áreas (filas, columnas o celdas debemos comprender la sintaxis de localización de {flextable}. Muchas de las funciones de este paquete utilizan la siguiente sintaxis:
function(table, part = "", i = , j = )
Vamos a entenderla:
width()
para determinar la anchura de las columnas, bg()
establecer colores de fondo, align()
fijar la alineación centro/derecha/izquierda.table =
es el nombre del objeto dataframe, No es necesario si en la tabla flextable se introduce en la función.part =
se refiere a qué parte de la tabla se está aplicando la función. Por ejemplo, "encabezado", "cuerpo" o "todo".i =
especifica el números de fila a las que se quiere aplicar la función, por ejemploi = 4
(cuarta fila)i = c(1:3)
(de la primera a la tercera fila)j =
especifica el números de columna a las que se quiere aplicar la función, por ejemploj = 1
(primera columna)j = c(5, 8)
(5ª y 8ª columna)Puedes encontrar la lista completa de la función de formato de la tabla flextable aquí o revisar la documentación entrando en ?flextable
. También puedes ver otro ejemplo trabajado en Manual de Epi R.
Introduce tu tabla en el siguiente código:
# Centra y une celdas align(part = "all", align = "center", j = c(2:4)) %>% # centraliza los valores en desde la segunda columna hasta la cuarta merge_at(i = 1:2, j = 1, part = "header") # Une de forma vertical el título "Hospital"
Ejecuta el comando vom_table
y observa los cambios en tu visor de RStudio.
Tus comandos de la tabla actualizada debería tener este aspecto
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Crea una tabla ----------------------------------------------------------- vom_table <- vig %>% mutate(hospital = fct_na_value_to_level(hospital, "Desconocido")) %>% # cambiar los valores NA a otro nombre group_by(hospital) %>% # obten la estadistica de cada hospital summarise( n_casos = n(), # numero de filas (casos) vomit_n = sum(vomito == "si", na.rm=T), # total de casos con vomito vomit_pct = percent(vomit_n / n_casos)) %>% # proporción de casos con vomito # convierte a formato flextable qflextable() %>% add_header_row( top = TRUE, # Nuevo encabezado encima del encabezado actual colwidths = c(1,1,2), # Extender el encabezado en el ancho de 4 columnas values = c("Hospital", # Valores para los nuevos encabezados "Total de casos", "Cases con vómito")) %>% # Esto expande el titulo al ancho de dos columnas # Nuevos nombres para los encabezados secundarios (de las columnas) set_header_labels(n_casos = "No.", vomit_n = "No.", vomit_pct = "Porciento del total") %>% # Alinea y une celdas align(part = "all", align = "center", j = c(2:4)) %>% # centraliza los valores en desde la segunda columna hasta la cuarta merge_at(i = 1:2, j = 1, part = "header") # Une de forma vertical el titulo "Hospital"
Por último, ajustaremos la fuente y el color de fondo de la tabla.
Añade el código siguiente a tu cadena comandos.
# Cambia el color de fondo para columnas especificas bg(part = "body", # Cuerpo de la tabla (excluyendo el cuerpo) j = 4, # 4ta columna bg = "gray95") %>% # Color Gris # Cambia el color condicionalmente bg(j = 4, # 4ta columna i = ~ vomit_pct >= 55, # El ~muestra que que filas seran afectadas por la condicion declarada part = "body", # parte de la tabla que será actualizada (el cuerpo por ejemplo- no el encabezado) bg = "orange") %>% # El color del fondo # Resaltar el encabezado bold(i = 1, # la primera fila del encabezado bold = TRUE, part = "header")
La opción más sencilla es utilizar add_footer_lines()
para añadir una celda grande que abarque la anchura de toda la tabla.
Añadir referencias en la tabla (con números en superíndice y referencias a pie de página) puede ser más complicado.
Añade una fila de pie de página a tu tabla que explique la fuente de datos y la lógica que hay detrás del coloreado condicional. Puntos extra si la creas con str_glue()
para que parte del texto sea dinámico, ¡y ponlo en cursiva!
A continuación, utiliza la función inversa add_header_lines()
para dar un título a tu tabla. ¡Puntos extra si luego alineas este título con el centro!
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
Introduce tu tabla en este código de la siguiente manera
#añadir pie de pagina add_footer_lines(values = str_glue( "Casos notificados al {max_report_date}. Hospitales con >=55% con vómito en destacado.", max_report_date = max(vig$fecha_notifica, na.rm = T))) %>% italic(part = "footer") %>% # añadir titulo add_header_lines(values = "Casos con Vómito por hospital") %>% align(part = "header", i = 1, align = "center")
Tu comando de tabla completa debería tener este aspecto
r fontawesome::fa("check", fill = "red")
Haz clic para ver una solución (¡pruébalo tú primero!)
# Create table ----------------------------------------------------------- # Crea una tabla ----------------------------------------------------------- vom_table <- vig %>% mutate(hospital = fct_na_value_to_level(hospital, "Desconocido")) %>% # cambiar los valores NA a otro nombre group_by(hospital) %>% # obten la estadistica de cada hospital summarise( n_casos = n(), # numero de filas (casos) vomit_n = sum(vomito == "si", na.rm=T), # total de casos con vomito vomit_pct = percent(vomit_n / n_casos)) %>% # proporción de casos con vomito # convierte a formato flextable qflextable() %>% add_header_row( top = TRUE, # Nuevo encabezado encima del encabezado actual colwidths = c(1,1,2 ), # Extender el encabezado en el ancho de 4 columnas values = c("Hospital", # Valores para los nuevos encabezados "Total de casos", "Cases con vómito")) %>% # Esto expande el titulo al ancho de dos columnas # Nuevos nombres para los encabezados secundarios (de las columnas) set_header_labels(n_casos = "No.", vomit_n = "No.", vomit_pct = "Porciento del total") %>% # Centra y une celdas align(part = "all", align = "center", j = c(2:4)) %>% # centraliza los valores en desde la segunda columna hasta la cuarta merge_at(i = 1:2, j = 1, part = "header") %>% # Une de forma vertical el titulo "Hospital" # Cambia el color de fondo para columnas específicas bg(part = "body", # Cuerpo de la tabla (excluyendo el cuerpo) j = 4, # 4ta columna bg = "gray95") %>% # Color Gris # Cambia el color condicionalmente bg(j = 4, # 4ta columna i = ~ vomit_pct >= 55, # El ~muestra que que filas seran afectadas por la condicion declarada part = "body", # parte de la tabla que sera actualizada (el cuerpo por ejemplo- no el encabezado) bg = "orange") %>% # El color del fondo # Resaltar el encabezado bold(i = 1, # la primera fila del encabezado bold = TRUE, part = "header") %>% #añadir pie de pagina add_footer_lines(values = str_glue( "Casos notificados al {max_report_date}. Hospitales con >=55% con vómito en destacado.", max_report_date = max(vig$fecha_notifica, na.rm = T))) %>% italic(part = "footer") %>% # añadir titulo add_header_lines(values = "Casos con Vomito por hospital") %>% align(part = "header", i = 1, align = "center")
Por último, guarda la tabla en un documento de Word, Powerpoint o la salida que prefieras. Recuerda que este comando no debe estar conectado por un operdador pipe al comando de creación de la tabla.
Este código debería funcionar para guardarla como Word en la carpeta "resultados" del proyecto RStudio.
# Save save_as_docx(vom_table, path = here("resultados", "vom_table.docx"))
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.