Tratamiento, modelizacion y visualizacion con R parte 2
Tomado del archivo de Nestor Montaño - Agosto 2019, R User Group Ecuador
Tidy: Ordenar los datos
#### LIBRERIAS ------
library(openxlsx) # para importar desde excel
library(dplyr) # manipulacion de datos
library(ggplot2) # graficos
library(lubridate) # Manipulacion de fechas
library(stringr) # Manipulacion de texto
library(tidyr) # Ordenar y juntar data
library(readr) # Importar data, modificar tipo variables
library(purrr) # Mapear funciones
# Los 4 incluidos en el tidyverse
library(magrittr) # %>% En la mayoría de los casos, antes de realizar nuestros análisis debemos reordenar los datos en algún software como excel para cumplir lo siguiente
- Que cada columna sea una variable
- Que cada fila sea una observación (granularidad)
- Que cada celda sea el valor de la variable para la observación
En el conjunto table1 se puede ver que la granularidad de la información es País, Año, mientras que las variables son: Casos y Población.
table1 <- data.frame(country = c("Afganistan", "Afganistan", "Brazil", "Brazil",
"China", "China"), year = c(1999, 2000, 1999, 2000, 1999, 2000), cases = c(745,
2666, 37737, 80488, 212258, 213766), population = c(19987071, 20595360, 172006362,
174504898, 1272915272, 1280428583))
# hacemos un data frame mejorado con tibbles
table1 <- tbl_df(table1)
table1# A tibble: 6 x 4
country year cases population
<chr> <dbl> <dbl> <dbl>
1 Afganistan 1999 745 19987071
2 Afganistan 2000 2666 20595360
3 Brazil 1999 37737 172006362
4 Brazil 2000 80488 174504898
5 China 1999 212258 1272915272
6 China 2000 213766 1280428583
Datos ordenados nos permiten trabajar fácilmente con ellos.
# A tibble: 6 x 5
country year cases population rate
<chr> <dbl> <dbl> <dbl> <dbl>
1 Afganistan 1999 745 19987071 0.373
2 Afganistan 2000 2666 20595360 1.29
3 Brazil 1999 37737 172006362 2.19
4 Brazil 2000 80488 174504898 4.61
5 China 1999 212258 1272915272 1.67
6 China 2000 213766 1280428583 1.67
# A tibble: 2 x 2
year n
<dbl> <dbl>
1 1999 250740
2 2000 296920
spread()
Se lo usa cuando una observación está en diferentes filas
table2 <- data.frame(country = c("Afganistan", "Afganistan", "Afganistan", "Afganistan",
"Brazil", "Brazil", "Brazil", "Brazil", "China", "China", "China", "China"),
year = c(1999, 1999, 2000, 2000, 1999, 1999, 2000, 2000, 1999, 1999, 2000, 2000),
type = c("cases", "population", "cases", "population", "cases", "population",
"cases", "population", "cases", "population", "cases", "population"), count = c(745,
19987071, 2666, 20595360, 37737, 172006362, 80488, 174504898, 212258, 1272915272,
213766, 1280428583))
table2 <- tbl_df(table2)
table2# A tibble: 12 x 4
country year type count
<chr> <dbl> <chr> <dbl>
1 Afganistan 1999 cases 745
2 Afganistan 1999 population 19987071
3 Afganistan 2000 cases 2666
4 Afganistan 2000 population 20595360
5 Brazil 1999 cases 37737
6 Brazil 1999 population 172006362
7 Brazil 2000 cases 80488
8 Brazil 2000 population 174504898
9 China 1999 cases 212258
10 China 1999 population 1272915272
11 China 2000 cases 213766
12 China 2000 population 1280428583
tibble [12 x 4] (S3: tbl_df/tbl/data.frame)
$ country: chr [1:12] "Afganistan" "Afganistan" "Afganistan" "Afganistan" ...
$ year : num [1:12] 1999 1999 2000 2000 1999 ...
$ type : chr [1:12] "cases" "population" "cases" "population" ...
$ count : num [1:12] 745 19987071 2666 20595360 37737 ...
Comparando table2 con table1
¿Qué columna tiene los nombres de las columnas? type
¿Qué columna contiene los valores observados? count
Para convertir table2 en datos ordenados se usa spread()
# A tibble: 6 x 4
country year cases population
<chr> <dbl> <dbl> <dbl>
1 Afganistan 1999 745 19987071
2 Afganistan 2000 2666 20595360
3 Brazil 1999 37737 172006362
4 Brazil 2000 80488 174504898
5 China 1999 212258 1272915272
6 China 2000 213766 1280428583
table3 <- data.frame(country = c("Afganistan", "Afganistan", "Brazil", "Brazil",
"China", "China"), year = c(1999, 2000, 1999, 2000, 1999, 2000), rate = c("745/19987071",
"2666/20595360", "37737/172006362", "80488/174504898", "212258/1272915272", "213766/1280428583"))
table3 country year rate
1 Afganistan 1999 745/19987071
2 Afganistan 2000 2666/20595360
3 Brazil 1999 37737/172006362
4 Brazil 2000 80488/174504898
5 China 1999 212258/1272915272
6 China 2000 213766/1280428583
¿Qué columna parece estar concatenada? rate
separate()
Permite disociar alguna columna que esté concatenada, unicamente los separa; es decir, los deja como character
country year cases population
1 Afganistan 1999 745 19987071
2 Afganistan 2000 2666 20595360
3 Brazil 1999 37737 172006362
4 Brazil 2000 80488 174504898
5 China 1999 212258 1272915272
6 China 2000 213766 1280428583
Con convert= TRUE se transforma a número
country year cases population
1 Afganistan 1999 745 19987071
2 Afganistan 2000 2666 20595360
3 Brazil 1999 37737 172006362
4 Brazil 2000 80488 174504898
5 China 1999 212258 1272915272
6 China 2000 213766 1280428583
fill()
Cuando nos pasan información procedentes de tablas dinámicas puede pasar que se nombra el primer valor y lo de abajo se asume es lo mismo
treatment <- tribble(~person, ~treatment, ~response, "Derrick Whitmore", 1, 7, NA,
2, 10, NA, 3, 9, "Katherine Burke", 1, 4)
treatment# A tibble: 4 x 3
person treatment response
<chr> <dbl> <dbl>
1 Derrick Whitmore 1 7
2 <NA> 2 10
3 <NA> 3 9
4 Katherine Burke 1 4
Con fill() se pueden llenar los NA
# A tibble: 4 x 3
person treatment response
<chr> <dbl> <dbl>
1 Derrick Whitmore 1 7
2 Derrick Whitmore 2 10
3 Derrick Whitmore 3 9
4 Katherine Burke 1 4
Ejemplo: Transacciones bancarias
El Banco del Pacífico requiere mejorar los tiempos de atención al cliente en ventanilla, para ello ha recolectado esta información anónimamente para cada cajero y transacción realizada.
Le suministran un excel con dos hojas:
Tiene los datos de las transacciones, columnas: Sucursal, Cajero, ID_Transaccion, Transaccion, Tiempo_Servicio_seg, Nivel de satisfacción, Monto de la transaccion.
Otra hoja que indica si en la sucursal se ha puesto o no el nuevo sistema.
# Cargar la libreria a utilizar
library(openxlsx)
# Leer el archivo de excel y asignarlo al objeto data_banco
data_banco <- read.xlsx(xlsxFile = "Data/Data_Banco.xlsx", sheet = "Sucursal")
data_sucursal <- read.xlsx(xlsxFile = "Data/Data_Banco.xlsx", sheet = "Data_Sucursal")Convertir a tibbles (un dataframe mejorado):
# Convertir el data_banco a un tibble
data_banco <- tbl_df(data_banco)
# Convertir el data_sucursal a un tibble
data_sucursal <- tbl_df(data_sucursal)Lo primero que necesitamos es corregir los tipos de datos, nótese que Monto tiene una mezcla de “,” y “.”
data_banco <- data_banco %>% mutate(Monto = str_replace(Monto, pattern = ",", replacement = ".")) %>%
mutate(Sucursal = as.character(Sucursal), Cajero = as.character(Cajero), Satisfaccion = parse_factor(Satisfaccion,
levels = c("Muy Malo", "Malo", "Regular", "Bueno", "Muy Bueno")), Monto = parse_number(Monto,
locale = locale(decimal_mark = ".")))Obtener la media del tiempo de servicio y del monto pero mostrar las variables como filas
# Obtener la media del tiempo de servicio y del monto Pero mostrar las variables
# como filas Se debe tener cuidado con los nombres
data_banco %>% rename(TiempoServicioSeg = Tiempo_Servicio_seg) %>% summarise_at(vars(TiempoServicioSeg,
Monto), funs(Media = mean(., na.rm = TRUE), MediaAcot = mean(., na.rm = TRUE,
trim = 0.05), Cantidad = n())) %>% gather %>% separate(key, c("Var", "Medida"),
sep = "_") %>% spread(Medida, value)# A tibble: 2 x 4
Var Cantidad Media MediaAcot
<chr> <dbl> <dbl> <dbl>
1 Monto 24299 1996. 1983.
2 TiempoServicioSeg 24299 156. 142.
Construir reportes específicos
# Obtener la media del tiempo de servicio y del monto Pero mostrar las variables
# como columnas Se debe tener cuidado con los nombres
data_banco %>% rename(TiempoServicioSeg = Tiempo_Servicio_seg) %>% summarise_at(vars(TiempoServicioSeg,
Monto), funs(Media = mean(., na.rm = TRUE), MediaAcot = mean(., na.rm = TRUE,
trim = 0.05), Cantidad = n())) %>% gather %>% separate(key, c("Var", "Medida"),
sep = "_") %>% spread(Var, value)# A tibble: 3 x 3
Medida Monto TiempoServicioSeg
<chr> <dbl> <dbl>
1 Cantidad 24299 24299
2 Media 1996. 156.
3 MediaAcot 1983. 142.
Unir dos datas: bind_rows()
Si tenemos dos bases de datos, y una es continuacion de otra usamos
# Crear un Data frame
df_1 <- data.frame(Nombre = c("Ana", "Berni", "Carlos", "Daniel", "Ericka"), Edad = c(20,
19, 20, 19, 18), Ciudad = factor(c("Gye", "Uio", "Cue", "Gye", "Cue")))
df_1 Nombre Edad Ciudad
1 Ana 20 Gye
2 Berni 19 Uio
3 Carlos 20 Cue
4 Daniel 19 Gye
5 Ericka 18 Cue
# Crear un Data frame
df_2 <- data.frame(Nombre = c("Fulton", "Gilda"), Ciudad = factor(c("Mach", "Gye")),
Edad = c(21, 18))
df_2 Nombre Ciudad Edad
1 Fulton Mach 21
2 Gilda Gye 18
Nombre Edad Ciudad
1 Ana 20 Gye
2 Berni 19 Uio
3 Carlos 20 Cue
4 Daniel 19 Gye
5 Ericka 18 Cue
6 Fulton 21 Mach
7 Gilda 18 Gye
# Aumentar columna que indica origen de la fila
bind_rows(list(df_1 = df_1, df_2 = df_2), .id = "DF_ORIGEN") DF_ORIGEN Nombre Edad Ciudad
1 df_1 Ana 20 Gye
2 df_1 Berni 19 Uio
3 df_1 Carlos 20 Cue
4 df_1 Daniel 19 Gye
5 df_1 Ericka 18 Cue
6 df_2 Fulton 21 Mach
7 df_2 Gilda 18 Gye
Aumentar columna: bind_cols()
Estado_Civil
1 S
2 D
3 S
4 C
5 D
Nombre Edad Ciudad Estado_Civil
1 Ana 20 Gye S
2 Berni 19 Uio D
3 Carlos 20 Cue S
4 Daniel 19 Gye C
5 Ericka 18 Cue D
Unir datos - Merge|Join|Buscarv
Se tienen dos data.frames con columnas o variables que hacen las veces de “key” o “id” de los mismos
Se desea agregar al primer conjunto el contenido del segundo conjunto de datos si y sólo si el “key” o “id” del segundo conjunto corresponde con el “key” o “id” del primer conjunto de datos.
Parecido al Buscarv y Vlookup de excel
Equivalente al Join de Bases de datos
Inner Join
A B
1 Ana 100
2 Daniel 200
3 Jose 300
Nombre Edad Ciudad B
1 Ana 20 Gye 100
2 Daniel 19 Gye 200
Left Join
Se unen todos, pero los que no tienen coneccion va con NA
Nombre Edad Ciudad B
1 Ana 20 Gye 100
2 Berni 19 Uio NA
3 Carlos 20 Cue NA
4 Daniel 19 Gye 200
5 Ericka 18 Cue NA
Right Join
Nombre Edad Ciudad B
1 Ana 20 Gye 100
2 Daniel 19 Gye 200
3 Jose NA <NA> 300
Join - Ejemplo transacciones bancarias
¿Qué join necesitamos realizar con nuestros datos?
View(data_banco)
View(data_sucursal)
# debemos tener el mismo tipo de datos
data_sucursal <- data_sucursal %>% mutate(ID_Sucursal = as.character(ID_Sucursal))Sucursal esta en los dos datos, y puede generar confusion, vamos a renombrar la columna sucursal por ID_Sucursal, para evitar esa confusion.
data_banco <- data_banco %>% rename(ID_Sucursal = "Sucursal") %>% left_join(data_sucursal,
by = c("ID_Sucursal"))
data_banco# A tibble: 24,299 x 9
ID_Sucursal Cajero ID_Transaccion Transaccion Tiempo_Servicio~ Satisfaccion
<chr> <chr> <chr> <chr> <dbl> <fct>
1 62 4820 2 Cobro/Pago~ 311 Muy Bueno
2 62 4820 2 Cobro/Pago~ 156 Malo
3 62 4820 2 Cobro/Pago~ 248 Regular
4 62 4820 2 Cobro/Pago~ 99 Regular
5 62 4820 2 Cobro/Pago~ 123 Muy Bueno
6 62 4820 2 Cobro/Pago~ 172 Bueno
7 62 4820 2 Cobro/Pago~ 140 Regular
8 62 4820 2 Cobro/Pago~ 247 Bueno
9 62 4820 2 Cobro/Pago~ 183 Muy Bueno
10 62 4820 2 Cobro/Pago~ 91 Muy Bueno
# ... with 24,289 more rows, and 3 more variables: Monto <dbl>, Sucursal <chr>,
# Nuevo_Sistema <chr>
Manipulación de datos - Avanzado
Aplicar funciones a las columnas Con summarise aplicábamos funciones a las columnas, con las funciones del paquete purrr (parte del tidyverse) se extienden dichas carácterísticas
- map() resulta en una lista.
- map_lgl() devuelve un vector de tipo logical.
- map_int() devuelve un vector de tipo integer.
- map_dbl() devuelve un vector de tipo double.
- map_chr() devuelve un vector de tipo character.
- map_dfr () y map_dfc () devuelven data.frame creadas unión de fila o columna respectivamente.
ID_Sucursal Cajero ID_Transaccion Transaccion
NA NA NA NA
Tiempo_Servicio_seg Satisfaccion Monto Sucursal
155.580 NA 1996.156 NA
Nuevo_Sistema
NA
Tiempo_Servicio_seg Monto
155.580 1996.156
# Obtener la media y la mediana de las columnas del data.frame
data_banco %>% select_if(is.numeric) %>% map_dfc(~c(mean(., na.rm = TRUE), median(.,
na.rm = TRUE)))# A tibble: 2 x 2
Tiempo_Servicio_seg Monto
<dbl> <dbl>
1 156. 1996.
2 122. 2087.
Obtener la media y la mediana de las columnas del data.frame agregando una columna con los nombres del estadístico obtenido
# Obtener la media y la mediana de las columnas del data.frame
data_banco %>% select_if(is.numeric) %>% map_dfc(~c(mean(., na.rm = TRUE), median(.,
na.rm = TRUE))) %>% mutate(Medida = c("Media", "Mediana"))# A tibble: 2 x 3
Tiempo_Servicio_seg Monto Medida
<dbl> <dbl> <chr>
1 156. 1996. Media
2 122. 2087. Mediana
Obtener la media y la mediana de las columnas del data.frame agregando una columna con los nombres del estadístico obtenido y en el formato requerido
# Obtener la media y la mediana de las columnas del data.frame
data_banco %>% select_if(is.numeric) %>% map_dfc(~c(mean(., na.rm = TRUE), median(.,
na.rm = TRUE))) %>% mutate(Medida = c("Media", "Mediana")) %>% gather(var, val,
1:(ncol(.) - 1)) %>% spread(key = Medida, val)# A tibble: 2 x 3
var Media Mediana
<chr> <dbl> <dbl>
1 Monto 1996. 2087.
2 Tiempo_Servicio_seg 156. 122.
Evaluar, para cada sucursal, la hipótesis de que el tiempo promedio es menor a 100 segundos
# Obtener la media y la mediana de las columnas del data.frame
data_banco %>% split(.$Sucursal) %>% map(~t.test(.$Tiempo_Servicio_seg, mu = 100,
alternative = "less"))$Alborada
One Sample t-test
data: .$Tiempo_Servicio_seg
t = 36.441, df = 3328, p-value = 1
alternative hypothesis: true mean is less than 100
95 percent confidence interval:
-Inf 186.3578
sample estimates:
mean of x
182.6271
$Centro
One Sample t-test
data: .$Tiempo_Servicio_seg
t = 60.418, df = 12043, p-value = 1
alternative hypothesis: true mean is less than 100
95 percent confidence interval:
-Inf 168.2032
sample estimates:
mean of x
166.3955
$`Mall del Sol`
One Sample t-test
data: .$Tiempo_Servicio_seg
t = 39.307, df = 4189, p-value = 1
alternative hypothesis: true mean is less than 100
95 percent confidence interval:
-Inf 184.4024
sample estimates:
mean of x
181.0116
$`Riocentro Sur`
One Sample t-test
data: .$Tiempo_Servicio_seg
t = -9.7567, df = 2837, p-value < 2.2e-16
alternative hypothesis: true mean is less than 100
95 percent confidence interval:
-Inf 91.1814
sample estimates:
mean of x
89.39253
$`Via Daule`
One Sample t-test
data: .$Tiempo_Servicio_seg
t = -15.666, df = 1897, p-value < 2.2e-16
alternative hypothesis: true mean is less than 100
95 percent confidence interval:
-Inf 84.19022
sample estimates:
mean of x
82.33456
Evaluar, para cada sucursal, la hipótesis de que el tiempo promedio es menor a 100 segundos
# Obtener la media y la mediana de las columnas del data.frame
data_banco %>% split(.$Sucursal) %>% map(~t.test(.$Tiempo_Servicio_seg, mu = 100,
alternative = "less")) %>% map_dbl(~.$p.value) Alborada Centro Mall del Sol Riocentro Sur Via Daule
1.000000e+00 1.000000e+00 1.000000e+00 1.916814e-22 2.050798e-52