```{=html}

```r
# load packages ----------------------------------------------------------------
library(introexercises) # get datos for exercises  
library(learnr)         # create lessons from rmd
library(gradethis)      # evaluate exercises
library(dplyr)          # wrangle datos  
library(tidyr)          # pivot  
library(forcats)        # factors
library(stringr)        # strings
library(flair)          # highlight code
library(ggplot2)        # visualise datos     
library(lubridate)      # work with dates  
library(epikit)         # for emojis   
library(skimr)          # browse datos
library(fontawesome)    # for emojis  
library(janitor)        # limpios datos
# library(RMariaDB)       # connect to sql database 

## set options for exercises and checking ---------------------------------------

## Define how exercises are evaluated 
gradethis::gradethis_setup(
  ## note: the below arguments are passed to learnr::tutorial_options
  ## set the maximum execution time limit in seconds
  exercise.timelimit = 60, 
  ## set how exercises should be checked (defaults to NULL - individually defined)
  # exercise.checker = gradethis::grade_learnr
  ## set whether to pre-evaluate exercises (so users see answers)
  exercise.eval = FALSE 
)

# ## event recorder ---------------------------------------------------------------
# ## see for details: 
# ## https://pkgs.rstudio.com/learnr/articles/publishing.html#events
# ## https://github.com/dtkaplan/submitr/blob/master/R/make_a_recorder.R
# 
# ## connect to your sql database
# sqldtbase <- dbConnect(RMariaDB::MariaDB(),
#                        user     = Sys.getenv("userid"),
#                        password = Sys.getenv("pwd"),
#                        dbname   = 'excersize_log',
#                        host     = "144.126.246.140")
# 
# 
# ## define a function to collect datos 
# ## note that tutorial_id is defined in YAML
#     ## you could set the tutorial_version too (by specifying version:) but use package version instead 
# recorder_function <- function(tutorial_id, tutorial_version, user_id, event, datos) {
#     
#   ## define a sql query 
#   ## first bracket defines variable names
#   ## values bracket defines what goes in each variable
#   event_log <- paste("INSERT INTO responses (
#                        tutorial_id, 
#                        tutorial_version, 
#                        date_time, 
#                        user_id, 
#                        event, 
#                        section,
#                        label, 
#                        question, 
#                        answer, 
#                        code, 
#                        correct)
#                        VALUES('", tutorial_id,  "', 
#                        '", tutorial_version, "', 
#                        '", format(Sys.time(), "%Y-%M%-%D %H:%M:%S %Z"), "',
#                        '", Sys.getenv("SHINYPROXY_PROXY_ID"), "',
#                        '", event, "',
#                        '", datos$section, "',
#                        '", datos$label,  "',
#                        '", paste0('"', datos$question, '"'),  "',
#                        '", paste0('"', datos$answer,   '"'),  "',
#                        '", paste0('"', datos$code,     '"'),  "',
#                        '", datos$correct, "')",
#                        sep = '')
# 
#     # Execute the query on the sqldtbase that we connected to above
#     rsInsert <- dbSendQuery(sqldtbase, event_log)
#   
# }
# 
# options(tutorial.event_recorder = recorder_function)
# hide non-exercise code chunks ------------------------------------------------
knitr::opts_chunk$set(echo = FALSE)
# datos prep --------------------------------------------------------------------
vig_bruta <- rio::import(system.file("dat/listado_vigilancia_20141201.csv", package = "introexercises"))

# for quizes
vig <- rio::import(system.file("dat/listado_vigilancia_limpio_20141201.rds", package = "introexercises"))

malaria_recuento <- rio::import(system.file("dat/malaria_facility_count_data.rds", package = "introexercises"))

datadict <- rio::import(system.file("dat/diccionario_listado_es.csv", package = "introexercises"))

cobertura_lugares_ord <- rio::import(system.file("dat/ejemplos_datos_desordenados.xlsx", package = "introexercises"), sheet = "tidy_site_coverage")

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 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)

Limpieza de datos

Este ejercicio se centra en la recodificación lógica de valores, el filtrado de filas, la creación de categorías de edad y otras tareas más complejas.

Formato

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

Obtener ayuda

Hay varias formas de obtener ayuda:

1) Busca la sección de ayuda (ver más abajo) 2) Pide ayuda al instructor/a o facilitador/a de tu curso en directo 3) Programa una llamada 1 a 1 con un/a instructor/a para "Tutoría del curso". 4) Publica una pregunta en Comunidad Epi Aplicada

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!)


vig %>% 
  filter(
    edad > 25,
    distrito == "Bolo"
  )

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


Cuestionario

Responder a las preguntas del cuestionario te ayudará a comprender el material. Las respuestas no se registran.

Para practicar, responde a las siguientes preguntas:

quiz(
  question_radio("¿Cuándo debería mirar las secciones de ayuda en rojo?",
    answer("Tras intentar escribir el código yo mismo", correct = TRUE),
    answer("Antes de intentar escribir el código", correct = FALSE),
    correct = "Revisar el código de ejemplo después de intentar escribirlo usted mismo puede ayudarle a mejorar",
    incorrect = "Por favor, intente completar el ejercicio usted mismo, o use las pistas disponibles, antes de mirar la respuesta."
  )
)
question_numeric(
 "¿Qué tan preocupado/a estás ante la idea de empezar este tutorial - en una escala de 1 (nada nervioso) a 10 (extremadamente preocupado/a)?",
 answer(10, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T),
 answer(9, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T),
 answer(8, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T),
 answer(7, message = "Intenta no preocuparte, ¡te ayudaremos a conseguirlo!", correct = T),
 answer(6, message = "De acuerdo, lo conseguiremos juntos", correct = T),
 answer(5, message = "De acuerdo, lo conseguiremos juntos", correct = T),
 answer(4, message = "¡Me gusta tu confianza!", correct = T),
 answer(3, message = "¡Me gusta tu confianza!", correct = T),
 answer(2, message = "¡Me gusta tu confianza!", correct = T),
 answer(1, message = "¡Me gusta tu confianza!", correct = T),
 allow_retry = TRUE,
 correct = "Gracias por responder",
 min = 1,
 max = 10,
 step = 1
)

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

Preparación

Abre el proyecto "ebola" de RStudio haciendo doble clic en el archivo "ebola.Rproj" de la carpeta "ebola".

Abre el archivo "analisis_ebola.R" con el que habías trabajado anteriormente. Seguirás completando el comando de limpieza.

Ejecuta todos los comandos desde la parte superior de este script hasta la parte inferior y lo más importante:

Tómate un momento para organizar y simplificar las secciones "Análisis exploratorio", "Lista de vigilancia limpia" y "Área de pruebas" de tu script para que no provoquen errores. Lo ideal sería que pudieras ejecutar todo tu script sin errores.

Ahora, compara tu comando de limpieza con el siguiente y asegúrate de que a tu comando de limpieza no le falta ningún paso.

vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer"))

Recuerda sólo debes tener un comando de limpieza en tu script. Cualquier otro comando como class() y tabyl() deben estar en la sección de "Área de Pruebas".

Recodificación lógica

A veces, la recodificación de valores no es tan sencilla como sustituir un valor por otro. En su lugar, puedes utilizar sentencias y operadores lógicos para realizar acciones de recodificación más complejas.

Valores negativos

En la columna de peso en kilogramos peso_kg hay que corregir algunos valores negativos, lo cual no tiene sentido.\ En el panel de Entorno, haz clic en el objeto dataframe vig. A continuación, busca la columna peso_kg y en el botón a la derecha de la columna, haz clic izquierdo para ordenar de menor a mayor. Observa los valores negativos.

Alternativamente, en tu sección "Área de pruebas", ejecuta el comando summary() del paquete {base} con la columna vig$peso_kg para retornar en la consola estadísticas resumidas de esta columna numérica:

summary(vig$peso_kg)

¡Los valores negativos deben ser un error de introducción de datos! Debemos eliminarlos de nuestros datos limpios convirtiéndolos en NA (desaparecidos).

Recuerda que es mejor realizar estas modificaciones en tu script de R que en Excel u otro programa. De este modo, los cambios quedan registrados, se pueden repetir en cualquier versión actualizada de la base de datos y se pueden revertir fácilmente.

La lógica que queremos aplicar es sencilla: "Si el valor de nuestro peso_kg es negativo, conviértelo en NA de lo contrario, mantén el mismo valor". Como la lógica es sencilla, podemos utilizar la función ifelse() dentro de la función mutate() para corregir la columna peso_kg.

La sintaxis de la función ifelse() es la siguiente:

ifelse(Condición aplicada en la fila, resultado si la condición es VERDADERA, resultado si la condición es FALSA)

En este ejemplo

ifelse(valor en la fila de la columna peso_kg es < 0, reemplaza el valor negativo con NA, de lo contrario, mantén su valor original)

Este es el ejemplo, escrito dentro de un mutate() que podría añadirse al comando de limpieza:

# actualiza peso_kg para reemplazar valores negativos con NA, de lo contrario manten el valor original
mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg))

Añade esto a tu comando de limpieza y comprueba que funciona correctamente.

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

vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) 


Ahora que has vuelto a ejecutar tu comando de limpieza y has actualizado tu objeto dataframe vig, volvamos a ejecutar el comando summary() en el "Área de pruebas" para comprobar el nuevo intervalo de valores:

summary(vig$peso_kg)

Revisar los operadores lógicos

A continuación, ofrecemos un resumen de los operadores lógicos útiles en R. No es necesario memorizarlos (consulta la sección capítulo de conceptos básicos del Manual de Epi), pero están aquí para que los revises.

Operadores relacionales

Los operadores relacionales comparan valores y suelen utilizarse al definir nuevas variables y subconjuntos de conjuntos de datos.

| Significado | Operador | Ejemplo | Resultado | |:---------------:|:---------------:|:---------------:|:-------------------:| | Igual a | == | "A" == "a" | FALSE (because R is case sensitive) | | No igual a | != | 2 != 0 | TRUE | | Mayor que | > | 4 > 2 | TRUE | | Menor que | < | 4 < 2 | FALSE | | Mayor o igual a | >= | 6 >= 4 | TRUE | | Menor o igual a | <= | 6 <= 4 | FALSE | | Valores vacios | is.na() | is.na(7) | FALSE | | Valores no vacios | !is.na() | !is.na(7) | TRUE |

Ten en cuenta que == (doble igual) hace la pregunta a R: "¿El valor de la derecha es igual al valor de la izquierda? El resultado es TRUE o FALSE (Verdadero o falso).

En cambio, la = (igual simple) se utiliza para asignar valores, como por ejemplo para un argumento dentro de una función:

Operadores lógicos

Los operadores lógicos, como Y y O, suelen utilizarse para conectar operadores relacionales y crear criterios más complicados. Las sentencias complejas pueden requerir paréntesis ( ) para la agrupación y el orden de aplicación.

| Significado | Operator | |:------------------:|:--------------------------------------------------:| | Y (AND) | & | | O (OR) | | (barra vertical) | | Parentesis | ( ) Para agrupar criterios y especificar el orden de ejecución de operaciones |

Otras opciones de recodificación lógica simple

Ahora que has aprendido la recodificación lógica simple, puedes utilizar ifelse() para muchas situaciones. Aquí tienes otras dos funciones sencillas de recodificación lógica que deberías marcar para leer más adelant:

case_when() para la recodificación compleja

Para una recodificación más compleja o avanzada, debes utilizar case_when() del paquete {dplyr} (parte del tidyverse). Esta función te permite ejecutar el equivalente de múltiples sentencias ifelse() - es el equivalente de R al SQL CASE WHEN de R -.

¡Es sólo una coincidencia que la función case_when() tenga un nombre que parece hecho para construir definiciones de casos epidemiológicos!

Es una función general, no específica de la salud pública. La función genérica case_when() es la siguiente:

mutate(NUEVA_columna = case_when(
    RECODIFICACIÓN LÓGICA 1  ~ VALOR NUEVA_columna SI DECLARACIÓN 1 ES VERDADERA,  
    RECODIFICACIÓN LÓGICA 2  ~ VALOR NUEVA_columna SI DECLARACIÓN 2 ES VERDADERA,  
    RECODIFICACIÓN LÓGICA 3  ~ VALOR NUEVA_columna SI DECLARACIÓN 3 ES VERDADERA)
)

Nota: los dos lados de las líneas de la declaración lógica están separados por un ~ (tilde o virgulilla). Este símbolo puede ser difícil de encontrar en el teclado. Para algunos, se encuentra cerca de la tecla Intro. Para otros, se encuentra en la parte inferior izquierda del teclado (a la izquierda del "1" en la fila de números). Tómate un momento para identificar dónde está la ~ en tu teclado, y avisa a un instructor si no lo encuentras. (la distribución del teclado influye)

Recuerda: siempre puedes utilizar ?case_when() para buscar la documentación de la función.

Aquí tienes un ejemplo de case_when() utilizado en mutate() para crear una columna de definición de caso:

mutate(def_caso = case_when(                        # crea una nueva columna def_caso
  lab_conferma == TRUE ~ "Confirmado",              # Caso confirmado si laboratorio confirmado
  epilink == "si" & fiebre == "si" ~ "Sospechoso",    # de lo contrario, caso sospechoso si vinculo epidemiologico y fiebre
  TRUE~ "A investigar"))                            # de lo contrario, si ninguno de las declaraciones anteriores son verdadera, asigna como "A investigar"

Examinemos lo que ocurre en el comando anterior:

Preparación

Dentro de la estructura case_when()

ATENCIÓN: Todos los valores de resultado del lado DERECHO deben ser de la misma clase: numéricos, enteros, caracteres, fechas, lógicos, etc.

Si se confirma el laboratorio de un paciente, se clasifica inmediatamente como caso "Confirmado" en nuestro nuevo def_caso columna. Si la confirmación de laboratorio es FALSE o falta, se comprueba la segunda fórmula: si el caso tiene un vínculo epidemiológico Y fiebre, se clasifica como caso "Sospechoso". Por último, a cualquier paciente / fila en el que no se cumplan las condiciones primera y segunda, se le asigna el valor "A investigar".

Cómo se evalúan las declaraciones

Las declaraciones se evalúan de arriba-abajo (no todas a la vez), por lo que el orden es importante. En este ejemplo, cada fila del la base de datos se evalúa primero para la declaración lógica superior (lab_conferma == TRUE). Si esta afirmación es verdadera (TRUE) para esa fila, su valor en la columna def_caso se establece como "Confirmado". Sin embargo, si lab_conferma no es Verdadera (TRUE) para esa fila de los datos, sólo entonces se comprueba la segunda afirmación lógica. Así, para cada fila de la base de datos, la primera sentencia lógica que se evalúa como TRUE es utilizada.

Escribe las fórmulas en un orden intencionado: de muy específica (arriba) a muy amplia (abajo)..

Si ninguna de las afirmaciones lógicas se aplica a una fila de los datos, se asigna el valor de esa fila NA por defecto. Puedes ajustar este valor "por defecto" haciendo una de las siguientes cosas:

Ejecuta ?case_when en la Consola para saber más sobre el argumento .default =, si te interesa.

Ahora que hemos revisado el uso de la función case_when() añade una nueva línea con la funciónmutate() y dentro de este, la función case_when() al final de tu comando de limpieza y vuelve a ejecutarlo.

Por favor, escribe el comando y no te limites a copiar/pegar.

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


vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numerica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar"))


<!--

NOTA: Fin de la solución -->

Ahora podemos hacer una tabla con la nueva columna def_caso y ver los resultados (escribe en la sección de "Area de Pruebas"):

tabyl(vig, def_caso)

¡También es conveniente ir a ver la base de datos o vig para confirmar que la lógica se ha aplicado de la forma como lo has especificado!

Crear columna edad_anios

Ahora aplica tus conocimientos sobre el uso de case_when() en el siguiente escenario.

Recuerda que según la unidad_edad , algunos valores de la columna edad se registran en "años", otros en "meses", y a algunos les falta este valor. Tenemos que crear una regla para que edad_anios contenga una edad numérica en años.

Añade otro paso a tu comando de limpieza para crear una nueva columna edad_anios. Utiliza las funciones mutate() y case_when() para evaluar la edad y unidad_edad y realizar los cálculos correspondientes.

A continuación te damos el esquema: es tu objetivo es rellenar las afirmaciones lógicas de la izquierda y los cálculos de la derecha.

  # crea la variable o columna nueva edad_anios (edad en años)
  mutate(edad_anios = case_when(

    # criterio de edad en años    # ajustes hacer para edad en años  
    [if meses]               ~ [calculo],    # retorna: edad / 12
    [if años]                ~ [calculo],    # retorna: edad 
    [if si valor]              ~ [calculo]))   # si unidad es vacia, asume años 

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


Dentro del paréntesis de la función case_when(), en los lados IZQUIERDOS debes escribir los criterios lógicos que se evaluarán para cada fila del base de datos, y en los lados derechos debes escribir el valor resultante que se devolverá.

El primer criterio lógico debe ser "es unidad_edad igual a"meses"? En código, esto se escribe unidad_edad == "meses" (fíjate en el doble igual, que plantea la cuestión de la equivalencia). A la derecha del simbolo \~ (virgulilla), escribirías una ecuación que produjera el resultado correcto en la nueva columna edad_anios. En este caso, sería el valor en edad dividido por 12. En código se escribiría como unidad_edad == "meses" ~ edad / 12. No olvides poner una coma al final, y continuar siempre en la línea siguiente con el siguiente criterio lógico.

Si te encuentras con este error 'names' attribute [1] must be the same length as the vector [0], confirma que has convertido el edad columna a Clase numérica anteriormente en el comando de limpieza. Este error se debe a que, al dividir por 12, estamos introduciendo decimales, lo que no es aceptable si la columna sigue siendo de clase "entero" (integer).


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


vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar")) %>% 


  # crea edad-en-años
  mutate(edad_anios = case_when(
    unidad_edad == "meses" ~ edad/12,   # si la edad es dada en meses
    unidad_edad == "anios"  ~ edad,      # si la edad es dada en años
    is.na(unidad_edad)      ~ edad))     # si unidad_edad está en blanco,asume edad en años, de lo contrario,será NA


¡Qué difícil! Si el ejercicio anterior con case_when() te ha resultado muy difícil, ¡no te desesperes! Lo conseguirás con la práctica. Habla con un instructor sobre los errores que pudiste haber encontrado.

Categorías de edad

Ahora que tenemos una edad_anios en la que todas las edades se registran en años, vamos a crear una columna nominal o categórica de grupos de edades.

Hay varias formas de hacerlo (ver este capítulo del Manual de Epi R para más detalles), pero aquí te mostramos el método más sencillo.

La función age_categories() de {epikit} puede utilizarse dentro de mutate() para definir una nueva columna edad_cat con categorías de edad de 10 años.

Los argumentos para age_categories son:

1) La columna numérica con los valores de edad que hay que dividir en categorías (edad_anios) 2) lower = Un número que es el límite numérico inferior (a menudo utilizamos 0) 3) upper = Un número que es el límite numérico superior para las categorías (por ejemplo, ponlo a 70, para que todas las edades por encima de 70 se incluyan en la categoría "70+") 4) by = Un número para los intervalos de las categorías (a menudo utilizamos 10, de modo que, por ejemplo, la categoría más joven sería "0-9")

Añade esta línea al final de tu comando de limpieza.

```{=html}

<details>

<summary style="text-decoration: underline; color: red;">

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

</summary>

</br>

```r
vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar")) %>% 


  # crea edad-en-años
  mutate(edad_anios = case_when(
    unidad_edad == "meses" ~ edad/12,   # si la edad es dada en meses
    unidad_edad == "anios"  ~ edad,      # si la edad es dada en años
    is.na(unidad_edad)      ~ edad)) %>%   # si unidad_edad está en blanco,asume edad en años, de lo contrario,será NA

  # crea la columna de categoría de edades
  mutate(edad_cat = age_categories(         # crea una nueva columna
    edad_anios,                             # columna numérica para hacer grupos
    lower = 0,
    upper = 70,
    by = 10))



También puedes definir las categorías de edad de otras formas. Por ejemplo, puedes proporcionar una vector con valores de los intervalos de edad como c(0, 5, 10, 15, 20, 30, 40, 50) a el argumento breaks = . Pregunta a un instructor si quieres saber cómo hacerlo, o lee la documentación de la función introduciendo ?age_categories en la consola de R.

Otras funciones que realizan tareas similares, pero con menos facilidad, son cut() de {base} R. Consulta el capítulo sobre limpieza de datos del Manual Epi R.

¿Has guardado tu guión últimamente?r fontawesome::fa("exclamation", fill = "red")

Diferencias de fecha

Ahora que las dos columnas de fecha están correctamente clasificadas como clase "Fecha" (date), podemos hacer operaciones matemáticas sobre ellas con operadores de suma (+) y de resta (-).

Añade otro paso a tu comando de limpieza utilizando mutate() para crear una nueva columna llamada difer. Define esta nueva columna para que sea igual al número de días transcurridos entre fecha_sintomas y fecha_notifica para cada caso.

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


Dentro de la función mutate, pon el nombre de la nueva columna difer y luego un signo igual, después utiliza la resta ( - ) para hallar la diferencia entre las dos fechas, en días.


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


vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar")) %>% 


  # crea edad-en-años
  mutate(edad_anios = case_when(
    unidad_edad == "meses" ~ edad/12,   # si la edad es dada en meses
    unidad_edad == "anios"  ~ edad,      # si la edad es dada en años
    is.na(unidad_edad)      ~ edad)) %>% # si unidad_edad está en blanco,asume edad en años, de lo contrario,será NA

  # crea la columna de categoría de edades
  mutate(edad_cat = age_categories(         # crea una nueva columna
    edad_anios,                             # columna numérica para hacer grupos
    lower = 0,
    upper = 70,
    by = 10)) %>% 

  # crea una columna para calcular la diferencia en días entre fecha de reporte y fecha de inicio de sintomas 
  mutate(difer = fecha_notifica - fecha_sintomas)


Abre el objeto dataframe vig y revisa algunas filas manualmente. Observa la diferencia entre fecha_notifica y fecha_sintomas en comparación con el valor de la nueva difer columna. ¿Es como esperabas?

En la columnadifer los valores aparecen como "4 days" (días) - se trata de una clase especial llamada "difftime" - . Si quieres que el resultado sea puramente numérico, puedes envolver el resultado del cálculo con función as.numeric() de {base}. .

NO LO HAGAS AHORA pero sólo como referencia, añadiendo as.numeric() al comando anterior quedaría así

mutate(difer = as.numeric(fecha_notifica - fecha_sintomas))

Actualmente, un caso con fecha_sintomas y fecha_notifica del 9 de noviembre de 2014 está registrado con difer de 0 días. No cambies esto, pero ten en cuenta que en tu contexto de trabajo, puedes optar por añadir un + 1 para que el ingreso en el mismo día se registre con un valor de 1.

Ubicación

Tenemos dos columnas que expresan el distrito / ubicación de los casos - distrito_res es el distrito de residencia, y distrito_not es el lugar donde se detectaron-.

¿En qué se parecen estas dos columnas?

Una forma de evaluar la similitud de las columnas es crear una nueva columna que indique si los valores de las dos columnas de una fila determinada son diferentes. Luego, puedes resumir en una tabla esta nueva columna.

En realidad, la sintaxis es bastante sencilla utilizando mutate() y el operador != (que significa NO ES IGUAL A).

Añade un nuevo paso al comando de limpieza que compare las dos columnas de distrito, y vuelve a ejecutar tu comando de limpieza.

# Nueva columna para evaluar si hay diferencias entre dos columnas (si son diferentes, será TRUE o verdadera)
mutate(translado = distrito_res != distrito_not)

Ahora evalúa el cambio - abre el vig -. Haz clic en el botón de filtro de la parte superior izquierda para limitar la vista a las filas en las que está la nueva columna translado sea VERDADERA. ¿Se ha aplicado correctamente la lógica de la nueva columna? ¿Aparecen VERDADERO y FALSO donde deberían?

quiz(caption = "Cuestionario - Casos que emigran",
  question_numeric(
    "¿Cuantos casos viven en un distrito y son diagnosticado en otro distrito?",
    answer(vig_bruta %>%
     distinct() %>%
     mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 
     mutate(translado = adm3_nombre_res != adm3_nombre_not) %>% 
     filter(translado) %>%
     nrow(),
           correct = T),
    allow_retry = TRUE,
    correct = "Correcto, buen trabajo.",
    min = 1,
    max = 700,
    step = 1
  )
)

También puedes utilizar código R para encontrar la respuesta, utilizando la función tabyl() en la nueva columna. Pruébalo en la sección "Área de pruebas" de tu script.

tabyl(vig, translado)

Prioriza determinados valores con coalesce()

Ahora que sabes que existen discrepancias, ¿cuál de estos valores debe priorizarse? ¿Distrito de residencia o distrito de detección?

Ésta es una pregunta que el equipo de epidemiología del brote debe responder dado su conocimiento del proceso de recogida de datos, y del propio brote. Para este ejercicio, daremos prioridad al lugar de detección porque puede reflejar mejor las poblaciones cercanas con mayor riesgo de infección (lo que puede interpretarse mejor mediante una buena investigación de los casos).

Crea una nueva columna en los datos llamada simplemente distrito.

En mutate() utiliza la función coalesce() que tomará el primer valor si está disponible; en caso contrario, tomará el segundo valor. Esta función nos permite "rellenar" los valores que faltan utilizando el primer valor disponible (dado un orden de preferencia), o dar prioridad

En mutate() utiliza el botón coalesce() que tomará el primer valor si está disponible; en caso contrario, tomará el segundo valor. Esta función nos permite "rellenar" los valores que faltan utilizando el primer valor no faltante (dado un orden de preferencia), o dar prioridad a un valor sobre otro si difieren.

Añade el siguiente paso a tu comando de limpieza y vuelve a ejecutarlo. Asegúrate de volver a ejecutar tu comando de limpieza para que tu vig se actualice con un distrito columna adicional.

# Añade el siguiente comando a tu cadena de limpieza
mutate(distrito = coalesce(distrito_not, distrito_res))

```{=html}

<details>

<summary style="text-decoration: underline; color: red;">

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

</summary>

</br>

```r
vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar")) %>% 


  # crea edad-en-años
  mutate(edad_anios = case_when(
    unidad_edad == "meses" ~ edad/12,   # si la edad es dada en meses
    unidad_edad == "anios"  ~ edad,      # si la edad es dada en años
    is.na(unidad_edad)      ~ edad)) %>%   # si unidad_edad está en blanco,asume edad en años, de lo contrario,será NA

  # crea la columna de categoría de edades
  mutate(edad_cat = age_categories(         # crea una nueva columna
    edad_anios,                             # columna numérica para hacer grupos
    lower = 0,
    upper = 70,
    by = 10)) %>% 

  # crea una columna para calcular la diferencia en días entre fecha de reporte y fecha de inicio de síntomas 
  mutate(difer = fecha_notifica - fecha_sintomas) %>% 

  # nueva columna para evaluar si hay diferencias entre dos columnas (si son diferentes, será TRUE o verdadera)
  mutate(translado = distrito_res != distrito_not) %>% 

  # nueva columna que prioriza distrito de detección de caso
  mutate(distrito = coalesce(distrito_not, distrito_res))


Visualiza de nuevo la base de datos y comprueba con la vista que este código ha funcionado como se esperaba

Añade el siguiente comando a tu "Sección del área de pruebas" y responde a la pregunta del cuestionario que aparece a continuación.

# tabla cruzada entre la columna "translado" con la columna "distrito_res" (emigró vs distrito de residencia)
tabyl(vig, translado, distrito_res)
quiz(caption = "Cuestionario - Origen de los casos que emigran",
  question_numeric(
    "De los casos donde el distrito de residencia es diferente al distrito de detección ¿Cúantos residian en el distrito 'Mountain Rural'?",
    answer(vig_bruta %>%
             distinct() %>%
             mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 
             mutate(translado = adm3_nombre_res != adm3_nombre_not) %>% 
             filter(translado & adm3_nombre_res == "Mountain Rural") %>%
             nrow(),
           correct = T),
    allow_retry = TRUE,
    correct = "Correcto, buen trabajo.",
    min = 1,
    max = 700,
    step = 1
  )
)

¿Has guardado tu script últimamente? r fontawesome::fa("exclamation", fill = "red")

Filtrando filas

El subconjunto de filas se realiza con la función filter(). ¡Esta función es muy útil! Dentro del paréntesis de la función filter() escribes criterios lógicos que se aplican a cada fila. Las filas que cumplan los criterios se conservan.

Vuelve a repasar brevemente los operadores relacionales y lógicos de R. Puedes encontrarlos siempre en el Manual de Epi R Página de conceptos básicos.

Operadores relacionales

+--------------------------+------------+--------------+------------------------------------------+ | Significado | Operador | Ejemplo | Resulado | +==========================+============+==============+==========================================+ | igual a | == | "A" == "a" | FALSE R distingue el tipo de letra | +--------------------------+------------+--------------+------------------------------------------+ | No igual a | != | 2 != 0 | TRUE | +--------------------------+------------+--------------+------------------------------------------+ | Mayor que | > | 4 > 2 | TRUE | +--------------------------+------------+--------------+------------------------------------------+ | Menor que | < | 4 < 2 | FALSE | +--------------------------+------------+--------------+------------------------------------------+ | Mayor o igual que | >= | 6 >= 4 | TRUE | +--------------------------+------------+--------------+------------------------------------------+ | Menor o igual que | <= | 6 <= 4 | FALSE | +--------------------------+------------+--------------+------------------------------------------+ | El valor es "vacio" | is.na() | is.na(7) | FALSE | +--------------------------+------------+--------------+------------------------------------------+ | El valos no es vacio | !is.na() | !is.na(7) | TRUE | +--------------------------+------------+--------------+------------------------------------------+

Operadores lógicos

Los operadores lógicos, como Y y O, suelen utilizarse para conectar operadores relacionales y crear criterios más complicados. Las sentencias complejas pueden requerir paréntesis ( ) para la agrupación y el orden de aplicación.

+---------------------+-----------------------------------------------------------------------------+ | Significado | Operator | +=====================+=============================================================================+ | Y (AND) | & | +---------------------+-----------------------------------------------------------------------------+ | O (OR) | | (barra vertical) | +---------------------+-----------------------------------------------------------------------------+ | Parentesis | ( ) Usado para agrupar criterios y clarificar el orden de las operaciones | +---------------------+-----------------------------------------------------------------------------+

Ten en cuenta que == (doble igual) hace la pregunta a R: "¿El valor de la derecha es igual al valor de la izquierda? El resultado es TRUE o FALSE.

Por el contrario, el símbolo = (igual simple) se utiliza para asignar valores,

Por el contrario, la = (igual simple) se utiliza para asignar valores,

como por ejemplo para un argumento dentro de una función:

%in%

El operador %in% es un operador muy útil para emparejar valores y para evaluar rápidamente si un valor está dentro de un vector o una base de datos. Por ejemplo, en un vector simple, podemos evaluar utilizando %in% si un valor (a la izquierda) está en el vector (a la derecha). Mira el ejemplo siguiente:

#Creamos un vector nuevo
mis_paises <- c("Estados Unidos", "Vietnam", "Mongolia", "Peru")
"Peru" %in% mis_paises
"Tanzania" %in% mis_paises

Para preguntar si un valor está no es %in% un vector, pon un signo de exclamación (!) delante de la declaración lógica:

# para negar, pon un símbolo de exclamación al principio del comando
!"Peru" %in% mis_paises
!"Tanzania" %in% mis_paises

Esto también funciona con columnas, porque las columnas son "vectores".

Eliminando los casos definidos como "A investigar"

A efectos de este ejercicio, sólo mantendremos en los datos los casos "Confirmados" y "Sospechosos", y excluiremos las filas "A investigar".

La forma más sencilla de utilizar filter() es aplicar una condición lógica (por ejemplo ==, !=, >, <) de modo que sólo las filas en las que la afirmación lógica sea TRUE se conservan.

Por ejemplo, sólo (NO añadas esto a tu código), esto es un filter() que mantiene sólo las filas en las que el sexo es igual a "mujer":

filter(sexo == "mujer")

En %in% puede utilizarse para comprobar si una fila contiene uno de los valores incluidos en un vector de valores.

NO LO AÑADAS A TU COMANDO

filter(sexo %in% c("hombre", "mujer"))

Ahora, añade otro paso a tu comando de limpieza utilizando filter(), %in% y c() para conservar sólo las filas en las que def_caso sea "Confirmado" o "Sospechoso".

A continuación, vuelve a ejecutar tucomando de limpieza para ver los cambios.

```{=html}

<details>

<summary style="text-decoration: underline; color: red;">

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

</summary>

</br>

```r
vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar")) %>% 


  # crea edad-en-años
  mutate(edad_anios = case_when(
    unidad_edad == "meses" ~ edad/12,   # si la edad es dada en meses
    unidad_edad == "anios"  ~ edad,      # si la edad es dada en años
    is.na(unidad_edad)      ~ edad)) %>%   # si unidad_edad está en blanco,asume edad en años, de lo contrario,será NA

  # crea la columna de categoría de edades
  mutate(edad_cat = age_categories(         # crea una nueva columna
    edad_anios,                             # columna numérica para hacer grupos
    lower = 0,
    upper = 70,
    by = 10)) %>% 

  # crea una columna para calcular la diferencia en días entre fecha de reporte y fecha de inicio de síntomas 
  mutate(difer = fecha_notifica - fecha_sintomas) %>% 

  # nueva columna para evaluar si hay diferencias entre dos columnas (si son diferentes, será TRUE o verdadera)
  mutate(translado = distrito_res != distrito_not) %>% 

  # nueva columna que prioriza distrito de detección de caso
  mutate(distrito = coalesce(distrito_not, distrito_res)) %>%  

  # Remueve casos "a investigar"
  filter(def_caso %in% c("Confirmado", "Sospechoso")) 


Filtros y valores perdidos

r fontawesome::fa("exclamation", fill = "red") Es importante tener en cuenta que filter(def_caso %in% c("Confirmado", "Sospechoso")) ¡Eliminará las filas en las que def_caso sean vacios (NA)!.

Del mismo modo, el filtrado basado en un criterio numérico o de fecha también puede eliminar las filas a las que les falten esos valores. Por ejemplo:

Para conservar las filas con valores omitidos, añade una barra vertical (operador O u "OR") y luego la función is.na() al filtro:

Los comandos anteriores permiten mantener las filas que cumplen el primer criterio, O que tienen valores que faltan.

Ahora, revisa tu comando de limpieza y el script de solución anterior. Asegúrate de que todos los pasos de limpieza de la solución también están presentes en tu script R "ebola_analysis.R".

Reordena las columnas

Haz clic en tu base de datos vig en el panel Entorno para revisar el orden de las columnas.

quiz(caption = "Cuestionario - Re-organiza las columnas",
  question("Las nuevas columnas que has creado ¿Dónde se encuentran en el dataframe?",
    allow_retry = TRUE,
    answer("En el lado más a la izquierda de la base de datos"),
    answer("En el lado más a la derecha de la base de datos", correct = TRUE),
    answer("En el medio de la base de datos")
  )
)

Puede que prefiramos guardar o exportar la base de datos con algunas columnas colocadas unas junto a otras, por ejemplo edad y edad_anios.

Si recuerdas la demostración de limpieza de datos de select() puedes utilizar la función select() para reorganizar columnas, así como para seleccionar determinadas columnas.

Por ejemplo, puedes listar algunas columnas para re-posicionarlas al principio, y terminar el select() utilizando la función de ayuda tidyselect everything() que incluye todas las demás columnas. Tienes a tu disposición otros ayudantes "tidyselect" como contains() y starts_with() con los que puedes escribir dentro de la página select() comando. Lee más sobre estos ayudantes en [capítulo del

Epi](https://epirhandbook.com/en/cleaning-datos-and-core-functions.html#clean_tidyselect).

He aquí un ejemplo (haz NO añadas este ejemplo a tu comando de limpieza):

select(id_caso, contains("fecha"), everything())

Añade un select() al final de tu comando de limpieza que reorganice las columnas en el siguiente orden:

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


vig <- vig_bruta %>% 

  # automaticamente limpia los nombres de las columnas
  clean_names() %>% 

  # limpia los nombres de las columnas de forma manual  
  rename(
    fecha_sintomas = fecha_inicio_sintomas,
    fecha_notifica = fecha_de_notifica,
    distrito_res = adm3_nombre_res,
    distrito_not = adm3_nombre_not) %>%

  # remueve las columnas innecesarias
  select(-num_fila) %>% 

  # de-duplica las filas 
  distinct() %>% 

  # convierte columna fecha_sintomas a tipo fecha ("Date")
  mutate(fecha_sintomas = mdy(fecha_sintomas)) %>% 
  mutate(fecha_notifica = mdy(fecha_notifica)) %>% 

  # convierte la columna aga a clase numérica
  mutate(edad = as.numeric(edad)) %>% 

  # convierte "Desconocido" (Desconocido) de la columna sexo a NA
  mutate(sexo = na_if(sexo, "Desconocido")) %>% 

  # de forma apropiada corrige en las columnas de tipo caracter el valor "" o en blanco a NA

  mutate(across(.cols = where(is.character), .fns = ~na_if(.x, ""))) %>% 

  # re-codifica la columna hospital
  mutate(hospital = recode(hospital,
    # para referencia: Valor VIEJO = valor NUEVO
    "Other"  = "Military Hospital",
    "Port"               = "Port Hospital",
    "Port Hopital"       = "Port Hospital",
    "St. Mark's Maternity Hospital (SMMH)" = "SMMH")) %>% 

  # re-codifica la columna sexo
  mutate(sexo = recode(sexo,
    "m" = "hombre",
    "f" = "mujer")) %>% 

  # convierte valores de peso negativos a NA
  mutate(peso_kg = ifelse(peso_kg < 0, NA, peso_kg)) %>% 

  # Crea la definición de caso
  mutate(def_caso = case_when(
    lab_conferma == TRUE             ~ "Confirmado",
    epilink == "si" & fiebre == "si" ~ "Sospechoso",
    TRUE                              ~ "A investigar")) %>% 


  # crea edad-en-años
  mutate(edad_anios = case_when(
    unidad_edad == "meses" ~ edad/12,   # si la edad es dada en meses
    unidad_edad == "anios"  ~ edad,      # si la edad es dada en años
    is.na(unidad_edad)      ~ edad)) %>%    # si unidad_edad está en blanco,asume edad en años, de lo contrario,será NA

  # crea la columna de categoría de edades
  mutate(edad_cat = age_categories(         # crea una nueva columna
    edad_anios,                             # columna numérica para hacer grupos
    lower = 0,
    upper = 70,
    by = 10)) %>% 

  # crea una columna para calcular la diferencia en días entre fecha de reporte y fecha de inicio de síntomas 
  mutate(difer = fecha_notifica - fecha_sintomas) %>% 

  # nueva columna para evaluar si hay diferencias entre dos columnas (si son diferentes, será TRUE o verdadera)
  mutate(translado = distrito_res != distrito_not) %>% 

  # nueva columna que prioriza distrito de detección de caso
  mutate(distrito = coalesce(distrito_not, distrito_res)) %>%  

  # Remueve casos "a investigar"
  filter(def_caso %in% c("Confirmado", "Sospechoso"))  %>% 

  # re-organiza el orden de las columnas
  select(id_caso, starts_with("fecha"), difer, sexo, edad, unidad_edad, edad_anios, edad_cat, hospital, distrito, distrito_res, distrito_not, translado, everything())


Recuerda volver a ejecutar tu comando de para que tu objeto dataframe vig se actualice con este último paso.

Exporta la base de datos limpio

Ahora que hemos limpiado esta base de datos, debemos guardarlo en el proyecto RStudio para utilizarlo más adelante, y quizás para compartirlo con otras personas.

Podemos utilizar la función export() de la función {rio} que es el mismo que utilizamos para import() los datos brutos.

La exportación es un comando autónomo - no forma parte de tu comando de limpieza. Los argumentos que espera son

1) El objeto R (dataframe o base de datos) que debe exportarse

2) El nombre deseado para el archivo, entre comillas, con extensión (por ejemplo, "lista_de_vigilancia_limpia_20141201.csv")

El comando siguiente es independiente (no forma parte de tu comando de limpieza). Esto significa que no hay ninguna base de datos conectado a él, por lo que tienes que escribir vig como primer argumento.

Como se escribe a continuación, vig se guardará como un archivo CSV en la carpeta raíz de tu proyecto RStudio.

Ejecuta el comando y comprueba que se ha guardado en la ubicación prevista.

# Exporta la base de datos limpia en un archivo en la carpeta principal del proyecto de R  
export(vig, "listado_vigilancia_limpio_20141201.csv")

Guardar como .rds

Si guardas el archivo como .xlsx o .csv, cualquier persona que utilice R para seguir analizando los datos tendrá que volver a limpiarlo convirtiendo de nuevo las columnas a sus clases correctas.

Para evitarlo, guarda la base de datos limpio como archivo ".rds". RDS es un formato de archivo específico de R, y resulta muy útil si vas a volver a trabajar en R con los datos exportados.

Por ejemplo, si trabajas en un equipo de epidemiología y necesitas enviar archivos de datos a un equipo de SIG para la elaboración de mapas, y ellos también utilizan R, ¡sólo tienes que enviarles el archivo .rds! Así se conservan todas las clases de columnas y tienen menos trabajo de limpieza que hacer.

En el ejemplo siguiente, vig se guarda como un archivo .rds. También especificamos que queremos que se guarde en una sub-carpeta ("datos/limpios") escribiendo la ruta del archivo mediante here() del mismo modo que hacemos para importar los datos.

¡Prueba éste también!

# Exporta el archivo limpio  
export(vig, here("datos", "limpios", "listado_vigilancia_limpio_20141201.rds"))

Si quieres exportar los datos como CSV o Excel, simplemente cambia la extensión del archivo en el comando a .csv o .xlsx.

Fin

¡Felicidades! ¡Has completado el ejercicio de limpieza! Este es un gran paso en tu introducción a R. Ahora ya conoces la mayoría de las funciones importantes de gestión de datos en R.

Asegúrate de guardar tu script de R, y compruébalo con tu facilitador.

En caso de que no hayas terminado, no te preocupes. Busca en la carpeta "ebola/scripts/copia_seguridad". Encontrarás scripts de R para el análisis del ébola en cada etapa de este curso. Así que siempre puedes utilizarlos como "copia de seguridad" para módulos posteriores.*

Haz clic para ver material extra interesante...

Extra - Datos ordenados

En estas secciones, repasaremos los conceptos de "datos ordenados" y algunos escollos habituales que impiden analizar fácilmente en R los datos de las hojas de cálculo de Excel.

knitr::include_graphics("images/tidy_shelves.png")

La forma en que estructures tu base de datos influirá en gran medida en lo complicado que deba ser tu código R. Una atención anticipada durante la fase de introducción de datos te ahorrará mucho tiempo durante la limpieza y el análisis de los datos.

Reflexiona: ¿Cuáles son algunas características de tus datos que dificultan especialmente su limpieza para el análisis?

Definiciones

Los datos ordenados tienen un significado específico en el análisis de datos: se refieren a cómo se ha almacenado la información dentro de la estructura de la base de datos. Así que empecemos con algo de terminología.

Estructuralmente, los conjuntos de datos o base de datos (llamados "dataframe" en R) constan de celdas, columnas, filas.

Sin embargo, "valores", "variables" y "observaciones son conceptos más abstractos.

Pongamos a prueba tu comprensión de estos términos.

quiz(
  question("50 kg, 75 kg, 67 kg, 90 kg son...",
    answer("Diferentes observaciones de la variable `temperatura`", message = "¡Piensa otra vez! ¡No tiene mucho sentido que la 'temperatura' se mida en kilogramos! También es poco probable que estos valores se denominen observaciones..."),
    answer("Diferentes variables de la observación `peso`", message = "¡Piensa otra vez! Todos estos valores tienen la misma unidad, por lo que probablemente serían una variable en lugar de diferentes. El 'peso' como observación también podría no ser la forma más lógica de registrar estos datos..."),
    answer("Diferentes valores de la variable `peso`", correct = TRUE, message = "De hecho, estos son valores correctos (en este caso estamos tratando con números, es decir, datos cuantitativos). Como los diferentes valores tienen la misma unidad, se pueden agrupar bajo una misma variable (en este caso, el peso tendría sentido)."),
    answer("Ninguna de estas answers son correctas", message = "¿Está seguro? ¡Intenta pensar a qué se refieren estos diferentes términos!"),
    answer("Todas estas answers son correctas", message = "¿Está seguro? ¡Intenta pensar a qué se refieren estos diferentes términos!"),
    allow_retry = TRUE
  ),
  question("Lo más probable es que 'Nombre' sea...",
    answer("Un Valor", message = "Si esto fuera un valor, ¿Cuál sería la variable y la observación bajo las cuales se podría agrupar?"),
    answer("Una variable", correct = TRUE, message = "De hecho, diferentes valores podrían incluir 'Josefina', 'Alejandro' o 'Guillermo'."),
    answer("Una observación", message = "Si esto fuera una observación, ¿cuál es la unidad de medida? ¿Cómo obtendrías otras observaciones?"),
    allow_retry = TRUE
  ),
  question("Pensando en la estructura de datos, ¿Cómo se representaría mejor una observación?",
    answer("Como una fila", correct = TRUE, message = "¡Así es! Más sobre esto a continuación..."),
    answer("Como una columna", message = "¡Piensa otra vez! ¿Es esta la forma más eficiente de representar sus datos?"),
    answer("Como una celda", message = "¡Piensa otra vez! ¿Es esta la forma más eficiente de representar sus datos?"),
    allow_retry = TRUE
  )
)

Principios de los datos ordenados

Tus datos pueden almacenarse de muchas formas: ¿Por qué es importante que estén "ordenados"?

Hay tres principios que hacen que una base de datos esté "ordenado":

Fuente: R para la Ciencia de Datos

Idealmente, se alinean: columnas = variables y filas = observacion

knitr::include_graphics("images/tidy_image.png")

Pero no siempre es así... sobre todo en los datos de salud pública. Seguro que has visto datos introducidos en este formato "amplio":

ejemplo_largo <- tribble(
     ~pais, ~Enero, ~Febrero, ~Marzo, 
     "Mozambique", 3200,    3300,      4100,  
     "Lesotho", 500,     750,       900,   
     "Sudafrica", 5100, 6200, 8100,)

ejemplo_largo
quiz(
  question("'Enero' es una columna, pero ¿es una variable?",
    answer("Si", message = "No, recuerda que una variable es el atributo subyacente más abstracto que se mide."),
    answer("No", correct = TRUE, message = "Enero es un valor de la variable 'mes'"),
    allow_retry = TRUE
  ),
  question("¿Dónde está la variable 'mes'?",
    answer("En la tercera columna", message = "No, no está en una columna."),
    answer("En una hoja de cálculo diferente", message = "Estas siendo tont@"),
    answer("Está distribuido en muchas columnas.", correct = TRUE, message = "Correcto, no tiene columna propia."),
    allow_retry = TRUE
  ),
  question("¿Cada observación tiene su propia fila?",
    answer("Si", message = "No, cada valor es una observación y hay tres en cada fila."),
    answer("No", correct = TRUE, message = "Correcto, hay tres observaciones en cada fila."),
    allow_retry = TRUE
  )
)

Aquí tienes los mismos datos, pero en un formato "ordenado":

ejemplo_largo_pivoted <- ejemplo_largo %>%
  pivot_longer(cols=2:4, names_to="mes", values_to = "casos") 

ejemplo_largo_pivoted

Reflexionando sobre las prácticas de los datos ordenados, ¿Qué proporción de las bases de datos que utilizas están ordenadas?

"Datos" legibles por máquina

Cuando empieces a recopilar datos, debes plantearte la pregunta: ¿es el público principal de este conjunto de humanos o máquinas?

Registrar la información de forma que esté optimizada para la "legibilidad humana" puede ser muy diferente de la optimización para la "legibilidad mecánica" y el análisis. Ten claro desde el principio cuál es tu prioridad. Aquí que te demos un consejo general: ¡Es más fácil pasar de la lectura de máquina a la lectura humana que al revés!

Hojas de cálculo Excel

En Applied Epi, promovemos el uso de R para muchas razones pero sabemos que para la mayoría de los epidemiólogos de campo, Excel es una herramienta fundamental para el trabajo diario. No hay nada malo en utilizar Excel. Los flujos de trabajo que implican R casi siempre también incluyen usar Excel u otro software de hojas de cálculo. Pero es importante que utilices Excel de forma que también puedas aprovechar al máximo las ventajas de R.

knitr::include_graphics("images/interoperability.png")

Excel es un programa potente y fácil de usar para principiantes. Aunque también es posible realizar algunos análisis en Excel, es probable que descubras que los análisis más sofisticados y las operaciones de gestión de datos pueden ser muy complicados o imposibles de llevar a cabo en este software. En estas situaciones es cuando programas versátiles como R resultan muy útiles.

knitr::include_graphics("images/difficulty_plot-1.png")

En este curso, te familiarizarás con la sintaxis del código R. En esta sección, nos centraremos en los pasos que puedes dar para asegurarte de que una base de datos de Excel pueda ser interpretado fácilmente por R para su análisis.

La principal razón por la que uno se encuentra con problemas al analizar datos de hojas de cálculo de Excel es cuando la hoja de cálculo se diseñó para dar prioridad a la lectura fácil por parte de humanos, no a la lectura fácil por parte de máquinas/software.

Para ayudarte a ver la diferencia, a continuación hay algunos ejemplos ficticios de hojas de cálculo que priorizan la lehumana-a la legibilidad máquina-legibilidad.

Importar datos de hojas de cálculo

Escribe un comando para importar la hoja de cálculo "datos_desordenados_ejemplos.xlsx" y almacenarla como objeto partner_tracking. La hoja de cálculo se guarda en la carpeta "ebola/datos/brutos".

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


partner_tracking <- import(here("datos", "brutos", "ejemplos_datos_desordenados.xlsx"))


Ahora, abre la hoja de cálculo con Microsoft Excel. Si puedes hacerlo, observa cómo en realidad hay 4 hojas en este libro de Excel. Utilizando la función import() ¡sólo ha importado la primera hoja!

Revisa tu comando de importación para utilizar la función sheet = para especificar el nombre de la hoja a importar. Escribe y ejecuta un comando para cada una de las hojas, como se muestra a continuación:

# importa la hojas para hacer seguimiento de los contactos con código de colores
contactos_tracking <- import(here("datos", "brutos", "ejemplos_datos_desordenados.xlsx"), sheet = "messy_colors")

# importa la hoja sobre cobertura de los sitios con los datos desordenados
sitio_cobertura <- import(here("datos", "brutos", "ejemplos_datos_desordenados.xlsx"), sheet = "messy_site_coverage")

# importa la hoja con los datos en formato "tidy" sobre la cobertura de los sitios
cobertura_lugares_ord <- import(here("datos", "brutos", "ejemplos_datos_desordenados.xlsx"), sheet = "tidy_site_coverage")

# importa la hoja con los datos de SIG desorganizados
messy_gis <- import(here("datos", "brutos", "ejemplos_datos_desordenados.xlsx"), sheet = "messy_gis")

Ahora que has importado estas cuatro hojas, vamos a revisarlas.

Colores

La hoja importada y nombrada parter_tracking registra el estado de una intervención de salud pública de varios socios de answer en varias provincias, distritos y subdistritos. En la parte derecha, el estado para fechas concretas se indica mediante el color de la celda, y hay un pequeño diccionario por colores en la parte superior derecha de la hoja.

knitr::include_graphics("images/messydata2.PNG")

Debemos preguntarnos: ¿esta hoja está escrita para maximizar la legibilidad humana, o la legibilidad de la máquina? Un ejemplo excelente de priorizar la legibilidad humana sobre la legibilidad mecánica es el uso de la codificación por colores de las celdas de una hoja de cálculo.

En R es bastante difícil interpretar el color de cada celda de una hoja de cálculo de Excel. Si utilizas el color, no debes sólo utilizar el color, sino también hacer que los valores reflejen las diferencias entre las celdas.

Almacenar información como en esta hoja no es lee o interpreta fácilmente por R - ¡Ni por humanos con daltonismo! -

Además, se combinan distintos datos en una misma celda (varias organizaciones asociadas que trabajan en un área, o el estado "TBC" en la misma celda que "Socio D").

question("En este caso, ¿Cuál crees que sería la mejor modificación antes de cargar la base de datos en R?",
  answer("Mantener como está, R reconocerá los colores.",
         message = "Es muy difícil para R interpretar los colores."),
  answer("Mantener como está, pero agregar texto a los colores.",
         message = "Esto ayudaría ya que R no puede leer colores. Sin embargo, este formato de datos aún no se leería bien en R y el diccionario de colores a la derecha causaría problemas. "),
  answer("Mantener como está, pero mover el diccionario de colores a otra hoja.",
         message = "Mover el diccionario de colores a otra hoja sería útil y podría cargarse por separado como una búsqueda; sin embargo, R no puede leer los colores, por lo que aún tendría problemas al cargar la tabla principal."),
  answer("Cambiar la base de datos para que cada columna sea una variable y cada celda contenga un valor único, integrando los colores como una nueva variable.", correct = TRUE,
         message = "De hecho, transformar estos datos a un formato ordenado es absolutamente necesario si se va a realizar algún análisis sobre estos datos. Eliminar la necesidad de colores agregando una variable para especificar lo que representan sería el camino a seguir."),
  answer("Cambie la base de datos agregando una columna para especificar lo que significa el color.",
         message = "Si bien este es generalmente el camino a seguir para transformar una base de datos usando un diccionario de colores antes de cargarlo en R, esta base de datos contiene otros problemas que lo hacen desordenado y difícil de analizar en el formato actual."),
  allow_retry =  TRUE
)

Aunque los diccionarios basados en colores pueden ser útiles para la legibilidad humana de una base de datos, los colores nunca deben utilizarse como única forma de registrar los datos. Las máquinas no podrán interpretarlos para el análisis.

De nuevo, es importante pensar cuál es la mejor forma de representar esa variable que actualmente se representa por colores: lo más probable es que debas reestructurar tus datos y representarla en su propia columna.

Y como regla general, los diccionarios (ya sean diccionarios de colores como los que se muestran aquí o diccionarios de datos) deben mantenerse separados de la tabla principal. En Excel, la mejor práctica sería tener este diccionario en otra hoja. Al importarlo a R, puedes importarlo como una base de datos independiente.

Pero nos estamos adelantando... ¡Más adelante hablaremos de los diccionaros de variables!

Celdas fusionadas

La hoja que ahora se importa como site_coverage contiene información sobre la "cobertura" en una serie de centros en mayo y junio de 2022:

knitr::include_graphics("images/site_coverage_untidy.jpg")

Observa las celdas fusionadas. Las celdas combinadas suelen ser útiles para hacer que los datos legibles para el ser pero pueden causar problemas en el análisis mecánico.

question("¿Qué problemas crees que encontrará R al cargar esta base de datos?",
  answer("R no reconocerá las celdas fusionadas y eliminará el valor.",
         message = "De hecho, R no puede reconocer las celdas fusionadas. Sin embargo, no eliminará todos esos valores."),
  answer("R reconocerá la celda fusionada y también creará una celda fusionada.",
         message = "Los objetos dataframe en R no pueden contener celdas fusionadas."),
  answer("R no reconocerá la celda fusionada, por lo que solo mantendrá el valor en la primera fila y columna del área fusionada.", correct = TRUE),
  answer("R reconocerá las celdas fusionadas y duplicará estos valores en el área fusionada.",
         message = "Desafortunadamente, la mayoría de las funciones de importación en R no harán esto de forma predeterminada."),
  allow_retry =  TRUE
)

¿Cómo se importan a R las celdas fusionadas utilizando el programa import() función de {rio}? Haz clic en la base de datos para abrirlo en el panel Visor. Mira la fila 8 como ejemplo.

knitr::include_graphics("images/site_coverage_imported.jpg")

Como ves, importar este base de datos a R en el formato Excel dado conlleva la pérdida de datos de múltiples formas:

Utilizando la import() R no reconocerá el formato de las celdas combinadas y todas las celdas, excepto la primera, se leerán como vacías. Esto provocará la pérdida de datos y dificultará el análisis.

Una solución es utilizar un paquete diferente para importar los datos. La página {openxlsx} R maneja los libros de Excel con más precisión que {rio}. Su función read.xlsx() ofrece un argumento fillMergedCells = que puede ajustarse a TRUE. Para esta función, el argumento para el nombre de la hoja también es sheet =.

sites <- openxlsx::read.xlsx(here("datos", "brutos", "ejemplos_datos_desordenados.xlsx"),
                             sheet = "messy_site_coverage",
                             fillMergedCells = TRUE)

El argumento fillMergedCells = puede hacer que el valor fusionado aparezca en todas sus celdas, pero la hoja de cálculo sigue siendo muy difícil de analizar:

knitr::include_graphics("images/site_coverage_filled.jpg")

Datos ordenados

Aún no está claro qué columnas utilizar: harían falta muchos comandos de R para limpiar los datos y producir incluso una simple tabulación de los valores "Sí" por centro.

¿Cómo introducirías estos datos en una hoja de cálculo de forma "ordenada" y legible por máquina?

question("Si estos datos se ingresaran en un formato ordenado, ¿Cuáles serían los encabezados de las columnas?",
  answer("Día, Celular, Provincia, X", message =  ""),
  answer("Fecha, Provincia, Sitio, Estado", correct=T, message= "Correcto, estas son las variables importantes, siendo el Estado Sí o No."),
  answer("mayo, junio"),
  answer("A, B, Sitio, Fecha"),
  allow_retry = TRUE
)

Mira la hoja que has importado como objeto cobertura_lugares_ord. Contiene los mismos datos que site_coverage pero en un formato ordenado (también llamado formato "largo"). Mira cómo:

knitr::include_graphics("images/site_coverage_long.jpg")

El formato anterior no es muy fácil de leer para los humanos, pero R lo importa y lo maneja con facilidad.

Una vez en R, es relativamente fácil trabajar con la base de datos. No esperamos que entiendas el código que aparece a continuación, pero debes saber que limpia y amplía estos datos ordenados para que todas las fechas y lugares posibles estén presentes en los datos.

# importa la datos en formato "largo"(vertical) 
cobertura_lugares_limpio <- cobertura_lugares_ord %>%    # crea una base de datos completa
  mutate(Date = ymd(Date)) %>%           # convierte fechas a su clase apropiada en R
  complete(                              # completa todos los sitios y fechas
    Date = seq.Date(
      from = min(Date),
      to = max(Date),
      by = "day"),
    Site = seq(1:14),
    fill = list(Status = "No")) %>%      # Si no esta listado en los datos, el estatus es "No"
  mutate(Province = as_factor(ifelse(Site %in% 1:7, "A", "B")), # Añade provincias
         Site = as_factor(Site)) 

Ahora la base de datos se ha ampliado de r nrow(cobertura_lugares_ord) filas a r nrow(cobertura_lugares_limpio) filas - todas las fechas y lugares posibles - ¡Una base de datos completa! (¡una fila por cada celda de la desordenada hoja de cálculo Excel original!)

cobertura_lugares_limpio

Posibilidades ampliadas

Con los datos en formato "ordenado", ¡las posibilidades de transformación y análisis de los datos se abren de par en par!

Por ejemplo, podemos utilizar la función {ggplot2} paquete R de visualización de datos para crear un "gráfico de calor" que se parezca a la hoja de cálculo original de Excel.

No esperamos que entiendas o escribas este código ahora mismo. Aprenderás más sobre {ggplot2} en sesiones posteriores

# crea un gráfico de mosaico de calor
ggplot(data =  cobertura_lugares_limpio,
       mapping = aes(x = Date, y = fct_rev(Site),
                     fill = Status, label = Status))+
  geom_tile(color = "white")+
  geom_text()+
  scale_x_date(
    date_breaks = "day",
    labels = scales::label_date_short(),
    expand = c(0,0))+
  scale_fill_manual(
    values = c(
      "Si" = "darkgreen",
      "No" = "orange"))+
  theme_minimal(base_size = 16)+
  labs(title = "Site coverage",
       y = "Site")+
  facet_wrap(~Province, ncol = 1, scales = "free_y")

Si no entiendes el código anterior, no pasa nada; sólo queremos mostrarte que con unas pocas líneas de código R puedes crear una salida "similar a Excel", "legible por humanos", que es mucho más más fácil de analizar que la hoja de cálculo original.

En muchos sentidos, esta configuración es más útil que la hoja de cálculo Excel original:

A diferencia del Excel, ¡Esta base de datos R se puede analizar! Sólo se necesitan unas pocas líneas de código para tabular Status en Province:

cobertura_lugares_limpio %>% 
  tabyl(Province, Status) 

O por Date:

cobertura_lugares_limpio %>% 
  tabyl(Date, Status) %>% 
  arrange(desc(No))

O los datos pueden agregarse en semanas y tabular el número de plazas sin cubrir:

cobertura_lugares_limpio %>% 
  group_by(week_of = floor_date(Date, "week")) %>% 
  summarise(days_coverage_needed = sum(Status == "No")) 

O se pueden utilizar los datos para hacer rápidamente otros gráficos informativos:

cobertura_lugares_limpio %>% 
  filter(Status == "Si") %>% 
  ggplot(mapping = aes(x = fct_infreq(Site)))+
  geom_bar(fill = "dodgerblue")+
  coord_flip()+
  theme_minimal(base_size = 16)+
  labs(title = "Numero de dias 'cubiertos', por sitio",
       x = "Sitio",
       y = "Numero de dias con cobertura of days with coverage")

Espacio vacío

Examina la hoja de Excel "messy_gis", que registra información sobre dispensarios concretos, incluidas las coordenadas GPS, la organización asociada operativa y la capacidad de camas.

knitr::include_graphics("images/messydata.PNG")
quiz(
   question("¿Cuáles cree que pueden ser los problemas al importar esto a R? Marque todos los que sean más probables de aplicar.",
    answer("No creo que haya ningún problema cuando carguemos esto en R.", message = "El formato no será el mismo en R."),
    answer("R no podrá leer esta base de datos.", message = "R podrá leer esta base de datos, ¡sin embargo, requerirá mucha limpieza!"),
    answer("Los colores no aparecerán.", correct=TRUE, message = "Los colores no aparecerán, lo cual puede ser un problema si los colores representan otra variable!"),
    answer("Los colores aparecerán pero no está claro qué representan.", message = "Los colores no aparecerán en R."),
    answer("Algunas filas están vacías.", correct=TRUE, message = "Las filas adicionales vacías requerirán un paso de limpieza adicional."),
    answer ("Algunos valores de fila están vacíos.", correct=TRUE, message = "R puede manejar valores faltantes en diferentes celdas. El problema con esta base de datos es que algunas celdas vacías implican otro valor (el de la fila anterior).") ,
    answer("Algunas columnas están vacías.", correct=TRUE, message = "Las columnas vacías requerirán un paso de limpieza adicional."),
    answer("Algunos valores de columna están vacíos.", message = "R puede manejar valores faltantes en las columnas."),
    answer("Espacios en los nombres de las columnas.", correct=TRUE, message = "Los nombres de las columnas se cambiarán de lo que vemos en Excel si tienen espacios y, como resultado, pueden requerir un paso de limpieza adicional!"),
    answer("Espacios en los valores de celda.", message = "R puede manejar espacios cuando se trata de valores de celda. Tenga en cuenta que estos valores se registrarán como una variable de caracteres (es decir, una cadena)."),
    answer ("Diferentes formatos de grabación en la misma columna.", message = "Si la misma columna tiene diferentes formatos de grabación, R aún podrá leerla. Sin embargo, es posible que la guarde en el formato incorrecto, lo que requerirá un paso de limpieza adicional . La grabación en diferentes formatos en la misma columna tampoco sigue los principios de datos ordenados."),
    allow_retry = TRUE
  )
)

Las filas y columnas vacías adicionales dla base de datos causarán dolores de cabeza de limpieza en R.

question("¿Cuál de las celdas vacías crees que será más problemática?",
  answer ("La primera fila vacía ya que R no reconocerá los nombres de las columnas",
         message = "R realmente reconocerá la segunda fila como la fila de encabezado en este caso. Entonces, aunque esto no es ideal, esta fila vacía no es demasiado problemática."),
  answer("La tercera fila vacía.",
         message = "La tercera fila vacía se cargará como una fila de valores `NA`. Aunque no es ideal, esto se puede eliminar con bastante facilidad."),
  answer("Las columnas vacías.", correct=TRUE,
         message = "Estos serán los más problemáticos ya que probablemente tendrás que eliminarlos manualmente en R, porque una columna 'real' (`Pacientes`) tampoco contiene valores. Esto significa que necesitarás tomar nota de cada columna. número que desea eliminar y hacerlo manualmente!"),
  answer("Todas las celdas vacías.",
         message = "Todas las celdas vacías no son necesariamente problemas, ya que a veces no hay datos disponibles para una variable en particular (que pueden ser datos en sí mismos o motivos para la exclusión del análisis)."),
  allow_retry=TRUE
)

Los vacíos vacías de esta base de datos serán probablemente las más problemáticas de las opciones anteriores, ya que tendrás que eliminarlas manualmente en R.

Las filas vacías a partir de la fila número 4 también son un problema, ya que en realidad implican el valor que se da en la celda anterior, pero se registrarán como NA valores. Esto requerirá una limpieza adicional importante.

Por otra parte, los valores de la columna Patients en realidad faltan. En este caso, sería más prudente escribir "falta" o algún otro indicador de ello en cada una de esas celdas, para indicar explícitamente que esos datos faltan para esa variable concreta.

Echa un vistazo a cómo se importaron los datos a R como el objeto messy_gis.

Recursos de Excel a R

Aquí tienes algunos enlaces a tutoriales que te ayudarán en la transición a R desde Excel:

R dispone de sólidas formas de importar libros de Excel, trabajar con los datos, exportar/guardar archivos de Excel y trabajar con los matices de las hojas de Excel. Es cierto que algunos de los formatos más estéticos de Excel pueden perderse en la traducción (por ejemplo, cursiva, texto de lado, etc.). Si tu flujo de trabajo requiere pasar documentos de un lado a otro entre R y Excel conservando el formato original de Excel, prueba paquetes como {openxlsx}.

Práctica extra

En estas secciones hay algunos ejercicios de limpieza de datos para practicar.

Los niños en el punto de mira

¿Puedes crear una base de datos? partiendo del objeto dataframe o conjunto de datos vig?

Crea una base de datos separado para los niños y guárdalo en la carpeta "salidas" como "niños_foco.csv"

Crea una base de datos separado para los niños y guárdalo en la carpeta "salidas" como "niños_foco.csv"

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


# crea un objeto dataframe con los casos menores de edad
children <- vig %>% 
  filter(edad_anios < 18) %>% 
  mutate(infant = ifelse(edad_anios < 1, "Infant", "Non-infant")) %>% 
  select(id_caso, edad_anios, infant, sexo, distrito, hospital, fecha_notifica)

# exporta
export(children, "children_spotlight.csv")


Conjunto de datos SIG

¿Puedes crear estos conjuntos de datos? partiendo de los datos limpios dla base de datos vig ?

Tu equipo de SIG necesita una base de datos para poder incluir mapas en su informe rutinario de este brote. Utilizan R, por lo que esperan un archivo .rds

¿Puedes crear estos marcos de datos? partiendo de los datos limpios vig marco de d?

Tu equipo de SIG necesita una base de datos para incluirlo en sus mapas rutinarios de este brote. Utilizan R, por lo que esperan un archivo .rds

bc13e7dcb5241508dfb9551dd18ea2226da9213c que contenga la id_caso, cualquier columna que contenga información sobre la ubicación o si el paciente se movió significativamente de su residencia antes de ser detectado.

Tienen acceso a tu proyecto RStudio y esperan que el archivo se guarde en la subcarpeta "salidas con la fecha actual de los datos al final del nombre del arch.

¿Puedes hacerles este archivo?

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


# crea una base de datos solo con la información SIG
gis <- vig %>% 
  select(id_caso, distrito, distrito_res, distrito_not, translado, lat, lon)

# exporta
export(gis, here("resultados", "gis_ebola_cases_20141201.rds"))


Detección de cadenas o texto

El paquete R {stringr} forma parte del paquete {tidyverse} y contiene muchas funciones diferentes para manejar palabras y "cadenas" de caracteres. Si alguna vez quieres dividir, unir o buscar dentro de un valor de caracteres, debes buscar en el paquete Capítulo Caracteres y cadenas del Manual de Epi R.

Por ejemplo, la función str_detect() devuelve TRUE o FALSE dependiendo de si se encuentran determinados caracteres dentro de un valor especificado. Los argumentos verdaderos son:

1) string = es la columna o el texto en el que buscar 2) pattern = este es el patrón de caracteres a buscar

Por ejemplo

Ten en cuenta que esta función es distingue entre mayúsculas y minúsculas por defecto. Puedes ajustarlo utilizando el consejo que se encuentra en aquí en el Manual de Epi R.

Ahora, partiendo dla base de datos limpio vig crea uno nuevo que contenga sólo los casos notificados desde el distrito cuyo nombre contenga "West". Luego expórtalo como

Ahora, partiendo dla base de datos limpio vig crea un nuevo marco de datos que contenga sólo los casos notificados desde el distrito cuyo nombre contenga "Oeste". Luego expórtalo como "western_district_cases.csv" a la carpeta "resultados".

quiz(caption = "Cuestionario -  Distritos del Oeste",
  question("Cuantos casos ocurrieron en los distritos del Oeste?",
    allow_retry = TRUE,
    answer("170"),
    answer(vig %>% filter(str_detect(string = distrito, pattern = "West")) %>% nrow(), correct = TRUE),
    answer("418")
  )
)

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


# Crea un dataframe solo con los casos de los distritos "West"
western_district_cases <- vig %>% 
  filter(str_detect(string = distrito, pattern = "West"))

# exporta
export(western_district_cases, here("resultados", "western_district_cases.csv"))


Buscar varios patrones

Puedes buscar varios patrones a la vez, por ejemplo "Doctor" O "Médico" O "Cirujano" utilizando la "barra OR" en tu cadena de patrones. Por ejemplo

str_detect(string = distrito, pattern = "West|North|west|north")

Más información sobre esta función y cómo hacer que no distinga entre mayúsculas y minúsculas en esta sección del Manual de Epi R.

Diccionarios de datos

Un diccionario de datos, también denominado a veces "claves", es una tabla independiente de tu hoja de registro principal. Este diccionario permite especificar qué significan determinadas variables, ya sean nombres de columnas, colores u otros.

Definición de variable

Un diccionario de datos describe el significado, las unidades y el rango de valores que tiene cada columna.

Aunque estés familiarizado con una base de datos, ¡el significado de los nombres de las columnas puede no ser obvio! Es entonces cuando los diccionarios de datos resultan útiles, ya que te proporcionarán información sobre el significado del nombre de las columnas.

quiz(
question("Por ejemplo, ¿qué crees que representa una columna llamada `age_cat5`?",
    answer("La quinta categoría de edad"),
    answer("La edad de los cinco gatos"),
    answer("La categoría de edad, categorizada por rangos de edad de 5 años", correct = T),
    answer("La categoría de edad, categorizada en 5 grupos"),
  allow_retry = TRUE
  )
)

Listado de posibles valores y niveles

Los diccionarios de datos también pueden utilizarse para especificar los valores aceptables de una variable.

Por ejemplo, podrías especificar la unidad para una variable numérica (kilogramos, libras, años, meses, etc.), o los incrementos de los grupos de edad. Esto es similar a lo que proporcionaría una celda con una lista desplegable de valores en Excel.

quiz(
 question("¿Cuál de estos sería útil agregar en un diccionario de datos para especificar los niveles de valor o el formato?",
    answer ("Fecha: AAAA-MM-DD", correct=TRUE, message = "Agregar el formato de fecha es útil ya que una fecha se puede escribir de muchas maneras diferentes (por ejemplo, años en 2 o 4 dígitos, poniendo el día/mes primero , etc.) y especificar qué formato de entrada debe tener la fecha ayudará a ahorrar tiempo a la hora de limpiar su conjunto de datos"),
    answer ("sexo: 'M' o 'F' u 'otro' o 'desconocido'", correct=TRUE, message = "Especificar el formato de entrada es útil ya que puede ingresar el sexo como palabras o iniciales, que pueden diferir según el idioma o los datos de entrada. Esto también puede ser útil para decidir dónde solo está registrando 'hombres' y 'hembras' o si también está registrando otros sexos."),
    answer("Edad: en años", correct = TRUE, message = "Especificar la unidad de un valor es importante especialmente si esa unidad no es evidente. En este caso, la edad podría registrarse en días, meses o años, por ejemplo." ),
    answer("Categoría de edad, '0-4' o '5-14' o '15-44', '45+' o 'desconocido'", correct=TRUE, message="Al solicitar que se categoricen los datos, es muy importante especificar las categorías que espera, ya que pueden no ser obvias y pueden diferir según la pregunta de investigación y el tipo de datos."),
    answer("Presencia de fiebre al ingreso: 'sí', 'no' o 'desconocido'", correct=TRUE, message = "Si espera una entrada de datos binarios, es importante especificar que así como el formato que espera. En este ejemplo, en lugar de 'sí' y 'no', el recopilador de datos podría haber pedido `1` y `0`, donde 1 = `sí` y 0 = `no`. para entradas numéricas a datos binarios, es importante especificar qué representan estos números."),
    allow_retry = TRUE
  )
)

Ejemplo de diccionario de datos

Aquí tienes las 5 primeras filas de la base vig_bruta que has importado:

head(vig_bruta, 5)

Y a continuación un diccionario de datos para estos datos:

knitr::kable(datadict)

Este diccionario de datos permite comprender qué significa cada valor de columna, así como en qué unidades se registraron los valores. Esto ayuda a mantener ordenados los marcos de datos, de modo que la base de datos importado pueda ser entendido por los ordenadores, pero el epidemiólogo siga teniendo una comprensión clara de lo que representa cada columna.

Es una buena práctica crear diccionarios de datos cuando recopiles datos y crees nuevas plantillas. También te permitirán tener nombres de columnas más fáciles de utilizar para el análisis. Cuando crees tu plantilla en Excel, intenta recordar estos consejos para que tu análisis en R sea lo más sencillo posible:

Descripciones de las variables del diccionario de datos

Pensando en las buenas prácticas que acabas de aprender, intenta responder a las siguientes preguntas.

quiz(
  question("¿Cuál sería la mejor descripción para una columna llamada `Fecha`?",
    answer ("Fecha, AA-MM-JJ", message = "Esta descripción puede no ser explícita (¿qué ocurrencia registra esta fecha?) y el formato es inconsistente: AA se refiere a 'años' (es decir, en inglés), mientras que JJ se refiere a 'jour' (es decir, en francés)."),
    answer("Fecha, AA-MM-DD", message = "Esta descripción puede no ser explícita (¿qué ocurrencia registra esta fecha?)."),
    answer("Fecha de recolección de muestras, AAAA-MM-DD", correct = TRUE, message = "Esta descripción especifica explícitamente qué evento está registrando la fecha y el formato es claro y consistente."),
    answer("Fecha, AAAA.MM.DD", message = "Esta descripción puede no ser explícita (¿qué ocurrencia registra esta fecha?)."),
    allow_retry = TRUE
  )
)

Los diccionarios de datos son extremadamente útiles e importantes si los datos son registrados por varias personas o analizados por personas que no fueron las que los recopilaron. Ser lo más explícito posible en el diccionario minimiza el riesgo de malentendidos y registros inexactos. Los diccionarios de datos deben guardarse en documentos u hojas separados de tu documento Excel.

El paquete R {epikit} desarrollado conjuntamente por Applied Epi y otras organizaciones, tiene funciones específicas para importar Kobo en R.

Registro de datos

Al registrar datos, el aspecto más importante es permanecer coherente. Esto ayudará a minimizar el tiempo que se tarda en limpiar los datos, así como a reutilizar el mismo código en nuevos datos.

Registro de fechas

Las fechas pueden registrarse en numerosos formatos. Por ejemplo:

question("¿Cuál es la forma óptima de registrar una cita?",
  answer("AAAA-MM-DD", message="Así es, ¡esta es una forma correcta de registrar una fecha! ¿Estás seguro de que esta es la única forma correcta?"),
  answer("DD/MM/AA", message="Así es, ¡esta es una forma correcta de registrar una fecha! ¿Estás seguro de que esta es la única forma correcta?"),
  answer("MM/DD/AAAA", message="Así es, ¡esta es una forma correcta de registrar una fecha! ¿Estás seguro de que esta es la única forma correcta?"),
  answer("MM.DD.YY", message="Así es, ¡esta es una forma correcta de registrar una fecha! ¿Estás seguro de que esta es la única forma correcta?"),
  answer("Todos ellos pueden ser correctos", correct = TRUE,
         message="De hecho, ¡todas las opciones anteriores pueden ser formas correctas de registrar fechas!"),
  answer("Ninguna de las dos es correcta",
         message="¿Estás seguro de esto?"),
  allow_retry = TRUE
)

Las fechas pueden registrarse de múltiples formas, ninguna de las cuales es particularmente superior a otra. Lo más importante es recordar coherente en la forma de registrar la fecha, ya sea numéricamente o en cadenas, el tipo de separador utilizado, el orden o la cantidad de números previstos para días, meses, años u horas y minutos.

Registrar el sexo

question("De las siguientes opciones, ¿Cuáles crees que son buenas formas de grabar sexo?",
  answer("`f`,`h`", correct = TRUE),
  answer("`Mujer`, `Niño`, `Hombre`, `Bebé`",
         message = "'Niño' y 'Bebé' no se incluirían en una variable de 'sexo'. Si se usa 'hombre', la coherencia requeriría la contraparte 'mujer' en lugar de 'mujer', pero al registrar el sexo, 'mujer' y 'hombre' ' son la forma más correcta."),
  answer("`mujer`, `hombre`",
         message = "¡Es necesaria la coherencia en el uso de mayúsculas!"),
  answer("`F`,`M`", correct = TRUE),
  answer("`0`,`1`", correct = TRUE),
  answer("`f`,`hombre`",
         message = "¡Es necesaria la coherencia en el formato de grabación! También sería mejor utilizar el término 'hombre' en lugar de 'hombre' al grabar sexo."),
  allow_retry = TRUE
)

Al registrar los sexos, tener coherencia es clave, sea cual sea el formato utilizado.

Las letras "F" y "M" suelen entenderse bien. Ten en cuenta que esto debe ir acompañado de un diccionario de datos para que, si se utilizan otras iniciales (por ejemplo, H y F para "homme" y "femme", en francés), la persona que analice los datos sepa lo que representan.

Si se utiliza un formato numérico binario para representar el sexo, un diccionario de datos es crucial para especificar qué número se refiere a qué sexo.

Aunque no es incorrecto, en general es mejor evitar deletrear el sexo, ya que esto deja más espacio para errores tipográficos o para utilizar diferentes estilos de mayúsculas y minúsculas (que se leerán como valores diferentes en R).

Lugar de Registro

Mira la columna GPS dla base de datos siguiente:

knitr::include_graphics("images/messydata.PNG")
question("¿Cuál es el problema con esto?",
  answer("No creo que haya ningún problema con esto."),
  answer("Los datos están registrados en el formato incorrecto."),
  answer("Los datos se registran en múltiples formatos."),
  answer("Los datos se registran en el formato correcto pero en varias filas."),
  answer("Los datos están registrados en el formato incorrecto y en varias filas."),
  answer("Los datos se registran en múltiples formatos y en múltiples filas", correct = TRUE),
  allow_retry = TRUE
)

Hay dos problemas con la forma en que se registran estos datos:

question("¿Cuál crees que es la mejor manera de remediar esto?",
  answer("Armonizas los formatos (elige uno) y duplicas los datos de la primera fila a la siguiente.",
         message = "¿Está seguro de que duplicar los datos de una fila a la siguiente es la forma más eficaz de presentar sus datos?"),
  answer("Armonizas los formatos (elige uno) y separas las coordenadas en dos columnas (celda fusionada).",
         message = "¡Recuerde que R no puede leer celdas fusionadas!"),
  answer("Armonizas los formatos (elige uno) y separas las coordenadas en dos columnas (`latitud` y `longitud`).",correct = TRUE),
  answer("El formato en las filas 16 y 17 es la forma correcta de registrar datos GPS. Convierte los otros valores a este formato y combina los valores de las dos filas en una celda.",
         message = "Si bien combinar los dos valores de coordenadas en una celda es correcto, no existe un formato que sea mejor que otro al registrar coordenadas GPS. Puede elegir el que crea adecuado, pero el aspecto más importante es mantener la coherencia. "),
  answer("El formato en las filas 16 y 17 es la forma correcta de registrar datos GPS. Elimina los valores que no se ajustan a este formato y combina los valores de las dos filas en una celda.",
         message = "Si bien combinar los dos valores de coordenadas en una celda es correcto, no existe un formato que sea mejor que otro al registrar coordenadas GPS. Puede elegir el que crea adecuado, pero el aspecto más importante es mantener la coherencia. No debe eliminar los valores ya que entonces estaría eliminando datos válidos."),
  answer("Las coordenadas están bien registradas excepto la fila 14. Elimina las filas 14 y 15 y convierte las coordenadas en dos columnas.",
         message = "Eliminar las filas 14 y 15 significaría deshacerse de los datos de otras variables también, por lo que no deberías hacerlo. Podrías reemplazar el valor 'pendiente' por `NA`."),
  allow_retry = TRUE
)

Las coordenadas GPS se pueden dar en diferentes formatos:

Puedes grabar en cualquiera de estas unidades, pero la regla más importante que debes recordar al grabar la ubicación es ser coherente con el formato que utilizas.

Buenas prácticas para recoger y almacenar datos

En este tutorial, has aprendido qué son los datos ordenados y su importancia para el análisis de datos. Repasemos algunos de estos conceptos, que será importante que tengas en cuenta la próxima vez que diseñes una base de datos.

Recopilación de datos

Antes de recoger tus datos, piensa en:

Una base de datos ideal será lo suficientemente exhaustivo como para permitir tu análisis sin que sea demasiado complicado rellenarlo al recoger los datos.

Almacenamiento de datos

En este tutorial te hemos mostrado ejemplos de conjuntos de datos almacenados en Excel y analizados en R. Tanto si utilizas este formato como si no, ten en cuenta que:

Cuando crees tu plantilla de recogida de datos y cuando recojas los datos, recuerda:

Si recoges datos en varias hojas de cálculo, piensa en:

1) Si tiene sentido recogerlos en varias hojas. Por ejemplo, si cada hoja representa el registro de un lugar o mes diferente, ¿no podrías añadir en su lugar una columna para especificar a qué lugar/mes pertenece la observación?

2) Si tiene sentido tener los datos en varias hojas, pero registras variables comunes en ambas hojas, ¡mantén la coherencia en la forma de registrar los datos y en las unidades que utilizas! También debes ser coherente en las celdas en las que registras los datos, de modo que puedas automatizar la extracción de datos de las hojas de Excel en R (en lugar de seleccionar manualmente las celdas de las que extraerás los datos en cada hoja).



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