viernes, 24 de mayo de 2024

Limpieza de datos

Manipulación y limpieza de datos. Data OPEC

Introducción

El análisis de datos se ha convertido en una herramienta fundamental para la toma de decisiones informadas en diversos sectores. La limpieza y el procesamiento de datos representan un desafío significativo en el análisis de datos.Se estima que esta actividad puede consumir hasta el 75% del tiempo total del análisis.

No es fácil automatizar completamente la limpieza de datos. Cada base de datos, cada marco de datos, presenta características únicas y desafíos específicos. Esta diversidad dificulta la creación de procedimientos generales y automatizados para la limpieza. No obstante,la identificación de patrones comunes en datos provenientes de la misma fuente puede contribuir a optimizar las estrategias de limpieza y procesamiento.

En este post comparto los procedimientos que he seguido para limpiar datos obtenidos desde la página web de la OPEP. La aproximación que he tomado para limpiar la data no es necesariamente la mejor, ni la más elegante, para cada caso, pero me han sido útiles para obtener la data en la forma en que la necesito para el análisis.

Preprocesamientos

Funciones útiles

Cuando en nuestro marco de datos hay caracteres extraños, R transforma automáticamente toda la variable en tipo caracter o en factor. Por ejemplo:

head(opecOilPro[,1:2],12)
##             pais   X1960
## 1        Algeria   181.1
## 2         Angola     1.1
## 3        Ecuador     7.5
## 4        IR.Iran 1,067.7
## 5           Iraq   972.2
## 6        Kuwait1 1,691.8
## 7          Libya     â<U+0080><U+0093>
## 8        Nigeria    17.4
## 9          Qatar   174.6
## 10 Saudi.Arabia1 1,313.5
## 11           UAE     â<U+0080><U+0093>
## 12     Venezuela 2,846.1
 #verifico la clase de las variables
apply(opecOilPro, 2, class)
##        pais       X1960       X1970       X1980       X1990       X2000 
## "character" "character" "character" "character" "character" "character" 
##       X2010 
## "character"

También se observa que se mezclan regiones y países. Genero entonces una función que me permita solucionar tanto el tipo de variables como la separación de los países de las regiones

# para eliminar caracteres en general
sacaExtranio <-function(x, patron) as.numeric(gsub(",", "", x))

# eliminar solo comas
saca<-function(x) as.numeric(gsub(",", "", x))

Aquí se observa con mayor claridad que las regiones están mezcladas con los países:

head(worldConsuPPet)
##     pais.region    X2006    X2007    X2008    X2009    X2010 X.change10.09
## 1 North.America 22,613.4 22,642.1 21,377.6 20,586.8 21,114.9           2.6
## 2        Canada  2,208.7  2,261.6  2,197.9  2,108.7  2,188.1           3.8
## 3 United.States 20,402.1 20,377.8 19,177.1 18,475.5 18,924.2           2.4
## 4      Othersna      2.7      2.7      2.6      2.6      2.6           2.3
## 5 Latin.America  7,014.4  7,355.8  7,548.8  7,583.1  7,836.3           3.3
## 6     Argentina    501.3    550.1    562.9    565.9    589.7           4.2

Adicionalmente, hay una columna que posiblemente no me sea de utilidad. Creo una función para solucionar esto. Puedo unir la solución de este problema con el del anterior

arregla<-function(x, eliminar, numericas){
        # elimino columna
        x <-x[,-eliminar]
        
        #transformo en numerico
        x[, numericas] <-  sapply(x[, numericas], saca)
        return(x)
}

extraer <-function(x, accion, valor){
        switch(accion,
               pais = x[- valor,],
               region= x[valor,],
               otros = x[- valor]
        )         
}

En algunos análisis necesitaré apilar la data y luego sacar algunos países o regiones específicas

apilar <-function(x){
        #cambio el nombre
        names(x)[2:6]<-c(2006:2010)
        pRegiones<-stack(x[2:6])
        names(pRegiones)<-c(rep(x[,1],
                              nrow(pRegiones)/nrow(x)))
        return(pRegiones)
}

listadoRegiones<-function(apilado){
     a<-pRegiones[which(pRegiones$pais=="North.America"),]
         b<-pRegiones[which(pRegiones$pais=="Latin.America"),]
          c<-pRegiones[which(pRegiones$pais=="Eastern.Europe"),]
          d<-pRegiones[which(pRegiones$pais=="Western.Europe"),]
          e<-pRegiones[which(pRegiones$pais=="Middle.East"),]
        f<-pRegiones[which(pRegiones$pais=="Asia.and.Pacific"),]
        return(list(a=a,b=b,c=c,d=d,e=e,f=f))   
}    

Puedo unir algunos de los procedimientos anteriores para extraer en bloque información o para analizar en bloque:

dos <- function(...){
        data <- list(...)
        n<- length(data)
        d1 <- list()
        for (i in 1:n) {
             d1[[i]]<-arregla(data[[i]], 7, c(2:6))
                
        }
      return(d1)
        invisible(NULL)
}

Para extraer las regiones y los países:

reg <-c("North.America","Latin.America",
        "Netherland.Antilles", "Eastern.Europe",
        "Western.Europe", "Middle.East", "AsiaandPacific")

l<-function(df) df[df$pais.region %in% reg,]


tres <-function(lista){
        n<- length(lista)
        d1 <- list()
        for(i in 1:n){
                d1[[i]]<-l(lista[[i]])
       }
        return(d1)
        invisible(NULL)
}

l2<-function(df) df[!df$pais.region %in% reg,]

cuatro <-function(lista){
        n<- length(lista)
        d1 <- list()
        for(i in 1:n){
                d1[[i]]<-l2(lista[[i]])
       }
        return(d1)
        invisible(NULL)
}

Aplicación al Pre-procesamiento

Con la funcion dos que he creado transformo rápidamente los ocho marcos de datos, eliminando caracteres extraños y transformando la data en numérica

limpios1 <-dos(reservas, OilWorldPrd, opecRigs,
               worldRefineryCap, worldOoutpuPet,
               worldConsuPPet, worldOilExport,
               worldExportPP, worldImportP, worldImportPP)
titulos <-list("reservas.prob","produccion.petroleo",
               "balancines", "capacidad.refin",
               "derivados.pet", "consumo.deriv",
               "export.crudo", "export.der",
               "export.crudo", "importaciones.crud"
               
               )

names(limpios1)<-titulos

verifico:

# primeras seis observaciones del primer marco de datos
head(limpios1[[1]])
##     region.pais  X2006  X2007  X2008  X2009  X2010
## 1 North.America  26699  25872  26217  24021  24021
## 2        Canada   4942   4900   4900   4900   4900
## 3 United.States  21757  20972  21317  19121  19121
## 4 Latin.America 124256 137421 210210 248820 334881
## 5     Argentina   2468   2587   2616   2520   2505
## 6        Brazil  12182  12624  12624  12802  12857
# cuantos marcos de datos he pre-procesado
length(limpios1)
## [1] 10

Extraigo ahora las regiones e inspecciono el segundo marco de datos creado

regiones1 <- tres(limpios1)

head(regiones1[[2]])
##       pais.region   X2006   X2007   X2008   X2009   X2010
## 1   North.America  6447.8  6452.6  6299.2  6577.5  6718.3
## 4   Latin.America 10077.8  9835.9  9635.2  9506.8  9664.4
## 15 Eastern.Europe 11532.4 12003.7 12039.5 12386.6 12644.2
## 23 Western.Europe  4501.5  4319.9  4046.5  3817.5  3522.1
## 33    Middle.East 22900.8 22361.6 23141.6 20868.5 21026.4

Extraigo los paises e inspecciono el cuarto marco de datos creado

regiones2 <-cuatro(limpios1)

head(regiones2[[4]])
##     pais.region   X2006   X2007   X2008   X2009   X2010
## 2        Canada  2041.2  1969.5  2029.5  2039.3  1902.0
## 3 United.States 17272.6 17447.2 17379.7 17763.5 17869.2
## 5     Argentina   624.6   626.1   626.1   627.1   627.1
## 6        Brazil  1908.3  1908.3  1908.3  1908.3  1908.3
## 7      Colombia   285.9   285.9   285.9   285.9   290.9
## 8       Ecuador   176.0   188.4   188.4   188.4   188.4
tail(regiones2[[4]])
##        pais.region   X2006   X2007   X2008   X2009   X2010
## 47       Australia   704.7   701.1   707.1   724.7   757.2
## 48     Total.world 86653.1 87362.4 87476.1 88272.9 88677.3
## 49            OPEC  8261.3  8286.8  8352.5  8575.2  8819.2
## 50 OPEC.percentage     9.5     9.5     9.5     9.7     9.9
## 51            OECD 45023.2 45053.2 45138.9 45592.9 46053.6
## 52             FSU  8261.0  8311.0  8015.0  8015.0  8196.0

Todavia debo eliminar algunos casos: OPEC, OPEC.percentage, OCDE y FSU:

reg<- c("Total.world","OPEC", "OPEC.percentage",
         "OECD", "FSU")


reg2Limpia <-cuatro(regiones2)
tail(reg2Limpia[[3]])
## [1] pais  X2006 X2007 X2008 X2009 X2010
## <0 rows> (or 0-length row.names)

En los productos derivados del petróleo generados por los países OPEP tenemos también mezclados las variables que nos dan cuenta de los productos, con aquellas que nos dan cuenta del país.

head(opecProd, 12)
##    region.pais X2006 X2007 X2008 X2009 X2010 X.change10.09
## 1      Algeria 451.2 501.8 516.5 411.6 631.5          53.4
## 2     Gasoline  49.4  44.8  60.9  60.9  69.5          14.1
## 3     Kerosene  20.9  22.6  21.6  57.2  34.1       â<U+0080><U+0093>40.3
## 4  Distillates 138.2 141.4 178.0 166.5 180.7           8.5
## 5    Residuals 100.9 104.3 121.6  92.6 118.7          28.2
## 6       Others 141.7 188.7 134.5  34.3 228.5         565.5
## 7       Angola  49.8  60.6  47.8  46.2  47.0           1.7
## 8     Gasoline   1.5   1.4   1.8   0.9   1.3          52.6
## 9     Kerosene   6.3   7.6   7.0  10.0   8.5       â<U+0080><U+0093>15.0
## 10 Distillates   9.3  10.5  10.8  10.4  10.6           1.6
## 11   Residuals  10.5  11.3  11.0  13.7  12.4        â<U+0080><U+0093>9.9
## 12      Others  22.3  29.8  17.2  11.2  14.2          27.0

En este caso, aplicar cualquiera de las funciones anteriores no nos daría resultado porque los nombres de las variables no son únicos. Así, por ejemplo, “Gasolina” se repite 12 veces, por tanto, la variable es, digamos, ambigua. Es necesario, por consiguiente particularizar los nombres, de forma tal que, además, nos permita conservar los valores de la producción para cada país

# elimino la columna 7
opecProd <-opecProd[, -7]

#agrego una nueva variable
pais<-rep(c("Algeria", "Angola","Ecuador","Iran", 
        "Iraq", "Kuwait", "Libya",
        "Nigeria", "Qatar", "Saudi.Arabia",
        "Arab.Emirates","Venezuela"), each=6)
# creo un data frame para manipularlo
opepProd <-data.frame(pais, opecProd)

# extraigo la produccion agregada
produccionTotal <-opepProd[
        which(opepProd$region.pais ==  pais),]
head(produccionTotal)
##       pais region.pais  X2006  X2007  X2008  X2009  X2010
## 1  Algeria     Algeria  451.2  501.8  516.5  411.6  631.5
## 7   Angola      Angola   49.8   60.6   47.8   46.2   47.0
## 13 Ecuador     Ecuador  133.0  170.2  194.3  189.3  185.1
## 19    Iran        Iran 1447.9 1498.0 1587.0 1726.1 1743.3
## 25    Iraq        Iraq  484.2  480.8  453.2  456.6  513.2
## 31  Kuwait      Kuwait  886.7  877.4 1003.2  892.7  979.4
#Elimino la primera fila
produccionTotal<-produccionTotal[,-1]

# extraigo los productos
productosOpec <-opepProd[
        which(opepProd$region.pais !=  pais),]
head(productosOpec)
##      pais region.pais X2006 X2007 X2008 X2009 X2010
## 2 Algeria    Gasoline  49.4  44.8  60.9  60.9  69.5
## 3 Algeria    Kerosene  20.9  22.6  21.6  57.2  34.1
## 4 Algeria Distillates 138.2 141.4 178.0 166.5 180.7
## 5 Algeria   Residuals 100.9 104.3 121.6  92.6 118.7
## 6 Algeria      Others 141.7 188.7 134.5  34.3 228.5
## 8  Angola    Gasoline   1.5   1.4   1.8   0.9   1.3
# creo una nueva variable uniendo pais y producto
productos<-paste0(substring(productosOpec$pais, 1, 4),".",
                 productosOpec$region.pais)

productosOpep <-data.frame(productos, productosOpec[,3:7])
head(productosOpep)
##          productos X2006 X2007 X2008 X2009 X2010
## 2    Alge.Gasoline  49.4  44.8  60.9  60.9  69.5
## 3    Alge.Kerosene  20.9  22.6  21.6  57.2  34.1
## 4 Alge.Distillates 138.2 141.4 178.0 166.5 180.7
## 5   Alge.Residuals 100.9 104.3 121.6  92.6 118.7
## 6      Alge.Others 141.7 188.7 134.5  34.3 228.5
## 8    Ango.Gasoline   1.5   1.4   1.8   0.9   1.3

Con respecto a los pozos y los pozos productivos:

head(opecWells,2)
##      pais X2006 X2007 X2008 X2009 X2010 change10.09
## 1 Algeria   302   260   249   265   258        â<U+0080><U+0093>7
## 2  Angola    57    75    99    60   118          58
head(opecPWells, 2)
##      pais X2006 X2007 X2008 X2009 X2010 change10.09
## 1 Algeria 1.580  1.79 2.028 2.028 2.014       â<U+0080><U+0093>14
## 2  Angola 1.183  1.25 1.321 1.321 1.325           4
# elimino la columna 7
opecWells <-opecWells[,-7]
opecPWells <-opecPWells[,-7]

Finalmente, tengo el marco de datos con los balancines disponibles en el mundo:

head(opecRigs)
##            pais X2006 X2007 X2008 X2009 X2010 change10.09
## 1 North.America 2,174 2,171 2,143 1,485 2,109         624
## 2        Canada   456   360   361   313   398          85
## 3 United.States 1,718 1,811 1,782 1,172 1,711         539
## 4 Latin.America   328   389   424   426   424        â<U+0080><U+0093>2
## 5     Argentina    81    87    70    55    61           6
## 6       Bolivia     3     2     3     4     5           1
# elimino columna 7 y transformo en numérica variables 2 al 6
balancines<-arregla(opecRigs, 7, c(2:6))
head(balancines)
##            pais X2006 X2007 X2008 X2009 X2010
## 1 North.America  2174  2171  2143  1485  2109
## 2        Canada   456   360   361   313   398
## 3 United.States  1718  1811  1782  1172  1711
## 4 Latin.America   328   389   424   426   424
## 5     Argentina    81    87    70    55    61
## 6       Bolivia     3     2     3     4     5
# extraigo los balancines según región y paises
reg <-c("North.America","Latin.America",
        "Netherland.Antilles", "Eastern.Europe",
        "Western.Europe", "Middle.East", "AsiaandPacific")

balancines.reg<-balancines[balancines$pais %in% reg,]
head(balancines.reg,3)
##              pais X2006 X2007 X2008 X2009 X2010
## 1   North.America  2174  2171  2143  1485  2109
## 4   Latin.America   328   389   424   426   424
## 14 Eastern.Europe   297   409   509   473   400
# balancines por países
balancines.pais<-balancines[!balancines$pais %in% reg,]
head(balancines.pais)
##            pais X2006 X2007 X2008 X2009 X2010
## 2        Canada   456   360   361   313   398
## 3 United.States  1718  1811  1782  1172  1711
## 5     Argentina    81    87    70    55    61
## 6       Bolivia     3     2     3     4     5
## 7        Brazil    33    46    59    66    75
## 8         Chile     1     2     1     4     3

En el caso particular de esta data, ya he hecho las principales limpiezas. Haría falta eliminar la X de los años. R suele agregar esa letra al inicio de los encabezados de las variables que se inician con números. Completada la limpieza, la data está lista para explorarla, efectuar limpiezas adicionales,las transformaciones necesarias y analizarla. El corolario obligado es que vale la pena planificar también el pre-procesamiento, creado funciones que faciliten la limpieza de la data, cuando tal cosa sea posible y, de serlo, entonces, aplicar pre-procesamientos en bloque.

No hay comentarios.: