logo

¿Qué vamos a aprender?

En estas próximas clases vamos a trabajar ejercicios sobre limpieza de datos, donde practicaremos algunas funciones del preprocesamiento de datos, como lo son:

¿Qué paquetes vamos a necesitar?

library(readxl) # leer este tipo de archivo
library(dplyr) 
library(tidyr) 
library(data.table)
library(parallel)

Importar datos

Las bases de datos que utilizaremos las pueden encontrar en la plataforma de UPR-Moodle

Las bases de datos que vamos a usar inicialmente son:

setwd("C:/Users/t990315/OneDrive - University of Puerto Rico/Cursos/ESTA 5504/Archivos R/Preprocesamiento/Ejercicio")
morosidad16 <- read_xlsx("morosidad16.xlsx")
morosidad17<- read_xlsx("morosidad17.xlsx")

Estas dos bases de datos son situaciones de deudas en empresas de Costa Rica para los años 2016 y 2017, respectivamente.

1. Estructurar información

Función merge

Para unir bases de datos necesitamos una o más variables en común entre las dos bases de datos. Existen varias funciones para unir bases de datos. Una de ellas es merge(), que sirve para unir columnas de dos bases de datos diferentes.

La sintaxis de merge() es la siguiente:

merge(base1, base2, by.x="nombre variable base 1", by.y="nombre variable base 2")

En el caso en el que la variable se llame igual en las dos bases: merge(base1, base2, by="nombre variable")

Pero es importante resaltar que solamente une los casos en común entre las dos bases. Si queremos que se unan todos los casos, usamos la opción merge(base1, base2, by="nombre variable", all=TRUE)

Para más detalles, pueden revisar: Función merge en R

Como ejercicio podemos probar unir las dos bases de morosidad, dejando solo las observaciones en común y otra de todas las observaciones que esten en ambas bases.

morosidad_merge     <- merge(morosidad16, morosidad17, by= "id")
morosidad_merge_all <- merge(morosidad16, morosidad17, by= "id", all=TRUE)

Función dplyr

La segunda forma que podemos usar, para unir bases de datos es con ayuda de la función dplyr(). Las funciones se ilstran en la siguiente imagen.

Si queremos repetir el ejercicio realizado, sería:

morosidad_dplyr     <-inner_join(morosidad16, morosidad17, by= "id")
morosidad_dplyr_all <-full_join(morosidad16, morosidad17, by= "id")

¿Qué pasa si las variables se llama diferente?

La sintaxis de by sería: by = c("variable1" = "variable2").

¿Qué pasa si queremos unir por más de una variable?

La sintaxis de by sería: by = c("variable1", "variable2").

Nota importante: Para que los comandos sean más sencillos, traten de que las variables se llamen igual en las diferentes bases de datos. Esto pueden hacerlo en Excel antes de importar la base, o en R con el comando rename(): rename(base, nombre_nuevo = nombre anterior)

2. Explorar y manipular información

Para explorar inicialmente una base de datos:

str(morosidad_merge)      # estructura de datos
glimpse(morosidad_merge)  # estructura de los datos
dim(morosidad_merge)      # dimension de datos
head(morosidad_merge)     # visualizar las primeras filas
tail(morosidad_merge)     # visualizar las ultimas filas

Librería dplyr

Uno de los paquetes que más funciona para manipular datos de forma fácil es dplyr(). Este paquete tiene principalmente cinco funciones para manipular datos: select(), filter(), arrange(), mutate() y summarize().

select()

Nos permite seleccionar columnas. La sintaxis sería: select(dataframe, columna1, columna2). Por ejemplo, supongamos que queremos seleccionar únicamente las columnas de id y deuda:

morosidad1 <- select(morosidad_merge, id, deuda16, deuda17,nombre.x,situacion.x,situacion.y,Estado)
head(morosidad1)
##       id deuda16 deuda17                                  nombre.x
## 1      0  133761  265838                    VAN  ADELBERGEN ISOLDA
## 2  15452   42569   83414                  BLAISE BLAISE CHRISTOPHE
## 3  34382    4000    7808 INDUSTRIAS ELECTRONICAS COSTARRICENSES SA
## 4  47329  313886  620534                 VANNUCCI VANNUCCI ALBERTO
## 5 469611  264303  526936               SAM SAM BRIAN WALTER HUBERT
## 6 611086   52546  103397                 MULLER MULLER JEAN-CLAUDE
##            situacion.x          situacion.y   Estado
## 1 COBRO ADMINISTRATIVO        DIFICIL COBRO Inactivo
## 2 COBRO ADMINISTRATIVO COBRO ADMINISTRATIVO Inactivo
## 3 COBRO ADMINISTRATIVO COBRO ADMINISTRATIVO Inactivo
## 4 COBRO ADMINISTRATIVO COBRO ADMINISTRATIVO Inactivo
## 5 COBRO ADMINISTRATIVO COBRO ADMINISTRATIVO Inactivo
## 6        DIFICIL COBRO        DIFICIL COBRO Inactivo

filter()

Nos permite filtrar filas. La sintexis sería: filter(base, condicion). Es necesario conocer los operadores lógicos:

Por ejemplo, si queremos filtrar deudas que incrementaron y son superiores a 3 millones de colones:

morosidad2 <- filter(morosidad1, (deuda16<deuda17)& (deuda17>3000000))

mutate()

Nos permite crear nuevas columnas de forma fácil. La sintexis sería: mutate(base, nueva variable=operacion). Por ejemplo, podemos crear una variable que nos muestre la diferencia entre deuda17 y deuda16:

morosidad3 <- mutate(morosidad2, diferencia=deuda17-deuda16)

arrange()

Nos permite ordenar las base por una o varias columnas. Por ejemplo, queremos ordenar la base en orden descendente por deuda17 y por diferencia:

morosidad4 <- arrange(morosidad3, desc(deuda17), desc(diferencia))

Librería tidyr

Este paquete tiene 4 funciones principales: gather(), spread(), separate() y unite()

Para este ejercicio trabajaremos dos bases bastante sencillas para entender su funcionamiento:

est1  <- read.csv("students1.csv", header = T, sep= ",")
est2  <- read.csv("students2.csv", header = T, sep= ",")
est1
##   grade male female
## 1     A    1      5
## 2     B    5      0
## 3     C    5      2
## 4     D    5      5
## 5     E    7      4

¿Cuál es un posible problema?

male y female son valores que queremos colocar como una variable sexo, por lo que hay que cambiar la estructura de la base de datos. Esta estructura de una base de datos se conoce como “formato ancho” y queremos convertirla a un “formato largo”.

gather()

La función toma las columnas múltiples, las colapsa en una sola y crea una nueva columna con los valores respectivos.

En este ejemplo, para reestructurar esta base sería:

est1_long <- gather(est1, sexo, frecuencia, -grade)
est1_long
##    grade   sexo frecuencia
## 1      A   male          1
## 2      B   male          5
## 3      C   male          5
## 4      D   male          5
## 5      E   male          7
## 6      A female          5
## 7      B female          0
## 8      C female          2
## 9      D female          5
## 10     E female          4

spread()

Es la función contraria a ``gather()```, que nos devolvería a la tabla original. Es decir, convierte la base a formato ancho nuevamente.

est1_wide <- spread(est1_long, sexo, frecuencia)
est1_wide
##   grade female male
## 1     A      5    1
## 2     B      0    5
## 3     C      2    5
## 4     D      5    5
## 5     E      4    7

Veamos ahora qué pasa con est2:

est2
##   grade male_1 female_1 male_2 female_2
## 1     A      3        4      3        4
## 2     B      6        4      3        5
## 3     C      7        4      3        8
## 4     D      4        0      8        1
## 5     E      1        1      2        7

En este caso tenemos un doble problema tenemos valores de una misma variable en diferentes columnas y diferentes variables en una sola. En este caso nos separa a los hombres y mujeres segun la clase en la que están: 1 y 2.

Entonces tenemos que hacer dos pasos. Primero usamos la función gather():

est2_long1 <- gather(est2, sexo_clase, frecuencia, -grade)
est2_long1
##    grade sexo_clase frecuencia
## 1      A     male_1          3
## 2      B     male_1          6
## 3      C     male_1          7
## 4      D     male_1          4
## 5      E     male_1          1
## 6      A   female_1          4
## 7      B   female_1          4
## 8      C   female_1          4
## 9      D   female_1          0
## 10     E   female_1          1
## 11     A     male_2          3
## 12     B     male_2          3
## 13     C     male_2          3
## 14     D     male_2          8
## 15     E     male_2          2
## 16     A   female_2          4
## 17     B   female_2          5
## 18     C   female_2          8
## 19     D   female_2          1
## 20     E   female_2          7

Y ahora usamos la función separate().

separate()

Esta función nos permite separar columnas. En este ejemplo, queremos separar el sexo de la clase.

est2_long2 <- separate(est2_long1, sexo_clase, c("sexo", "clase"))
est2_long2
##    grade   sexo clase frecuencia
## 1      A   male     1          3
## 2      B   male     1          6
## 3      C   male     1          7
## 4      D   male     1          4
## 5      E   male     1          1
## 6      A female     1          4
## 7      B female     1          4
## 8      C female     1          4
## 9      D female     1          0
## 10     E female     1          1
## 11     A   male     2          3
## 12     B   male     2          3
## 13     C   male     2          3
## 14     D   male     2          8
## 15     E   male     2          2
## 16     A female     2          4
## 17     B female     2          5
## 18     C female     2          8
## 19     D female     2          1
## 20     E female     2          7

unite()

Es el contrario a separate(). En este caso si queremos volver a la tabla original:

est2_unida <- unite(est2_long2, sexo_clase, sexo, clase, sep="-")
est2_unida
##    grade sexo_clase frecuencia
## 1      A     male-1          3
## 2      B     male-1          6
## 3      C     male-1          7
## 4      D     male-1          4
## 5      E     male-1          1
## 6      A   female-1          4
## 7      B   female-1          4
## 8      C   female-1          4
## 9      D   female-1          0
## 10     E   female-1          1
## 11     A     male-2          3
## 12     B     male-2          3
## 13     C     male-2          3
## 14     D     male-2          8
## 15     E     male-2          2
## 16     A   female-2          4
## 17     B   female-2          5
## 18     C   female-2          8
## 19     D   female-2          1
## 20     E   female-2          7

3. Planificar variables

Para este ejercicio, usaremos la siguiente base de datos

vuelos <-read.csv("vuelos.csv",header = T,sep=",")

Esta base de datos esta compuesta por todos los vuelos realizados por una aerolinea de Estados Unidos en el 2018.

Planificar vectores

Para este ejercicio, solo trabajaremos con la columna tiempo de vuelo

tv <- vuelos$tiempo_vuelo

Supongamos que queremos crear el vector suma de tiempos de vuelos. Notemos lo importante que es saber utilizar las funciones correctas.

# Opcion 1
nn     <- 200000
inicio <- Sys.time()
suma   <- tv[1]
for(k in 2:nn){
  suma <- c(suma,suma[k]+tv[k])
}
Sys.time() - inicio
## Time difference of 31.53916 secs
# Opcion 2
inicio  <- Sys.time()
suma    <- rep(0,nn)
suma[1] <- tv[1]
for(j in 2:nn){
  suma[j] <-suma[j-1] + tv[j]
}
Sys.time() - inicio
## Time difference of 0.02042794 secs
inicio  <- Sys.time()
suma    <- cumsum(tv[1:nn])
Sys.time() - inicio
## Time difference of 0.003274918 secs

Planificar matrices

Para este ejercicio, vamos a crear una nueva base de datos

vuelos2 <- select(vuelos,"cantidad_pasajeros","numero_maletas","tiempo_vuelo")
head(vuelos2)
##   cantidad_pasajeros numero_maletas tiempo_vuelo
## 1                 57            137          233
## 2                 60            153          266
## 3                 58            152          103
## 4                 60            141          190
## 5                 60            154          380
## 6                 51            166          138
M       <- matrix(rep(0,15),nrow=3)
colnames(M) <- c("minimo","media","mediana","maximo","desviacion")
rownames(M) <- c("cantidad_pasajeros","numero_maletas","tiempo_vuelo")
M
##                    minimo media mediana maximo desviacion
## cantidad_pasajeros      0     0       0      0          0
## numero_maletas          0     0       0      0          0
## tiempo_vuelo            0     0       0      0          0

¿Cómo podriamos llenar esta matriz?

for(i in 1:3){
  val   <- as.numeric(vuelos2[,i])
  M[i,] <-c(min(val),mean(val),median(val),max(val),sd(val))
}
M
##                    minimo     media mediana maximo desviacion
## cantidad_pasajeros     32  60.00151      60     95   6.480512
## numero_maletas        109 149.99159     150    191   8.661695
## tiempo_vuelo           60 330.25809     330    600 155.757790

4. Alternativas para manejo de bases de datos “complejas”.

Existen varias alternativas para que nuestro trabajo sea eficiente al trabajar con bases de datos “complejas”. A continuación veremos algunas posibilidades.

Librería data.table

Simplemente para cargar los datos, ya nos ahorramos un buen tiempo. Veamos:

inicio   <- Sys.time()
vuelos   <- read.csv("vuelos.csv")
Sys.time()-inicio
## Time difference of 3.069431 secs
inicio       <- Sys.time()
vuelos_dt    <- fread("vuelos.csv") # cargar datos con DataTable
Sys.time()-inicio
## Time difference of 0.04000592 secs

Para más información de esta libría, pueden revisar: data.table

Veamos la sintaxis básica de esta librería.

Filtrar

# Por columa
vuelos_dt[dia_semana==2,.(destino,cantidad_pasajeros)]
##         destino cantidad_pasajeros
##      1:     BOG                 60
##      2:     CAM                 63
##      3:     DEN                 50
##      4:     BOG                 56
##      5:     PHX                 55
##     ---                           
## 174772:     MAD                 63
## 174773:     BIL                 52
## 174774:     ORL                 65
## 174775:     PEK                 62
## 174776:     DWF                 65
# Por fila

vuelos_dt[mes==2,.("Aeropuerto"=destino,"Total"=cantidad_pasajeros)]
##         Aeropuerto Total
##      1:        VLC    61
##      2:        DWF    72
##      3:        CUN    56
##      4:        VLC    66
##      5:        BOG    60
##     ---                 
## 249996:        PHX    59
## 249997:        MIA    58
## 249998:        MOS    54
## 249999:        CAM    54
## 250000:        DEN    68

Nuevas instrucciones

# Nuevas instrucciones
vuelos_dt[dia_semana==2, .N] # Total de casos
## [1] 174776
vuelos_dt[,mean(retraso_llegada<retraso_salida,na.rm=T)]
## [1] 0.750385

Agrupar

vuelos_dt[destino=="MAD",.N,by=.(mes,"retraso_grave"=(retraso_llegada+retraso_salida)>15)]
##    mes retraso_grave     N
## 1:   1         FALSE 19933
## 2:   1          TRUE     3
## 3:   2         FALSE  9969
## 4:   2          TRUE     2
## 5:   3         FALSE  9998
## 6:   4         FALSE  1840

Paralelización

Cuando se trabaja con bases de datos “complejas” también se puede paralelizar los proceso.

Lo primero que debemos hacer es saber cuantos nucleos tiene nuestra computadora.

nucleos <- detectCores()
nucleos
## [1] 12

Trabajemos con 10 núcleos, para no bloquear completamente la computadora. Pero si se desea, se pueden usar todos, pero no se podría usar la computadora en ese momento.

Pensemos en una tarea sencilla que podamos resolver con la función apply(). Calculemos la media por renglon. Esto es:

inicio  <- Sys.time()
result1 <- apply(vuelos,1,mean,na.rm=T)
Sys.time()-inicio
## Time difference of 1.294625 mins

Ahora, usando la paralelización.

cl      <- makeCluster(10)
inicio <- Sys.time()
result2 <- parApply(cl=cl,vuelos,1,mean,na.rm=T)
Sys.time()-inicio
## Time difference of 18.6816 secs
stopCluster(cl)