Un CRAN a la vez: janitor

Así damos inicio a la sección dada a llamar Un CRAN a la vez (inspirada en este proyecto de la gran Danielle Navarro), mostrando lo que hace una simpática biblioteca llamada janitor, hecha por Sam Firke y subida aqui, en conjunción con nuestro querido, y ya clásico, tidyverse.

library(tidyverse)
library(janitor)

janitor es una biblioteca que ayuda a limpiar nombres de columnas que vienen formateados de forma subóptima (para ser generosos) para el análisis de datos. Generalmente sirve para aquellos Excel que nos pasan con nombres de columna con tildes, puntos como separadores, duplicados, entre otros problemillas. Para aquellos que saben inglés, Allison Horst (/allisonhorst en Twitter) hizo una ilustración sobre este paquete que es bastante informativa:

Veamos un ejemplo de un dataset real, truncado por temas de confidencialidad, en el que una biblioteca como janitor nos vino al pelo para volverlo más ameno para su procesamiento.

datos_originales <- read.csv("https://elradar.github.io/data/data_janitor.csv", encoding = "latin1")
glimpse(datos_originales)
## Observations: 17
## Variables: 8
## $ X                   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...
## $ Edad                <int> 21, 51, 42, 56, 19, 78, 33, 38, NA, 58, 46...
## $ Género              <fct> B, B, A, A, B, A, B, B, B, A, B, A, B, B, ...
## $ DNI                 <fct> B, A, A, A, B, A, B, A, B, B, B, B, A, A, ...
## $ Sabe.leer.y.escribi <fct> B, A, , A, A, A, A, A, A, A, A, A, A, A, A...
## $ Teléfono            <fct> , A, A, A, A, A, , , A, A, C, A, A, A, A, ...
## $ Visitas             <fct> A, B, A, A, B, A, A, A, A, A, A, B, A, A, ...
## $ Apoyo.espiritual    <fct> A, A, A, A, B, A, A, A, A, B, B, B, A, A, ...

Al husmear la estructura del dataset, vemos que las columnas Género y Teléfono tienen tilde, lo cual es muy correcto a nivel idiomático pero muy desaconsejado a nivel pragmático ya que tener caracteres especiales en el nombre de las columnas puede afectar muchas operaciones (que se encuentran penadas y adaptadas al inglés o el esperanto de la era digital).
Por otro lado, podemos ver que en muchos de los nombres de columnas han decidido usar el punto como separador de palabras. Esto tiene más que ver con convención (o hedonismo estético) que por utilidad, pero en general se prefiere utilizar el guión bajo o “underscore” como separador. Por último, que los nombres de las columnas se encuentren enteramente en minúscula no nos vendría nada mal para ahorrar tiempo al tipear (el viaje de ida y vuelta al Caps Lock).

datos_limpios <- datos_originales %>% 
  clean_names()
  
colnames(datos_limpios)
## [1] "x"                   "edad"                "genero"             
## [4] "dni"                 "sabe_leer_y_escribi" "telefono"           
## [7] "visitas"             "apoyo_espiritual"

Lo primero que salta a la vista es que janitor y tidyverse se llevan increíblemente bien. Lo segundo, es que con una simple función todas las variables perdieron los caracteres especiales (en nuestro caso las tildes), los puntos fueron reemplazador por “_" como separadores y todo está en bella letra minúscula. Vamos a ver si hay filas enteramente vacías (todo es posible cuando viene de Excel), que tengan siempre el mismo valor (categoría constante) y si hay alguna línea duplicada.

datos_limpios <- datos_originales %>% 
  clean_names() %>% 
  remove_empty("rows") %>% 
  remove_constant() %>% 
  convert_to_NA("factor")

duplicados <- datos_limpios %>% 
  get_dupes()
## No variable names specified - using all columns.
## No duplicate combinations found of: x, edad, genero, dni, sabe_leer_y_escribi, telefono, visitas, apoyo_espiritual
dim(datos_limpios)
## [1] 17  8
dim(duplicados)
## [1] 0 9

No tenemos filas vacías ni duplicados (tenemos la misma cantidad de observaciones) aunque sí encontró una fila con todos valores constantes y la removió (fecha_ult_ingreso).
Lo que nos va a interesar en este caso es hacer un análisis de las variables género y apoyo espiritual. Ahora, ¿no sería más útil tener en vez de categorías A y B más información sobre las categorías? Consultando el libro de código de esta base (qué linda esa prolijidad) sé que A significa hombre en la variable género (ninguna sorpresa por aquí) y B significa mujer. Además, que en la columna apoyo espiritual A representa que no solicitó apoyo espiritual y B que sí lo solicitó y lo tuvo (existe otra categoría que lo solicitó y no lo tuvo pero no nos tocaron casos en la muestra). Vamos a usar la función recode() del tidyverse para emprolijarla.
Por último, vimos que había muchas celdas vacías que no eran tomadas como casos perdidos aunque conceptualmente lo son. Vamos a ayudarnos con mutate_all() y na_if() para resolver eso; y vamos a sacarnos de encima esa variable X que viene de guardar archivos en excel sin ninguna modificación de argumento.

datos_limpios <- datos_limpios %>% 
  select(-x) %>% 
  mutate(genero = recode(genero,
                           "A" = "hombre",
                           "B" = "mujer"),
         apoyo_espiritual = recode(apoyo_espiritual,
                           "A" = "no quiso",
                           "B" = "tuvo apoyo")) %>% 
  mutate_all(na_if,"")

glimpse(datos_limpios)
## Observations: 17
## Variables: 7
## $ edad                <int> 21, 51, 42, 56, 19, 78, 33, 38, NA, 58, 46...
## $ genero              <fct> mujer, mujer, hombre, hombre, mujer, hombr...
## $ dni                 <fct> B, A, A, A, B, A, B, A, B, B, B, B, A, A, ...
## $ sabe_leer_y_escribi <fct> B, A, NA, A, A, A, A, A, A, A, A, A, A, A,...
## $ telefono            <fct> NA, A, A, A, A, A, NA, NA, A, A, C, A, A, ...
## $ visitas             <fct> A, B, A, A, B, A, A, A, A, A, A, B, A, A, ...
## $ apoyo_espiritual    <fct> no quiso, no quiso, no quiso, no quiso, tu...

Una gran ventaja de janitor es que nos permite además es hacer tablas relativamente lindas de manera sencilla y compatibles con el pipe de magrittr (%>%) que popularizó el tidyverse. En esta ocasión vamos a cruzar la variable género con apoyo espiritual que fue lo que definimos más arriba que nos interesaba analizar.

datos_limpios %>% 
  tabyl(genero, apoyo_espiritual)
##  genero no quiso tuvo apoyo
##  hombre        3          3
##   mujer        9          2

Genial, tenemos la cantidad de casos en cada celda. Pero, ¿no nos interesaría mucho más poder ver estos mismos datos como porcentajes?

datos_limpios %>% 
  tabyl(genero, apoyo_espiritual) %>% 
  adorn_totals("row") %>%   ##muestra el total por categoria de columna
  adorn_percentages("row") %>% ##nos muestra las celdas en porcentajes
  adorn_pct_formatting() %>% ##formatea el porcentaje de manera legible
  adorn_ns() %>% ##le suma a cada celda el número de casos absolutos entre paréntesis
  adorn_title() ##le suma el título del cruce en la esquina superior izquierda
##         apoyo_espiritual           
##  genero         no quiso tuvo apoyo
##  hombre       50.0%  (3)  50.0% (3)
##   mujer       81.8%  (9)  18.2% (2)
##   Total       70.6% (12)  29.4% (5)

Y así tenemos una tabla bastante linda, con bastante flexibilidad de formateo y sin salir en ningún momento del universo tidyverse (yey). Por supuesto que no sacaremos ninguna conclusión sobre estos datos ya que forman parte de una muestra no aleatoria ni representativa de un conjunto de información no disponible (aún) para su publicación.

BONUS TRACK: Otra joyita que tiene esta biblioteca janitor es la función excel_numeric_to_date() para hacer ingeniería inversa de aquellas fechas de Excel que llegan como enteros a R (si les pasó alguna vez reconocerán rápido este problema)