Ordenar datos con el paquete Tidyverse

Data Science Tidy Data Tidyverse

Es un ejemplo de caso extraido del libro R4DS.

Author

Affiliation

Franklin Santos ORCID ID

AgriTech Bolivia

Published

Oct. 31, 2020

Citation

Santos, 2020

Introducción

La ordenación de datos es una de las tareas mas importantes despues de concluir la investigación. En las ciencias agrícolas, generalmente la investigación concluye con la evaluación de la cosecha del cultivo. Generalmente nuestros datos pueden estar organizados en un libro de campo; sin embargo, en otras áreas no es así.

En este blog replicaré un ejemplo de ordenación de datos con el paquete Tidyverse del libro R4DS. El dataset datos::oms contiene datos de tuberculosis (TB) detallados por año, país, edad, sexo y método de diagnóstico. Los datos provienen del Informe de Tuberculosis de la Organización Mundial de la Salud 2014, disponible en http://www.who.int/tb/country/data/download/en/.

Procedimiento de ordenación

Cargar el paquete tidyverse

El primer paso es instalar el paquete tidyverse del CRAN de R. Posterior a esto es cargar el paquete en nuestra consola de R.

library(tidyverse)
#En el paquete datos se encuentra la base de datos para este ejemplo
library(datos)

A continuación observación el estado de los datos de oms.

head(oms)
# A tibble: 6 x 60
  pais       iso2  iso3   anio nuevos_fpp_h014 nuevos_fpp_h1524
  <chr>      <chr> <chr> <int>           <int>            <int>
1 Afganistán AF    AFG    1980              NA               NA
2 Afganistán AF    AFG    1981              NA               NA
3 Afganistán AF    AFG    1982              NA               NA
4 Afganistán AF    AFG    1983              NA               NA
5 Afganistán AF    AFG    1984              NA               NA
6 Afganistán AF    AFG    1985              NA               NA
# … with 54 more variables: nuevos_fpp_h2534 <int>,
#   nuevos_fpp_h3544 <int>, nuevos_fpp_h4554 <int>,
#   nuevos_fpp_h5564 <int>, nuevos_fpp_h65 <int>,
#   nuevos_fpp_m014 <int>, nuevos_fpp_m1524 <int>,
#   nuevos_fpp_m2534 <int>, nuevos_fpp_m3544 <int>,
#   nuevos_fpp_m4554 <int>, nuevos_fpp_m5564 <int>,
#   nuevos_fpp_m65 <int>, nuevos_fpn_h014 <int>,
#   nuevos_fpn_h1524 <int>, nuevos_fpn_h2534 <int>,
#   nuevos_fpn_h3544 <int>, nuevos_fpn_h4554 <int>,
#   nuevos_fpn_h5564 <int>, nuevos_fpn_h65 <int>,
#   nuevos_fpn_m014 <int>, nuevos_fpn_m1524 <int>,
#   nuevos_fpn_m2534 <int>, nuevos_fpn_m3544 <int>,
#   nuevos_fpn_m4554 <int>, nuevos_fpn_m5564 <int>,
#   nuevos_fpn_m65 <int>, nuevos_ep_h014 <int>,
#   nuevos_ep_h1524 <int>, nuevos_ep_h2534 <int>,
#   nuevos_ep_h3544 <int>, nuevos_ep_h4554 <int>,
#   nuevos_ep_h5564 <int>, nuevos_ep_h65 <int>, nuevos_ep_m014 <int>,
#   nuevos_ep_m1524 <int>, nuevos_ep_m2534 <int>,
#   nuevos_ep_m3544 <int>, nuevos_ep_m4554 <int>,
#   nuevos_ep_m5564 <int>, nuevos_ep_m65 <int>,
#   nuevosrecaida_h014 <int>, nuevosrecaida_h1524 <int>,
#   nuevosrecaida_h2534 <int>, nuevosrecaida_h3544 <int>,
#   nuevosrecaida_h4554 <int>, nuevosrecaida_h5564 <int>,
#   nuevosrecaida_h65 <int>, nuevosrecaida_m014 <int>,
#   nuevosrecaida_m1524 <int>, nuevosrecaida_m2534 <int>,
#   nuevosrecaida_m3544 <int>, nuevosrecaida_m4554 <int>,
#   nuevosrecaida_m5564 <int>, nuevosrecaida_m65 <int>

En la salida se observa un ejemplo muy típico de una base de datos de la vida real. Contiene columnas redundantes, códigos extraños de variables y muchos valores faltantes. Practicamente, la base de datos oms está desordenado, por tanto, se necesita ordenarlo de manera sencilla con tidyverse.

Pasos de ordenación

Necesitamos agrupar todas las columnas desde nuevos_fpp_h014 hasta recaidas_m65. No sabemos aún qué representa esto, por lo que le daremos el nombre genérico de "clave". Sabemos que las celdas representan la cuenta de casos, por lo que usaremos la variable casos.

Existen múltiples valores faltantes en la representación actual, por lo que de momento usaremos na.rm para centrarnos en los valores que están presentes.

oms1 <- oms %>%
  pivot_longer(
    cols = nuevos_fpp_h014:nuevosrecaida_m65, 
    names_to = "clave", 
    values_to = "casos", 
    values_drop_na = TRUE
  )
oms1
# A tibble: 76,046 x 6
   pais       iso2  iso3   anio clave            casos
   <chr>      <chr> <chr> <int> <chr>            <int>
 1 Afganistán AF    AFG    1997 nuevos_fpp_h014      0
 2 Afganistán AF    AFG    1997 nuevos_fpp_h1524    10
 3 Afganistán AF    AFG    1997 nuevos_fpp_h2534     6
 4 Afganistán AF    AFG    1997 nuevos_fpp_h3544     3
 5 Afganistán AF    AFG    1997 nuevos_fpp_h4554     5
 6 Afganistán AF    AFG    1997 nuevos_fpp_h5564     2
 7 Afganistán AF    AFG    1997 nuevos_fpp_h65       0
 8 Afganistán AF    AFG    1997 nuevos_fpp_m014      5
 9 Afganistán AF    AFG    1997 nuevos_fpp_m1524    38
10 Afganistán AF    AFG    1997 nuevos_fpp_m2534    36
# … with 76,036 more rows

Para visualizar el conteo de valores en la nueva columna clave:

oms1 %>%
  count(clave)
# A tibble: 56 x 2
   clave               n
   <chr>           <int>
 1 nuevos_ep_h014   1038
 2 nuevos_ep_h1524  1026
 3 nuevos_ep_h2534  1020
 4 nuevos_ep_h3544  1024
 5 nuevos_ep_h4554  1020
 6 nuevos_ep_h5564  1015
 7 nuevos_ep_h65    1018
 8 nuevos_ep_m014   1032
 9 nuevos_ep_m1524  1021
10 nuevos_ep_m2534  1021
# … with 46 more rows

Para entender el significado de cada variable, se dispone de un diccionario de datos a mano. Este dice lo siguiente:

  1. Lo que aparece antes del primer _ en las columnas indica si la columna contiene casos nuevos o antiguos de tuberculosis. En este dataset, cada columna contiene nuevos casos.

  2. Lo que aparece luego de indicar si se refiere casos nuevos o antiguos es el tipo de tuberculosis:

  1. La letra que aparece después del último _ se refiere al sexo de los pacientes. El conjunto de datos agrupa en hombres (h) y mujeres (m).

  2. Los números finales se refieren al grupo etareo que se ha organizado en siete categorías:

Necesitamos hacer un pequeño cambio al formato de los nombres de las columnas: desafortunadamente lo nombres de las columnas son ligeramente inconsistentes debido a que en lugar de nuevos_recaida tenemos nuevosrecaida (es difícil darse cuenta de esto en esta parte, pero si no lo arreglas habrá errores en los pasos siguientes). Para esto, la idea básica es bastante simple: reemplazar los caracteres “nuevosrecaida” por “nuevos_recaida”. Esto genera nombres de variables consistentes.

oms2 <- oms1 %>%
  mutate(clave = stringr::str_replace(clave, "nuevosrecaida", "nuevos_recaida"))
oms2
# A tibble: 76,046 x 6
   pais       iso2  iso3   anio clave            casos
   <chr>      <chr> <chr> <int> <chr>            <int>
 1 Afganistán AF    AFG    1997 nuevos_fpp_h014      0
 2 Afganistán AF    AFG    1997 nuevos_fpp_h1524    10
 3 Afganistán AF    AFG    1997 nuevos_fpp_h2534     6
 4 Afganistán AF    AFG    1997 nuevos_fpp_h3544     3
 5 Afganistán AF    AFG    1997 nuevos_fpp_h4554     5
 6 Afganistán AF    AFG    1997 nuevos_fpp_h5564     2
 7 Afganistán AF    AFG    1997 nuevos_fpp_h65       0
 8 Afganistán AF    AFG    1997 nuevos_fpp_m014      5
 9 Afganistán AF    AFG    1997 nuevos_fpp_m1524    38
10 Afganistán AF    AFG    1997 nuevos_fpp_m2534    36
# … with 76,036 more rows

Una vez reemplazado, nos facilita separar los valores en cada código aplicando separate() dos veces. La primera aplicación dividirá los códigos en cada _.

oms3 <- oms2 %>%
  separate(clave, c("nuevos", "tipo", "sexo_edad"), sep = "_")
oms3
# A tibble: 76,046 x 8
   pais       iso2  iso3   anio nuevos tipo  sexo_edad casos
   <chr>      <chr> <chr> <int> <chr>  <chr> <chr>     <int>
 1 Afganistán AF    AFG    1997 nuevos fpp   h014          0
 2 Afganistán AF    AFG    1997 nuevos fpp   h1524        10
 3 Afganistán AF    AFG    1997 nuevos fpp   h2534         6
 4 Afganistán AF    AFG    1997 nuevos fpp   h3544         3
 5 Afganistán AF    AFG    1997 nuevos fpp   h4554         5
 6 Afganistán AF    AFG    1997 nuevos fpp   h5564         2
 7 Afganistán AF    AFG    1997 nuevos fpp   h65           0
 8 Afganistán AF    AFG    1997 nuevos fpp   m014          5
 9 Afganistán AF    AFG    1997 nuevos fpp   m1524        38
10 Afganistán AF    AFG    1997 nuevos fpp   m2534        36
# … with 76,036 more rows

A continuación podemos eliminar la columna nuevos, ya que es constante en este dataset. Además eliminaremos iso2 e iso3 ya que son redundantes.

oms3 %>%
  count(nuevos)
# A tibble: 1 x 2
  nuevos     n
  <chr>  <int>
1 nuevos 76046
oms4 <- oms3 %>%
  select(-nuevos, -iso2, -iso3)

Luego separamos sexo_edad en sexo y edad dividiendo luego del primer carácter:

oms5 <- oms4 %>%
  separate(sexo_edad, c("sexo", "edad"), sep = 1)
oms5
# A tibble: 76,046 x 6
   pais        anio tipo  sexo  edad  casos
   <chr>      <int> <chr> <chr> <chr> <int>
 1 Afganistán  1997 fpp   h     014       0
 2 Afganistán  1997 fpp   h     1524     10
 3 Afganistán  1997 fpp   h     2534      6
 4 Afganistán  1997 fpp   h     3544      3
 5 Afganistán  1997 fpp   h     4554      5
 6 Afganistán  1997 fpp   h     5564      2
 7 Afganistán  1997 fpp   h     65        0
 8 Afganistán  1997 fpp   m     014       5
 9 Afganistán  1997 fpp   m     1524     38
10 Afganistán  1997 fpp   m     2534     36
# … with 76,036 more rows

¡Ahora la base de datos oms está ordenado!

Resumen

En la anterior sección se hizo el procedimiento de ordenación paso a paso, asignando los resultados intermedios a nuevas variables. Esta no es la forma típica de trabajo. En realidad, los códigos debería ser de manera incremental usando pipes ("%>%):

fsdata<- oms %>%
  pivot_longer(
    cols = nuevos_fpp_h014:nuevosrecaida_m65,
    names_to = "clave", 
    values_to = "valor", 
    values_drop_na = TRUE) %>%
  mutate(clave = stringr::str_replace(clave, "nuevosrecaida", "nuevos_recaida")) %>%
  separate(clave, c("nuevos", "tipo", "sexo_edad")) %>%
  select(-nuevos, -iso2, -iso3) %>%
  separate(sexo_edad, c("sexo", "edad"), sep = 1)
fsdata
# A tibble: 76,046 x 6
   pais        anio tipo  sexo  edad  valor
   <chr>      <int> <chr> <chr> <chr> <int>
 1 Afganistán  1997 fpp   h     014       0
 2 Afganistán  1997 fpp   h     1524     10
 3 Afganistán  1997 fpp   h     2534      6
 4 Afganistán  1997 fpp   h     3544      3
 5 Afganistán  1997 fpp   h     4554      5
 6 Afganistán  1997 fpp   h     5564      2
 7 Afganistán  1997 fpp   h     65        0
 8 Afganistán  1997 fpp   m     014       5
 9 Afganistán  1997 fpp   m     1524     38
10 Afganistán  1997 fpp   m     2534     36
# … with 76,036 more rows

Conclusión

Es un ejemplo muy bueno para practicar y usar las diferentes funciones de tidyverse en la ordenación de datos.

Footnotes

    Citation

    For attribution, please cite this work as

    Santos (2020, Oct. 31). Franklin Santos: Ordenar datos con el paquete Tidyverse. Retrieved from https://franklinsantos.com/posts/2021-03-14-tidyexam/

    BibTeX citation

    @misc{santos2020ordenar,
      author = {Santos, Franklin},
      title = {Franklin Santos: Ordenar datos con el paquete Tidyverse},
      url = {https://franklinsantos.com/posts/2021-03-14-tidyexam/},
      year = {2020}
    }