# load packages ----------------------------------------------------------------
library(introexercises)
library(learnr)
library(gradethis)
library(dplyr)
library(flair)
library(ggplot2)
library(lubridate)
library(fontawesome)
library(janitor)
library(kableExtra)
# 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 data 
# ## 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, data) {
#     
#   ## 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, "',
#                        '", data$section, "',
#                        '", data$label,  "',
#                        '", paste0('"', data$question, '"'),  "',
#                        '", paste0('"', data$answer,   '"'),  "',
#                        '", paste0('"', data$code,     '"'),  "',
#                        '", data$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)


# Data prep --------------------------------------------------------------------
# Import
surv <- rio::import(system.file("dat/surveillance_linelist_clean_20141201.rds", package = "introexercises")) 

hospitals <- bind_rows(rio::import(system.file("dat/hospitals/20141201_hosp_port.csv", package = "introexercises")),
                    rio::import(system.file("dat/hospitals/20141201_hosp_central.csv", package = "introexercises"))) %>% 
             select(hospital, date_hospitalisation, outcome, date_outcome) %>% 
             janitor::clean_names()

## NOT USED ANYMORE
# geo_data <- rio::import(system.file("dat/pop/sle_admpop_adm3_2020.csv", package = "introexercises")) %>% 
#                  select(-c(Female, Male), -starts_with("T"))


## NOT USED ANYMORE Make the hospital information dataframe
# hospital_dirty = data.frame(
#   hosp_name     = c("central hospital", "military", "military", "port", "St. Mark's", "ignace", "sisters"),
#   catchment_pop = c(1950280, 40500, 10000, 50280, 12000, 5000, 4200),
#   level         = c("Tertiary", "Secondary", "Primary", "Secondary", "Secondary", "Primary", "Primary")
# )
# 
# hospital_clean <- hospital_dirty %>% 
#   mutate(
#     hosp_name = case_when(
#       # criteria                       # new value
#       hosp_name == "military"          ~ "Military Hospital",
#       hosp_name == "port"              ~ "Port Hospital",
#       hosp_name == "St. Mark's"        ~ "St. Mark's Maternity Hospital (SMMH)",
#       hosp_name == "central hospital"  ~ "Central Hospital",
#       TRUE                             ~ hosp_name
#       )
#     )

# Create smaller linelists for the easier exemples
patients <- tibble(ID = c("patient_1", "patient_2", "patient_3", 
                            "patient_4", "patient_10"), 
                     sexe = c("F", "M", "M", "F", "F"), 
                     age = c(5, 10, 2, 15, 14), 
                     age_unit = c("Year", "Year", "Year", "Year", "Year"))

results <- tibble(ID = c("patient_1", "patient_2", "patient_4", 
                        "patient_5", "patient_6"), 
                 test_result = c("positive", "negative", 
                                 "negative", "positive", "positive"))


df1 <- tibble(ID = c("patient_1", "patient_2", "patient_3"),
              sexe = c("F", "M", "M"))

df2 <- tibble(ID = c("patient_1", "patient_1", "patient_1", "patient_2", "patient_4"),
              date_test = as.Date(c("2021-12-01", "2021-12-26", "2022-01-05", "2021-12-18", "2022-01-01")),
                 test_result = c("positive", "negative", "negative", "positive", "positive"))

hosp_central <- rio::import(system.file("dat/hospitals/20141201_hosp_central.csv", package = "introexercises"))
hosp_military <- rio::import(system.file("dat/hospitals/20141201_hosp_military.csv", package = "introexercises"))
hosp_port <- rio::import(system.file("dat/hospitals/20141201_hosp_port.csv", package = "introexercises"))
hosp_smmh <- rio::import(system.file("dat/hospitals/20141201_hosp_smmh.csv", package = "introexercises"))
hosp_other <- rio::import(system.file("dat/hospitals/20141201_hosp_other.csv", package = "introexercises"))
hosp_missing <- rio::import(system.file("dat/hospitals/20141201_hosp_missing.csv", package = "introexercises"))

lab <- rio::import(system.file("dat/lab_results_20141201.xlsx", package = "introexercises"))
investigations <- rio::import(system.file("dat/case_investigations_20141201.xlsx", package = "introexercises"))
# hide non-exercise code chunks ------------------------------------------------
knitr::opts_chunk$set(echo = FALSE)

Introducción a R para Epidemiología Aplicada y Salud Pública

Bienvenido

Bienvenido al curso "Introducción a R para epidemiología aplicada", ofrecido por Epidemiología - 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 de la salud pública.

knitr::include_graphics("images/logo.png", error = F)

Unir datos

Este ejercicio se centra en unir marcos de dato.

Formato

Este ejercicio te guía a través de las tareas que debes realizar en RStudio en tu ordenador lo.

Obtener ayuda

Hay varias formas de obtener ayuda:

1) Busca a los "ayudantes" (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 Comunidad Epi Aplicada

Este es el aspecto que tendrán esos "ayudantes":

r fontawesome::fa("lightbulb", fill = "gold") Haz clic para leer una sugerencia

¡Aquí verás una pista útil!


r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

linelist %>% 
  filter(
    age > 25,
    district == "Bolo"
  )

Aquí tienes más explicaciones sobre por qué funciona la solución.


Preguntas del concurso

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("When should I view the red 'helper' code?",
    answer("After trying to write the code myself", correct = TRUE),
    answer("Before I try coding", correct = FALSE),
    correct = "Reviewing best-practice code after trying to write yourself can help you improve",
    incorrect = "Please attempt the exercise yourself, or use the hint, before viewing the answer."
  )
)
question_numeric(
 "How anxious are you about beginning this tutorial - on a scale from 1 (least anxious) to 10 (most anxious)?",
 answer(10, message = "Try not to worry, we will help you succeed!", correct = T),
 answer(9, message = "Try not to worry, we will help you succeed!", correct = T),
 answer(8, message = "Try not to worry, we will help you succeed!", correct = T),
 answer(7, message = "Try not to worry, we will help you succeed!", correct = T),
 answer(6, message = "Ok, we will get there together", correct = T),
 answer(5, message = "Ok, we will get there together", correct = T),
 answer(4, message = "I like your confidence!", correct = T),
 answer(3, message = "I like your confidence!", correct = T),
 answer(2, message = "I like your confidence!", correct = T),
 answer(1, message = "I like your confidence!", correct = T),
 allow_retry = TRUE,
 correct = "Thanks for sharing. ",
 min = 1,
 max = 10,
 step = 1
)

Licencia

Por favor, envía un correo electrónico a contact@appliedepi.org si tienes preguntas sobre el uso de estos materiales.

Objetivos de aprendizaje

En este ejercicio

Preparación

Abre tu proyecto R y el script R Markdown.

Ejecuta cada fragmento de código de tu script R Markdown, fragmento a fragmento, utilizando el botón verde de reproducción situado en la esquina superior derecha de cada fragmento. Los tres trozos siguientes son de especial importancia:

Ahora deberías haber cargado correctamente los paquetes necesarios y poblado tu entorno con surv_raw y surv.

Crear un fragmento de código para unir datos

Crea un nuevo fragmento para "Unir datos" debajo de tu fragmento de código de limpieza de datos (para crear surv).

Puedes escribir un comentario en el chunk para indicar su propósito

# Joining data  

Organización del guión

NOTA: Navegaremos a través de nuestro script R Markdown con frecuencia, ¡así que asegúrate de que está bien organizado!

Lo ideal es que tu guión se ajuste aproximadamente a este esquema:

  1. YAML
  2. Opciones de configuración
  3. Cargar paquetes
  4. Importar datos
  5. (Opcional) Análisis exploratorio
  6. Limpiar la lista de vigilancia
  7. NUEVO - Unir datos
  8. Resumen textual del brote
  9. Tablas resumen
  10. Parcelas
  11. (Opcional) Zona de pruebas

Asegúrate de que eval = FALSE en los dos fragmentos de código opcionales para que no se impriman en el informe.

Vinculación de filas

Combinar conjuntos de datos a veces puede ser tan sencillo como combinar las filas de los marcos de datos que tienen exactamente las mismas columnas. Por ejemplo, si tienes varios centros de estudio, o centros clínicos, que te envían datos exactamente en el mismo formato.

Conjuntos de datos hospitalarios

En nuestro estudio de caso del brote de ébola, hay seis conjuntos de datos de varios hospitales en la subcarpeta "data/raw/hospitals" del proyecto R "ebola" :

Cada archivo corresponde a un hospital concreto (Central, Militar, Puerto, San Marcos). Uno contiene registros de pacientes de otros hospitales, y otro con pacientes de los que faltan los datos del hospital.

En el fragmento de código para Importar datos (cerca de la parte superior de tu script), añade comandos para importar los archivos de los hospitales desde la subcarpeta "data/raw/hospitals". Nombra los marcos de datos en R como hosp_central, hosp_port, hosp_missing etc.

Recuerda que puedes ejecutar trozos enteros de código con su botón verde de reproducción, o puedes ejecutar líneas concretas dentro de de un fragmento colocando el cursor y pulsando Ctrl y Enter (o Cmd Enter en un Mac).

Una vez que hayas añadido estos comandos, vuelve a ejecutar todo el chunk, para importar los 6 conjuntos de datos del hospital.

r fontawesome::fa("check", fill = "red")Haz clic para ver una solución (¡pruébalo tú primero!)

Como recordatorio debes escribir estos comandos en el fragmento de código de importación de datos de tu R Markdown, para una organización óptima. Este es el aspecto que debe tener tu fragmento de código de importación de datos:

# Import data -------------------------------------------------------------

# surveillance dataset
surv_raw <- import(here("data", "raw", "surveillance_linelist_20141201.csv"))

# hospital datasets
hosp_central  <- import(here("data", "raw", "hospitals", "20141201_hosp_central.csv"))
hosp_military <- import(here("data", "raw", "hospitals", "20141201_hosp_military.csv"))
hosp_other    <- import(here("data", "raw", "hospitals", "20141201_hosp_other.csv"))
hosp_port     <- import(here("data", "raw", "hospitals", "20141201_hosp_port.csv"))
hosp_smmh     <- import(here("data", "raw", "hospitals", "20141201_hosp_smmh.csv"))
hosp_missing  <- import(here("data", "raw", "hospitals", "20141201_hosp_missing.csv"))



Navega hasta tu Entorno y haz clic en los marcos de datos para observar sus nombres de columna.

quiz(caption = "Quiz - Hospital linelists",
    question("Are the column names the same in each of the six hospital linelists?",
    allow_retry = T,
    answer("Yes", correct = T),
    answer("No")
  )
)

Como los nombres de las columnas son los mismos, puedes simplemente "unir" las filas, apilando digitalmente los 6 marcos de datos uno sobre otro.

Esto se hace mejor con la función bind_rows() del paquete {dplyr}. Esta función es flexible, ya que no es necesario que las columnas estén en el mismo orden para que los marcos de datos se unan. Alineará las columnas automáticamente: sólo deben tener los mismos nombres.

Ésta es la sintaxis: es bastante sencilla:

bind_rows(df1, df2, df3, df4, ...)

Arriba, los valores "dfX" se sustituirían por los nombres de los marcos de datos que deseas combinar.

En tu fragmento de código "Unir datos (justo debajo de tu fragmento de limpieza de datos), escribe y ejecuta un comando que una los seis conjuntos de datos del hospital, y guarda el resultado en un objeto llamado hosp.

r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

hosp <- bind_rows(hosp_central, 
                  hosp_port, 
                  hosp_military, 
                  hosp_smmh, 
                  hosp_other, 
                  hosp_missing)

El orden de los marcos de datos dentro del bind_rows() no es importante, a efectos de este ejemplo, ya que los 6 conjuntos de datos de hospitales tienen los mismos nombres de columna y el mismo número de columnas.



Sabrás que los comandos han funcionado si ves aparecer el objeto hosp en tu entorno.

Ahora haz clic en hosp para ver el marco de datos que has creado. ¿Parece correcto?

Se une a la revisión

A menudo, querrás combinar datos utilizando uniones y no simplemente uniendo filas. Dediquemos un momento a repasar los tipos de uniones:

Revisión de los tipos de uniones

Tómate unos minutos para repasar la información que aparece a continuación. Te la damos en forma de descripción textual, de esquema y de animación (fuente).

NOTA las uniones más comunes en contextos de salud pública son uniones a la izquierda y antiuniones.

Esquema

knitr::include_graphics("images/summary_joins.png", error = F)

Descripciones textuales

Uniones mutantes: añade nuevas variables a un marco de datos
Filtrar uniones: filtra un marco de datos a partir de otro

Animaciones

knitr::include_graphics("images/left-join.gif", error = F)
knitr::include_graphics("images/right-join.gif", error = F)
knitr::include_graphics("images/full-join.gif", error = F)
knitr::include_graphics("images/inner-join.gif", error = F)
knitr::include_graphics("images/anti-join.gif", error = F)
knitr::include_graphics("images/semi-join.gif", error = F)

Otros recursos

Demuestra que lo entiendes

Repasa estos dos mini marcos de datos:

Datos del paciente (denominados patients)

patients %>% kbl() %>% kable_styling(full_width = FALSE, 
                                       bootstrap_options = c("striped", "hover", "condensed"))

Resultados de laboratorio (nombrados results)

results %>% kbl() %>% kable_styling(full_width = FALSE, 
                                   bootstrap_options = c("striped", "hover", "condensed"))

Ahora contesta a las preguntas del cuestionario.

quiz(caption = "Which join would you use:",
     question("To add the age and sex for all patients into the lab database, if available there",
              answer("left_join(patients, results, by = 'ID')"),
              answer("right_join(patients, results, by = 'ID')",
                     correct = TRUE),
              answer("inner_join(patients, results, by = 'ID')",
                     message = "An inner join would not include patients from the results database without a match in the patient database. Rows for patient 5 and 6 would be lost"),
              answer("full_join(patients, results, by = 'ID')",
                     message = "An full join would bring in rows with no results from the patient database and add unwanted NA. Here we want to use the results dataframe as the reference."),
              answer("anti_join(patients, results, by = 'ID')"),
              answer("semi_join(patients, results, by = 'ID')"),
              allow_retry = TRUE),


     question("To get a dataframe with patients for which we have data for age, sex and test result",
              answer("left_join(patients, results, by = 'ID')"),
              answer("right_join(patients, results, by = 'ID')"),
              answer("inner_join(patients, results, by = 'ID')",
                     correct = TRUE),
              answer("full_join(patients, results, by = 'ID')",
                     message = "The full join would include all lines from the two dataframes, even if they do not have a match in the other dataframe"),
              answer("anti_join(patients, results, by = 'ID')"),
              answer("semi_join(patients, results, by = 'ID')"),
              allow_retry = TRUE),


     question("To keep all the patients from the results dataframe for which we have age and sex data (but not add those columns to the results dataframe)",
              answer("left_join(results, patients, by = 'ID')"),
              answer("right_join(patients, results, by = 'ID')"),
              answer("inner_join(results, patients, by = 'ID')",
                     message = "You are close. But an inner join in this case would import the columns from the patients dataframe, which we do not want"),
              answer("full_join(results, patients,  by = 'ID')"),
              answer("anti_join(patients, results, by = 'ID')"),
              answer("semi_join(results, patients, by = 'ID')",
                     correct = TRUE),
              allow_retry = TRUE), 


     question("To get the list of patients for whom we have age and sex, but no test result",
              answer("left_join(patients, results, by = 'ID')"),
              answer("right_join(patients, results, by = 'ID')"),
              answer("inner_join(patients, results, by = 'ID')"),
              answer("full_join(patients, results, by = 'ID')"),
              answer("anti_join(patients, results, by = 'ID')",
                     correct = TRUE),
              answer("semi_join(results, patients, by = 'ID')"),
              allow_retry = TRUE),


     question("Which of these would return only the rows found in patients?",
              answer("inner_join(patients, results, by = 'ID')",
                     correct = FALSE),
              answer("full_join(patients, results, by = 'ID')",
                     correct = FALSE),
              answer("left_join(patients, results, by = 'ID')",
                     correct = TRUE),
              allow_retry = TRUE),


     question("Which of these would use NA to fill-in values in rows that did not match?",
              answer("full_join(patients, results, by = 'ID')",
                     correct = TRUE),
              answer("semi_join(patients, results, by = 'ID')",
                     correct = FALSE),
              answer("anti_join(patients, results, by = 'ID')",
                     correct = FALSE),
              answer("inner_join(patients, results, by = 'ID')",
                     correct = FALSE),
              allow_retry = TRUE)
)

Únete a

Sintaxis

La sintaxis de las funciones de unión es siempre la misma:

NO AGREGUES EL CÓDIGO DE ABAJO EN TU R Markdown. Se trata simplemente de mostrar el formato de las funciones de unión.

XXX_join(dataframe1,      # "baseline" dataframe
         dataframe2,      # other dataframe
         by = "column")   # column used to match rows


# Or, with a pipe:
dataframe1 %>%                # "baseline" dataframe
     XXX_join(dataframe2,     # other dataframe
              by = "column")  # columns used to match rows

Si quieres unir marcos de datos basándote en dos o más columnas, puedes proporcionar las columnas a la función by = dentro de un vector c() separadas por comas. Las filas sólo coincidirán entre sí si los valores de todas esas columnas son exactamente iguales. En la unión siguiente, ambos marcos de datos tienen las columnas age y sex y las filas coinciden si tienen los mismos valores en ambas columnas.

NO AGREGUES EL CÓDIGO DE ABAJO EN TU R Markdown. Esto es simplemente un ejemplo de cómo unir dos columnas.

XXX_join(dataframe1, dataframe2,      
         by = c("age", "sex")   # columns used to match rows

Muchas veces, los nombres de columnas comparables en los dos marcos de datos son diferentes. Por ejemplo, las edades pueden registrarse en la columna age en un marco de datos, y registrarse en la columna Ages en el segundo marco de datos. Puedes indicarle al join los dos nombres utilizando el símbolo = igual dentro del vector c(). Esta unión a continuación coincide con las filas cuando el valor en age en el primer marco de datos es el mismo que el valor en Ages en el segundo marco de datos.

NO AGREGUES EL CÓDIGO DE ABAJO EN TU R Markdown. Esto es sólo a modo de ejemplo.

XXX_join(dataframe1, dataframe2,      
         by = c("age" = "Ages")   # columns with different names

Por último, a continuación se muestra un ejemplo de ambas sintaxis juntas. Este comando coincide con las filas cuando:

NO AGREGUES EL CÓDIGO DE ABAJO EN TU R Markdown. Esto es simplemente una muestra de cómo unir basándose en la coincidencia de varias columnas, donde algunas de las columnas que coinciden tienen nombres diferentes en cada marco de datos respectivo.

XXX_join(dataframe1, dataframe2,      
         by = c("age" = "Ages",  # different column names
                "sex" = "Sex",   # different column names
                "city")          # both data frames have column "city"

Unir datos hospitalarios

Ahora que tenemos un hosp (hospital), unámosla a la lista de líneas surv lista de vigilancia.

Inspecciona los datos antes de unirlos

Es muy importante conocer tus conjuntos de datos an unirlos y anticiparte a lo que ocurrirá cuando te unas.

Muchas cosas pueden ir mal y quedar ocultas a la vista. Dedica tiempo por adelantado a entenderlo:

Gran parte de estas pruebas previas a la unión se producirán en un fragmento de código de área de Pruebas.

Revisar dimensiones

En el fragmento Código de área de prueba, utiliza la función dim() en los dos marcos de datos.

dim() es un atajo para devolver el número de filas y el número de columnas. Como alternativa, puedes utilizar nrow() y ncol() individualmente.

¿Cómo se calculan las dimensiones de surv y hosp ¿son comparables?

r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

dim(surv)
dim(hosp)


Revisar duplicados

Ahora, sería bueno saber si hay identificadores duplicados en el surv marco de datos, o en el hosp marco de datos. Los duplicados pueden dar lugar a uniones que aumenten el número de filas del marco de datos base.

Utiliza tus conocimientos del módulo sobre tablas resumen, escribe un comando en tu trozo del Área de Pruebas que muestre el surv identificadores (case_id) que aparecen más de una vez.

r fontawesome::fa("lightbulb", fill = "gold") Haz clic para leer una sugerencia

Puedes canalizar el surv conjunto de datos en count(case_id) para obtener una tabla de recuentos de todos los case_id valores en surv. Luego pasa a filter() y especifica un criterio lógico para que sólo queden los que aparezcan más de una vez.


r fontawesome::fa("check", fill = "red")Haz clic para ver una solución (¡pruébalo tú primero!)

surv %>% 
  count(case_id) %>% 
  filter(n > 1)


Ahora haz lo mismo con el marco de datos hosp utilizando la columna identificador ID también en tu trozo "Zona de pruebas".

Ahora sabemos que hay algunos pacientes que tienen varias filas en surv. Lo ideal sería investigar estos duplicados para ver si corresponden a visitas diferentes, o si algunos pacientes se introdujeron dos veces por error. Sin embargo, hoy nos centramos en la unión, así que vamos a quedarnos con todos estos registros.

Te preguntarás: ¿Por qué no se eliminaron estos duplicados durante los pasos de limpieza de datos? La respuesta es que utilizamos distinct() que elimina por defecto las filas que son completamente duplicados de otra fila. Estas filas que identificamos aquí tienen duplicados case_id pero valores diferentes en otras columnas.

Revisar los valores de los identificadores

Antes de unir, es muy importante considerar si la(s) columna(s) utilizadas para unir los dos marcos de datos están limpias.

La dirección join funciones de {dplyr} utilizan correspondencia exacta los valores deben ser exactamente iguales en ambos marcos de datos para que coincidan.

Nota: Otros paquetes realizan correspondencias probabilísticas ("difusas") (véase la página capítulo sobre uniones en el Manual de Epi R).

¿Sabemos si los ID de los pacientes se introdujeron de la misma forma en ambos marcos de datos? Si los datos proceden de una base de datos limpia y son una clave primaria, probablemente. Con datos de Excel introducidos por muchas personas distintas en entornos de urgencias, quizá no. En general, es una buena regla escudriñar las columnas que quieres utilizar como identificadores/claves para unir tus.

Dedica algo de tiempo a examinar la estructura de los valores en la columna surv columna case_id. ¿Siguen el mismo patrón que ID en la columna hosp marco de datos?

Veamos juntos una muestra de identificadores de ambos marcos de datos. Los comandos siguientes ordenan ambos marcos de datos por su columna de identificadores, y luego devuelven sólo esa columna con pull() y, a continuación, toma sólo los 10 primeros valores.

Ejecuta el código siguiente en tu trozo de código "Zona de pruebas si quieres ver el resultado en RStudio. También puedes ver a continuación los 10 primeros valores identificadores de surv.

# for each dataset, sort and print the first 10 identifier values

# for the surveillance dataset
surv %>% 
  arrange(case_id) %>%
  pull(case_id) %>%
  head(10)

Ten en cuenta que si ves aparecer la salida anterior en dos líneas no se debe a ninguna diferencia en los valores. Hay 10 valores producidos por head(10)pero como la ventana es estrecha, se han enrollado. Observa los números entre paréntesis (por ejemplo [9]) que te indican qué valor inicia la nueva línea.

Ejecuta el mismo código en tu trozo de código "Zona de pruebas", pero esta vez para hosp.

# for the hospital dataset
hosp %>%
  arrange(ID) %>%
  pull(ID) %>%
  head(10)

Parece que los identificadores utilizan patrones y estilo similares. Con datos muy sucios, debes inspeccionar los identificadores por si se utilizaron varios patrones de identificadores dentro del mismo archivo.

Revisar solapamiento

Ahora ejecutaremos un anti_join() para comprender cómo se relacionan los dos marcos de datos.

Escribe un comando en tu trozo "Unir", que realice una anti_join() primero utilizando hosp como marco de datos "base". Asigna la salida a un objeto llamado anti_surv.

r fontawesome::fa("lightbulb", fill = "gold") Haz clic para leer una sugerencia

Utiliza anti_join(dataframe1, dataframe2, by = ). Recuerda que las columnas identificadoras de los dos marcos de datos tienen nombres diferentes, por lo que debes indicar al join que deben compararse con un =, y dentro de un vector c().


r fontawesome::fa("check", fill = "red")Haz clic para ver una solución (¡pruébalo tú primero!)

anti_surv <- anti_join(hosp, surv, by = c("ID" = "case_id"))  # Which rows in hosp are NOT present in surv?



Podemos ver que hay algunos pacientes en hosp que no están presentes en surv. Ahora realiza lo contrario anti_join() utilizando hosp como línea de base, también en tu trozo de código "Unir".

¿Hay algún paciente en surv no presentes en hosp? Recuerda intercambiar los nombres de las columnas en by = para que coincidan con el orden de los marcos de datos unidos.

r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

¿Te has acordado de cambiar el orden de los by = así como los marcos de datos?

anti_hosp <- anti_join(surv, hosp, by = c("case_id" = "ID")) # Which rows in surv are not present in hosp?


Así vemos que hay 0 filas de esa segunda antiunión. El surv marco de datos tiene todos los pacientes que están en el hosp marco de datos, y que el hosp marco de datos es un subconjunto del surv pacientes.

quiz(caption = "Quiz - Data inspections",
    question("If you were to conduct a full_join() with surv as the baseline and hosp joined into it, how many *additional* rows would you expect to appear in the joined data frame?",
    allow_retry = T,
    answer("0", message = "We are asking about a 'full' join."),
    answer("17"),
    answer("40", correct = T, message = "Remember, a full join includes ALL rows from both data frames."),
    answer("5 rows would be removed")
  )
)

Revisa los nombres de las columnas

Es importante saber cuáles son los nombres de las columnas de cada marco de datos.

Repasa de nuevo los nombres de las columnas de c ejecutando el siguiente código en tu trozo de código "Área de pruebas":

surv %>% names()
hosp %>% names()
quiz(caption = "Quiz - Compare columns",
  question("Which columns in the hosp linelist also exist in the surv linelist?",
    allow_retry = T,
    answer("hospital", correct = T),
    answer("date_outcome"),
    answer("age", correct = T),
    answer("age_unit", correct = T),
    answer("sex", correct = T)
  ),
  question("Which columns exist only in the hosp linelist?",
    allow_retry = T,
    answer("date_hospitalisation", correct = T),
    answer("age"),
    answer("time_admission", correct = T),
    answer("outcome", correct = T),
    answer("date_outcome", correct = T)
  )
)

Hagamos un experimento: escribe y ejecuta un comando en el fragmento "Unir" para unir los dos marcos de datos:

r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

test_join <- surv %>% 
  left_join(hosp, by = c("case_id" = "ID"))


¿Cuáles son las dimensiones del nuevo marco de datos? Ejecuta dim(test_join) en tu trozo "Zona de pruebas y compáralo con las dimensiones anteriores de surv.

Ahora, pulsa test_join en tu Entorno R para verlo de cerca. Observa las columnas cerca del lado izquierdo que formaban parte del original surv marco de datos original, y luego desplázate a la derecha para ver las nuevas columnas que se han añadido, desde la columna hosp marco de datos.

¿Qué notas en las columnas sex, age, age_unit y hospital?

quiz(caption = "Quiz - Joins",
    question("What happened to these columns?",
    allow_retry = T,
    answer("They were removed"),
    answer("They were all moved to the left end of the data frame"),
    answer("They appear twice - once with '.x' appended, and again with '.y' appended", correct = T)
  )
)

Estos nombres de columna existían en ambos marcos de datos y NO se utilizaron en by = para emparejar filas, así que cuando se unieron los datos, R tuvo que diferenciar entre las columnas duplicadas añadiendo los sufijos .x y .y.

¿Los valores son los mismos en ambos sex.x y en sex.y ¿Cómo puedes examinarlo?

Como ahora están en el mismo marco de datos, puedes hacer una tabulación cruzada de las dos columnas. Ejecuta el código siguiente en tu fragmento de código "Zona de pruebas" para imprimir los resultados.

# cross tabulate the two columns in the joined dataset
test_join %>% 
  tabyl(sex.x, sex.y)

Puedes ver que sex.x (del original surv que limpiaste en un módulo anterior) tiene los valores "mujer" y "hombre".

Por el contrario, sex.y que procede de la lista de hospitales, tiene los valores "f", "m", "" y NA.

¿Qué hacer con esta situación? Esta es una situación clásica cuando se intenta combinar conjuntos de datos: ¿qué hacer con las columnas redundantes?

¿Pero son realmente columnas redundantes? Tendrás que hacerte estas preguntas:

Son preguntas que sólo puedes responder sobre tu conjunto de datos.

Tienes tres opciones:

1) Realiza la unión como antes, para mantener todas las columnas

2) Establece todas las columnas redundantes como identificadores en la unión

test <- full_join(surv, hosp,
          by = c(
            "case_id" = "ID",
            "age",                    # the other columns are
            "age_unit",               # named the same in both 
            "sex",
            "hospital"))

3) Eliminar las columnas redundantes antes de la fusión - Si no te interesa conservar las columnas redundantes en el segundo marco de datos, elimínalas con select() antes de la unión.

Para este ejercicio, vuelve al fragmento de código "Unir" de tu R Markdown y escribe un código que utilice Opción 3.

1) Ir a la página bind_rows() comando que crea hosp y pásalo a un select que sólo deja "ID" y las nuevas columnas. 2) Aprovecha la función de renombrar de select() para cambiar el nombre de "ID" a "case_id", de forma que el comando join sea más sencillo. 3) Vuelve a ejecutar el comando

hosp <- bind_rows(hosp_central, hosp_port, hosp_military,
                  hosp_smmh, hosp_other, hosp_missing) %>% 

  # select specific columns from hosp, and re-name ID as case_ID
  select(
    case_id = ID,          # select and rename
    date_hospitalisation,  # select
    time_admission,        # select
    date_outcome,          # select
    outcome)               # select

4) Ahora, en tu trozo de código de Unión, escribe un left_join() para surv y hosp utilizando case_id como identificador común. Asigna el objeto unido de salida a un marco de datos llamado combined.

r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

# Join the two data frames with a left join
combined <- left_join(surv, hosp, by = "case_id")



¡Enhorabuena por completar tu primera unión!

Unirse a los datos del laboratorio

Ahora tenemos que unir los resultados de las pruebas de laboratorio.

Sigue los pasos siguientes para unir el marco de datos de laboratorio al marco de datos combined marco de datos

Importa y limpia los datos del laboratorio

En el trozo de código de importación (cerca de la parte superior), escribe y ejecuta un comando para hacer lo siguiente:

¡Asegúrate de ejecutar el código una vez que hayas escrito el comando!

r fontawesome::fa("check", fill = "red")Haz clic para ver una solución (¡pruébalo tú primero!)

# Import lab data
# (place this in the importing chunk of your script)
lab <- import(here("data", "raw", "lab_results_20141201.xlsx")) %>% 
  clean_names()

¡No olvides utilizar la extensión de archivo correcta para el archivo de laboratorio (.xlsx)! Y asegúrate de resaltar y ejecutar este código para garantizar que el objeto lab se añada al Entorno R.


Une los datos del laboratorio

En el fragmento "Área de pruebas", escribe y ejecuta comandos para comparar los combined y lab marcos de datos, como dim().

Luego, en el fragmento "Unir" (debajo de tu hosp unir), realiza una left_join() para añadir el lab columna ct_blood a la columna combined marco de datos, coincidente con case_id.

r fontawesome::fa("check", fill = "red")Haz clic para ver una solución (¡pruébalo tú primero!)

# Join the two data frames with a left-join
combined <- left_join(combined, lab, by = "case_id")



Comprueba manualmente en el Visor de RStudio que la unión se ha producido correctamente.

Añade los datos de la investigación del caso

Por último, tenemos que añadir nuevos datos de las investigaciones de casos. Los equipos han realizado llamadas telefónicas y visitas a domicilio para hacer un trabajo de detective epidemiológico. Han registrado los casos de origen probable, las fechas de infección y el contexto de infección de algunos de los casos.

Completa los siguientes pasos para unir por la izquierda el marco de datos de investigaciones de casos al marco de datos de combined marco de datos

1) En el marco de datos "Importar de tu R Markdown (cerca de la parte superior), escribe y ejecuta un comando para importar el archivo XLSX ubicado en "data/raw/case_investigations_20141201.xlsx" y guárdalo como investigations.

2) En el Trozo de área de pruebas escribe y ejecuta comandos para comparar las dimensiones de investigations y combined

3) En el Trozo de unión escribe y ejecuta comandos para llevar a cabo una left_join() para añadir el investigations columnas a combined

r fontawesome::fa("check", fill = "red")Haz clic para ver la solución (¡pruébalo tú primero!)

En el fragmento de importación

¡No olvides utilizar la extensión correcta para el archivo (.xlsx)!

# In the import data code chunk 

# Import lab data 
# (add to the import code chunk of your R script)
investigations <- import(here("data", "raw", "case_investigations_20141201.xlsx")) %>% 
 # remove unnecessary columns  
 select(-c(age, age_unit, sex))

En el trozo de prueba

# In the testing area code chunk

# Compare data frame dimensions
# (write and run directly in the Console)
dim(investigations)
dim(combined)

# Compare case_id (our matching variable)
# (write and run directly in the Console)
# for the investigations dataset
investigations %>% arrange(case_id) %>% pull(case_id) %>% head(10)

# for the combined dataset
combined %>% arrange(case_id) %>% pull(case_id) %>% head(10)

En el chunk de unión:

# In the joining code chunk

# Join the two data frames with a left-join
# (add to the joining code chunk of your R script)
combined <- left_join(combined, investigations, by = "case_id")


Comprueba manualmente, mirando el marco de datos, que la unión se ha producido correctamente.

Limpieza posterior a la unión

Ahora que tenemos estas nuevas columnas, debemos hacer una limpieza final en la columna combined marco de datos.

Nombres y clases de columnas

Tenemos nuevas columnas en nuestro marco de datos, por lo que debemos escribir código para asegurarnos de que son de la clase correcta.

Escribe un breve comando en tu trozo de unión para abordar las siguientes cuestiones en combined:

quiz(caption = "Quiz - Date conversions",
  question("Which {lubridate} function should you use to convert date_hospitalisation to class date?",
    allow_retry = T,
    answer("mdy()", correct = T),
    answer("ymd()", message = "No, you need to choose the function that aligns with the format of the data as written PRIOR to use of the function."),
    answer("dmy()", message = "No, you need to choose the function that aligns with the format of the data as written PRIOR to use of the function."),
    answer("imho()", message = "In my humble opinion, this is not a real function.")
  ),
    question("Which {lubridate} function should you use to convert date_outcome to class date?",
    allow_retry = T,
    answer("mdy()", correct = T),
    answer("ymd()", message = "No, you need to choose the function that aligns with the format of the data as written PRIOR to use of the function."),
    answer("dmy()", message = "No, you need to choose the function that aligns with the format of the data as written PRIOR to use of the function."),
    answer("imho()", message = "In my humble opinion, this is not a real function.")
  ),
    question("Which {lubridate} function should you use to convert date_infection to class date?",
    allow_retry = T,
    answer("mdy()", message = "No, you need to choose the function that aligns with the format of the data as written PRIOR to use of the function."),
    answer("ymd()", correct = T),
    answer("dmy()", message = "No, you need to choose the function that aligns with the format of the data as written PRIOR to use of the function."),
    answer("imho()", message = "In my humble opinion, this is not a real function.")
  )
)

r fontawesome::fa("lightbulb", fill = "gold") Haz clic para leer una pista

Utiliza clean_names() primero para normalizar la sintaxis. Para establecer la clase de fecha, elige entre mdy(), ymd() o dmy() colocar dentro del mutate() en función del formato actual de la fecha (mira el marco de datos), por ejemplo:

mutate(date_outcome = mdy(date_outcome))


r fontawesome::fa("check", fill = "red")Haz clic para ver una solución (¡pruébalo tú primero!)

Añadir a tu trozo de Unión

# (add this to your joining code chunk)

# Clean the new columns that have been joined to 'combined'
combined <- combined %>% 

  # convert all column names to lower case and remove spaces
  clean_names() %>% 

  # covert new columns to class date
  mutate(date_hospitalisation = mdy(date_hospitalisation),
         date_outcome         = mdy(date_outcome),
         date_infection       = ymd(date_infection)) %>% 

  # clean outcome and hospital missings
  mutate(outcome = na_if(outcome, ""),
         hospital = na_if(hospital, ""))


Tienes cierta flexibilidad sobre dónde colocar esta limpieza de datos:

Limpiar el script

Llegados a este punto, ya tenemos uniones y varios comandos de importación nuevos.

Es importante mantener nuestro script R Markdown ordenado y en un flujo lógico. Deberías tener la siguiente organización general:

1) Cargar paquetes 2) Importar datos 3) Limpiar el marco de datos primario 4) Realiza las uniones (y realiza cualquier limpieza residual) 5) Tablas y visualizaciones

Tómate un momento para asegurarte de que tu guión se ajusta a lo anterior, y de que puedes ejecutarlo de arriba abajo sin ningún error.

Comprobaciones finales

Revisa tu informe para asegurarte de que no hay errores. Si encuentras errores, intenta "depurarlos":

Añade un texto explicativo

La mayor parte del código que has añadido se ejecutará "silenciosamente" y no debería aparecer en el informe tejido. Sin embargo, revisa los pasos y comprueba si quieres añadir algún texto informativo sobre los conjuntos de datos que se han añadido al análisis.

Asegúrate también de que el código está bien documentado con símbolos #, para que otros lectores puedan entender tus acciones y fundamentos.

Fin de la página

¡Enhorabuena! Has terminado este ejercicio sobre la unión de datos.

Ahora tenemos un conjunto de datos mucho más completo con el que trabajar, que incluye columnas como outcome, date_hospitalisation, blood_ct y source.

Recuerda guardar tu script antes de salir de RStudio.

Si quieres aprender un poco más sobre los atajos en R Studio y otros consejos útiles, puedes pasar al siguiente tema extra.

Extras

Rutas de archivos

R Studio puede ayudarte a especificar rápida y correctamente las rutas de los archivos utilizando Tab para autocompletar.

Empieza a escribir una ruta de archivo (entre comillas) y pulsa Tab en tu teclado. R Studio listará las subcarpetas y archivos disponibles para esa ubicación. El ejemplo siguiente muestra el resultado de pulsar Tab después de escribir "data/". Haz clic en la subcarpeta o archivo deseado o navega hasta él con el teclado y pulsa enter.

knitr::include_graphics("images/filepaths1.png", error = F)

Puedes seguir haciendo esto hasta llegar a tu destino final:

knitr::include_graphics("images/filepaths2.PNG", error = F)

Especialmente en un R Markdown es importante seguir escribiendo esta ruta de archivo dentro de here() debido al comportamiento indicado en los Extras del módulo R Markdown.

Atajos de teclado

A continuación encontrarás algunos atajos de teclado muy útiles.

Para ver la lista completa, ve al menú Herramientas y a la "Ayuda para atajos de teclado".

Mira más ejemplos aquí: https://epirhandbook.com/en/r-basics.html#keyboard-shortcuts.

| Windows/Linux | Mac | Acción | | ---------------- | --------------- | -------------------------------------------------------------------------------------- | | Esc | Esc | Interrumpir el comando actual (útil si el código que se está ejecutando está atascado) | | Ctrl+s | Cmd+s | Guardar (script) | | Pestaña | Tab | Autocompletar | | Ctrl + Intro | Cmd + Intro | Ejecutar la(s) línea(s)/selección actual(es) de código | | Ctrl + Mayús + C | Cmd + Mayús + c | comentar/descomentar las líneas resaltadas | | Alt + - | Opción + - | Insertar \<- | | Ctrl + Mayús + m | Cmd + Mayús + m | Insertar %>%. | | Ctrl + Alt + i | Cmd + Mayús + r | Insertar fragmento de código (en R Markdown) | | Ctrl + f | Cmd + f | Buscar y reemplazar en el script actual | | Ctrl + Mayús + f | Cmd + Mayús + f | Buscar en archivos (buscar/reemplazar en muchos scripts) |



appliedepi/introexercises documentation built on April 22, 2024, 1:01 a.m.