datacubeR

Utilizar el Tidyverse para imputar NAs en una tabla.

Imputar valores Perdidos

Resolviendo problemas de Stackoverflow, tenía tiempo.

picture of me

Navegando a través de stackoverflow me encontré un problema que encontré interesante de resolver. EL problema utilizaba la siguiente data:

El problema

sample <-
  structure(
    list(
      `Country Name` = c(
        "Aruba","Afghanistan","Angola","Albania","Andorra","Arab World","United Arab Emirates",
        "Argentina","Armenia","American Samoa","Antigua and Barbuda","Australia"
      ),
      `Country Code` = c(
        "ABW","AFG","AGO","ALB","AND","ARB","ARE","ARG","ARM","ASM","ATG","AUS"),
      `2007` = c(
        5.39162036843645,8.68057078513406,12.2514974459487,2.93268248162318,NA,4.74356585295154,
        NA,NA,NA,NA,1.41605259409743,NA),
      `2008` = c(
        8.95722105296535,26.4186641547444,12.4758291326398,3.36313757366391,NA,NA,12.2504202448139,
        NA,8.94995335353386, NA, 5.33380639820232, NA),
      `2009` = c(
        -2.13630037272305,-6.81116108898995,13.7302839288409,2.23139683475865,NA,2.92089711805365,
        1.55980098148558,NA,3.40676682683799,NA,-0.550159995508869,NA),
      `2010` = c(
        2.07773902027782,2.1785375238942,14.4696564932574,3.61538461538463,NA,3.91106195534027,
        0.879216764156813,NA,8.17636138473956,NA,3.3700254022015,2.91834002677376),
      `2011` = c(
        4.31633194082721,11.8041858089129,13.4824679218511,3.44283593170005,NA,4.75316388885632,
        NA,NA,7.6500080785929,NA,3.45674967234599,3.30385015608744),
      `2012` = c(
        0.627927921638161,6.44121280934118,10.2779049218839,2.03642235579081,NA,4.61184432206646,
        0.662268900269082,NA,2.55802007757907,NA,3.37688044338879,1.76278015613193),
      `2013` = c(
        -2.37226328015073,7.38577178397857,8.77781429332619,1.92544399507649,NA,3.23423783752364,
        1.10111836375706,NA,5.78966778544654,NA,1.05949782356168,2.44988864142539),
      `2014` = c(
        0.421637771012246,4.67399603536339,7.28038730361125,1.61304235314414,NA,2.77261158414198,
        2.34626865671643,NA,2.98130868933673,NA,1.08944157435363,2.48792270531403
      )
    ),
    class = c("tbl_df", "tbl", "data.frame"),
    row.names = c(NA,-12L)
  )

sample

La idea del problema es poder inputar los valores perdidos siguiendo las siguientes reglas:

  1. Algunos países tienen NAs para los 8 años de datos (columnas de la 3 a la 10) En ese caso se requiere remmplazar todos los NAs con el promedio de la columna.

  2. Otros países sólo tienen NAs en alguns columnas in ese caso se quiere reemplazar los NAs con valor del año previo.

  3. La última condición es que si el NA está en el primer año (2007) se quiere reemplazar con la media del año 2007 solamente (el 2008 fue la crisis financiera así que la inflación se fue a las nubes y ensucia el problema.

Por supuesto este problema puede ser resuelto fácilmente utilizando reglas de programación regular utilizando for loops y sentencias if. Pero la idea sería utilizar un approach Tidy utilizando librerías del Tidyverse.

library(dplyr, warn.conflicts = FALSE)
library(tidyr)
library(janitor)
# Obtener la medGetting the Column Means to Replace according to Condition 1 and 3. 
(replacement <- sample %>%
    select_if(is.numeric) %>%
    summarize_all( ~ mean(., na.rm = TRUE)) %>%
    #Transformando alista ya que es un requerimiento para 
    #tidyr::replace_na()
    as.list())

La solución final

sample %>%
  pivot_longer(`2007`:`2014`, names_to = "year", values_to = "int_rate") %>%
  group_by(`Country Name`) %>%
  summarize(na_num = is.na(int_rate) %>% sum) %>%
  #Join con el número de NAs na-num con la nueva columna
  left_join(sample, by = "Country Name") %>%
  #Reemplazar los valores de 2007. Condición 3.
  mutate(`2007` = if_else(between(na_num, 1, 7) &
                            is.na(`2007`), replacement[[1]] , `2007`)) %>%
  #Haciendo un dataset long
  pivot_longer(`2007`:`2014`, names_to = "year", values_to = "int_rate") %>%
  group_by(`Country Name`) %>%
  #imputar valores con año previo. Condición 2.
  fill(int_rate) %>%
   #Haciendo un dataset wide
  pivot_wider(names_from = year, values_from = int_rate) %>%
  #Reemplazar cuando todos los calores son NAs. Condición 1.
  replace_na(replace = replacement) 
  
Go to top