En estas próximas clases vamos a trabajar ejercicios sobre limpieza de datos, donde practicaremos algunas funciones del preprocesamiento de datos, como lo son:
Estructurar información.
Explorar y manipular datos.
Planificar variables.
Alternativas para manejo de bases de datos “complejas”.
library(readxl) # leer este tipo de archivo
library(dplyr)
library(tidyr)
library(data.table)
library(parallel)
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.
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)
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")
.
rename(): rename(base, nombre_nuevo = nombre anterior)
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
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()
.
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
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))
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)
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))
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”.
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
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()
.
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
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
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.
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
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
Existen varias alternativas para que nuestro trabajo sea eficiente al trabajar con bases de datos “complejas”. A continuación veremos algunas posibilidades.
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.
# 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
vuelos_dt[dia_semana==2, .N] # Total de casos
## [1] 174776
vuelos_dt[,mean(retraso_llegada<retraso_salida,na.rm=T)]
## [1] 0.750385
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
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)