Durante esta actividad, se trabajará con la base de datos
precios_2023, la cual contiene información sobre el precio
del combustible en Colombia durante 2023. Esta base de datos está
disponible en la página
oficial del Ministerio de Minas y Energía. Los datos incluyen
registros sobre el precio del combustible en diversas estaciones de
servicio del país, junto con la marca, el tipo de producto, la fecha de
registro, el departamento, el municipio y el precio del combustible.
df <- read_excel("C:/Users/jcami/Downloads/precios_2023.xlsx")
df %<>% clean_names
El análisis de las dimensiones muestra que la base de datos está compuesta por 267,943 observaciones y siete variables:
df %>% glimpse
## Rows: 267,943
## Columns: 7
## $ bandera <chr> "TERPEL", "TERPEL", "TERPEL", "TERPEL", "TERPEL", "TE…
## $ nombre_comercial <chr> "ESTACIONDESERVICIOFLUVIALBALSAJUNIN", "ESTACIONDESER…
## $ producto <chr> "GASOLINAMOTOR", "GASOLINAMOTOR", "DIESEL", "DIESEL",…
## $ fecha_registro <chr> "01-Apr-2023", "01-Apr-2023", "01-Apr-2023", "01-Apr-…
## $ departamento <chr> "AMAZONAS", "AMAZONAS", "AMAZONAS", "AMAZONAS", "AMAZ…
## $ municipio <chr> "LETICIA", "LETICIA", "LETICIA", "LETICIA", "LETICIA"…
## $ valor_precio <dbl> 12968, 13000, 11800, 11980, 12605, 11800, 12968, 1180…
La tabla a continuación resume el tipo de cada variable:
Nombre <- c("bandera","nombre_comercial","producto","fecha_registro","departamento","municipio","valor_precio")
Tipo_original <- c("Cadena","Cadena","Cadena","Cadena","Cadena","Cadena","Numerico")
Tipo_modificado <- c("Factor","Factor","Factor","Fecha","Factor","Factor","Numerico")
as.data.frame(cbind(Nombre,Tipo_original,Tipo_modificado))
## Nombre Tipo_original Tipo_modificado
## 1 bandera Cadena Factor
## 2 nombre_comercial Cadena Factor
## 3 producto Cadena Factor
## 4 fecha_registro Cadena Fecha
## 5 departamento Cadena Factor
## 6 municipio Cadena Factor
## 7 valor_precio Numerico Numerico
A continuación, se presentan las primeras 6 observaciones de la base de datos:
head(df)
## # A tibble: 6 × 7
## bandera nombre_comercial producto fecha_registro departamento municipio
## <chr> <chr> <chr> <chr> <chr> <chr>
## 1 TERPEL ESTACIONDESERVICIOFLUV… GASOLIN… 01-Apr-2023 AMAZONAS LETICIA
## 2 TERPEL ESTACIONDESERVICIOBALS… GASOLIN… 01-Apr-2023 AMAZONAS LETICIA
## 3 TERPEL ESTACIONDESERVICIOFLUV… DIESEL 01-Apr-2023 AMAZONAS LETICIA
## 4 TERPEL ESTACIONDESERVICIOBALS… DIESEL 01-Apr-2023 AMAZONAS LETICIA
## 5 TERPEL ESTACIONDESERVICIODIST… GASOLIN… 01-Apr-2023 AMAZONAS LETICIA
## 6 TERPEL ESTACIONDESERVICIODIST… DIESEL 01-Apr-2023 AMAZONAS LETICIA
## # ℹ 1 more variable: valor_precio <dbl>
Ahora procedemos a ajustar el tipo de datos, a cambiar a minuscula el texto, y a reducir nombres para hacerlos más manejables en analisis posteriores:
selected_columns <- c("bandera","nombre_comercial","producto","departamento","municipio")
df[selected_columns] <- lapply(df[selected_columns], tolower)
df$bandera <- str_replace(df$bandera , "zapatayvelasquez", "zapata")
df[selected_columns] <- lapply(df[selected_columns], as.factor)
df$fecha_registro <- dmy(df$fecha_registro)
Finalmente concluimos esta primera sección, con una descripción general de la base de datos:
summary(df)
## bandera nombre_comercial
## terpel :94823 edsautomotrizsanjosemina : 546
## primax :36112 estaciondeserviciojltsanalberto : 459
## biomax :32225 estaciondeserviciosanfrancisco : 441
## texaco :27092 estaciondeservicioautomotrizfullleo: 361
## petromil :17249 estaciondeserviciootun : 353
## coomulpinort: 8918 combustibleyhotellasmulas : 344
## (Other) :51524 (Other) :265439
## producto fecha_registro departamento
## diesel :108205 Min. :2023-01-01 narino : 31054
## extra : 32400 1st Qu.:2023-04-01 antioquia : 25009
## gasolinamotor:127338 Median :2023-06-07 nortedesantander: 21752
## Mean :2023-06-20 valledelcauca : 18754
## 3rd Qu.:2023-09-22 cundinamarca : 16631
## Max. :2023-12-31 bogotad.c. : 16031
## (Other) :138712
## municipio valor_precio
## bogota,d.c. : 16031 Min. : 0
## cali : 7388 1st Qu.: 9350
## sanandresdetumaco: 6540 Median : 10880
## sanjosedecucuta : 6183 Mean : 12023
## medellin : 5429 3rd Qu.: 13849
## barranquilla : 4366 Max. :14750147
## (Other) :222006
La variable Bandera indica la marca del combustible. El
análisis de esta variable iniciara construyendo una tabla de frecuencia
que nos ayudara a determinar el número de observaciones en cada
condición y la posible existencia de datos faltantes.
table(df$bandera)
##
## ayatawacoop biomax brio coomulpinort discom discowacoop
## 8057 32225 719 8918 2917 3895
## ecos esso octano petrdecol petrobras petrodecol
## 3613 3502 2221 3495 4871 3337
## petromil plusmas primax proxxon puma pyb
## 17249 1806 36112 167 3059 1684
## save terpel texaco zapata zeuss
## 285 94823 27092 408 7488
Debido a que la tabla de frencuencia muestra que no hay datos faltantes en esta variable, procederemos a realizar una tabla con un conteo y el porcentaje de observaciones según su clase:
Tabla_bandera <- df %>%
group_by(bandera) %>%
count() %>%
ungroup() %>%
mutate(Proporcion = `n` / sum(`n`)) %>%
arrange(Proporcion) %>%
mutate(Porcentaje = scales::percent(Proporcion))
Tabla_bandera
## # A tibble: 23 × 4
## bandera n Proporcion Porcentaje
## <fct> <int> <dbl> <chr>
## 1 proxxon 167 0.000623 0.0623%
## 2 save 285 0.00106 0.1064%
## 3 zapata 408 0.00152 0.1523%
## 4 brio 719 0.00268 0.2683%
## 5 pyb 1684 0.00628 0.6285%
## 6 plusmas 1806 0.00674 0.6740%
## 7 octano 2221 0.00829 0.8289%
## 8 discom 2917 0.0109 1.0887%
## 9 puma 3059 0.0114 1.1417%
## 10 petrodecol 3337 0.0125 1.2454%
## # ℹ 13 more rows
Culiminamos el analisis de variable Bandera visualizando
el número de observaciones por bandera en un diagrama de barras.
ggplot(Tabla_bandera, aes(x=reorder(
bandera,-n),y=n))+
geom_bar(stat="identity", position=position_dodge(),fill="darkolivegreen")+
scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = "K"))+
theme(plot.title = element_text(hjust = 0.5, size = 12, vjust = 2),
axis.text.x = element_text(angle = 90, hjust = 0.8, vjust = 0.5, ))+
ggtitle("Observaciones por bandera de combustibles")+
xlab("Bandera")+
ylab("Observaciones")
La variable Producto indica el tipo de combustible. Tal
como en la variable anterior, se construira una tabla de frecuencia para
determinar el número de observaciones en cada condición y la posible
existencia de datos faltantes.
table(df$producto)
##
## diesel extra gasolinamotor
## 108205 32400 127338
Debido a que la tabla de frencuencia muestra que no hay datos faltantes en esta variable, procederemos a realizar una tabla con un conteo y el porcentaje de observaciones según su clase:
Tabla_producto <- df %>%
group_by(producto) %>%
count() %>%
ungroup() %>%
mutate(Proporcion = `n` / sum(`n`)) %>%
arrange(Proporcion) %>%
mutate(Porcentaje = scales::percent(Proporcion))
Tabla_producto
## # A tibble: 3 × 4
## producto n Proporcion Porcentaje
## <fct> <int> <dbl> <chr>
## 1 extra 32400 0.121 12.1%
## 2 diesel 108205 0.404 40.4%
## 3 gasolinamotor 127338 0.475 47.5%
Culiminamos el analisis de variable Producto
visualizando el número de observaciones por bandera en un diagrama de
barras.
ggplot(Tabla_producto, aes(x=reorder(
producto,-n),y=n))+
geom_bar(width = 0.7, stat = "identity", position = position_dodge(0.7), fill = "cyan4") + coord_cartesian(ylim = c(0, 180000)) + geom_text(aes(label = Porcentaje), vjust = -0.5, color = "black", hjust = 0.5,position = position_dodge(0.7),angle = 0,size = 4.5) + scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = "K"))+ theme(plot.title = element_text(hjust = 0.5, size = 12, vjust = 2), axis.text.x = element_text(angle = 0))+ ggtitle("Observaciones por tipo de producto")+ xlab("Producto")+ ylab("Observaciones")
Tal como su nombre de indica, la variable fecha_registro
ofrece información sobre la fecha en donde se recolecto la información.
Debido a la naturaleza de esta varibale no es recomendable darle el
mismo tratamiento aplicado en las secciones anteriores.En este caso,
comenzaremos verificando el número de datos faltantes.
which(is.na(df$fecha_registro))
## integer(0)
Dado que no se han encontrado datos faltantes, se procedera visualizar el número de observaciones a lo largo del tiempo:
ggplot(df, aes(x = fecha_registro)) +
geom_line(stat = "count", color = "blue") +
labs(title = "Frecuencia de registros por fecha",
x = "Fecha",
y = "Numero de registros") +
theme(
plot.title = element_text(hjust = 0.5, vjust = 1, margin = margin(b = 20)),
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15))
)
De manera similar, podemos extraer los meses de la variable
fecha_registro y estudiar el número de observaciones en
cada uno de ellos. Esto se muestra acontinuación:
df$mes <- month(df$fecha_registro)
df$mes <- factor(df$mes,levels = 1:12,labels = c("Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"))
table(df$mes)
##
## Enero Febrero Marzo Abril Mayo Junio Julio
## 22369 22206 22125 21635 25590 26887 18453
## Agosto Septiembre Octubre Noviembre Diciembre
## 21959 21927 19487 25872 19433
Con estos datos podemos seguir el procedimiento aplicado en las primeras tres varibables y generar una tabla de frecuencia con datos descriptivos:
Tabla_mes <- df %>%
group_by(mes) %>%
count() %>%
ungroup() %>%
mutate(Proporcion = `n` / sum(`n`)) %>%
arrange(Proporcion) %>%
mutate(Porcentaje = scales::percent(Proporcion))
Tabla_mes
## # A tibble: 12 × 4
## mes n Proporcion Porcentaje
## <fct> <int> <dbl> <chr>
## 1 Julio 18453 0.0689 6.887%
## 2 Diciembre 19433 0.0725 7.253%
## 3 Octubre 19487 0.0727 7.273%
## 4 Abril 21635 0.0807 8.074%
## 5 Septiembre 21927 0.0818 8.183%
## 6 Agosto 21959 0.0820 8.195%
## 7 Marzo 22125 0.0826 8.257%
## 8 Febrero 22206 0.0829 8.288%
## 9 Enero 22369 0.0835 8.348%
## 10 Mayo 25590 0.0955 9.551%
## 11 Noviembre 25872 0.0966 9.656%
## 12 Junio 26887 0.100 10.035%
ggplot(Tabla_mes, aes(x=mes,y=n))+
geom_bar(stat="identity", position=position_dodge(),fill="cyan4")+
coord_cartesian(ylim = c(0, 30000))+
scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = "K"))+
theme(plot.title = element_text(hjust = 0.5, size = 12, vjust = 2),
axis.text.x = element_text(angle = 90, hjust = 0.7, vjust = 0.5))+
ggtitle("Observaciones por mes")+
xlab("Mes")+
ylab("Observaciones")
Las variables departamento y municipio
contienen información sobre el lugar donde se registro la información.
Debido a que estas variables son categoricas primer se examinaran sus
tablas de frecuencia y se revisar la posibles presencias de datos
faltantes.
table(df$departamento)
##
## amazonas antioquia arauca atlantico
## 513 25009 2312 8595
## bogotad.c. bolivar boyaca caldas
## 16031 7027 7769 4517
## caqueta casanare cauca cesar
## 2390 2744 5617 15703
## choco cordoba cundinamarca guainia
## 2640 7440 16631 261
## guaviare huila laguajira magdalena
## 1043 6355 13636 5339
## meta narino nortedesantander putumayo
## 6250 31054 21752 6610
## quindio risaralda san andres islas santander
## 2555 5435 267 10247
## sucre tolima valledelcauca vaupes
## 4358 8014 18754 153
## vichada
## 922
head(table(df$municipio),30)
##
## abejorral abrego abriaqui acacias acevedo
## 50 1061 33 459 178
## achi agrado aguachica aguadas aguadedios
## 77 60 3660 134 24
## aguazul agustincodazzi aipe alban albania
## 280 188 99 151 667
## alcala aldana alejandria algarrobo algeciras
## 90 97 28 35 131
## almaguer alpujarra altamira alvarado amaga
## 79 46 208 116 124
## amalfi ambalema anapoima ancuya andalucia
## 111 38 59 99 141
Debido a la gran número de categorias, la inspección de la tabla de frecuencia no es recomendable. Por lo cual, se solo nos hemos limitado a mostrar los 30 primeros municipios y procederemos a revisar los datos faltantes por otro metodo:
which(is.na(df$municipio))
## integer(0)
Tal que no se encontraon datos atipicos procederemos a presentar dos graficos de barras que resumen el numero de observaciones por condición:
Tabla_departamento <- df %>%
group_by(departamento) %>%
count() %>%
ungroup() %>%
mutate(Proporcion = `n` / sum(`n`)) %>%
arrange(Proporcion) %>%
mutate(Porcentaje = scales::percent(Proporcion))
Tabla_departamento
## # A tibble: 33 × 4
## departamento n Proporcion Porcentaje
## <fct> <int> <dbl> <chr>
## 1 vaupes 153 0.000571 0.0571%
## 2 guainia 261 0.000974 0.0974%
## 3 san andres islas 267 0.000996 0.0996%
## 4 amazonas 513 0.00191 0.1915%
## 5 vichada 922 0.00344 0.3441%
## 6 guaviare 1043 0.00389 0.3893%
## 7 arauca 2312 0.00863 0.8629%
## 8 caqueta 2390 0.00892 0.8920%
## 9 quindio 2555 0.00954 0.9536%
## 10 choco 2640 0.00985 0.9853%
## # ℹ 23 more rows
ggplot(Tabla_departamento, aes(x=reorder(
departamento,-n),y=n))+
geom_bar(stat="identity", position=position_dodge(),fill="darkolivegreen")+
scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = "K"))+
theme(plot.title = element_text(hjust = 0.5, size = 12, vjust = 2),
axis.text.x = element_text(angle = 90, hjust = 0.8, vjust = 0.5, ))+
ggtitle("Observaciones por departamento")+
xlab("Departamento")+
ylab("Observaciones")
Una vez más debido al gran numero de municipios considerados nos limitamos a graficas solo los 30 con más observaciones:
Tabla_municipio <- df %>%
group_by(municipio) %>%
count() %>%
ungroup() %>%
mutate(Proporcion = `n` / sum(`n`)) %>%
arrange(Proporcion) %>%
mutate(Porcentaje = scales::percent(Proporcion))
Tabla_municipio
## # A tibble: 910 × 4
## municipio n Proporcion Porcentaje
## <fct> <int> <dbl> <chr>
## 1 quipama 2 0.00000746 0.00075%
## 2 mutiscua 4 0.0000149 0.00149%
## 3 california 6 0.0000224 0.00224%
## 4 guapota 8 0.0000299 0.00299%
## 5 coper 10 0.0000373 0.00373%
## 6 chima 12 0.0000448 0.00448%
## 7 chinavita 12 0.0000448 0.00448%
## 8 sanjose 16 0.0000597 0.00597%
## 9 cacota 18 0.0000672 0.00672%
## 10 ubaque 20 0.0000746 0.00746%
## # ℹ 900 more rows
Tabla_municipio <- Tabla_municipio %>%
arrange(desc(Proporcion))
ggplot(head(Tabla_municipio,30), aes(x=reorder(
municipio,-n),y=n))+
geom_bar(stat="identity", position=position_dodge(),fill="cyan4")+
scale_y_continuous(labels = scales::label_number(scale = 1e-3, suffix = "K"))+
theme(plot.title = element_text(hjust = 0.5, size = 12, vjust = 2),
axis.text.x = element_text(angle = 90, hjust = 0.8, vjust = 0.5, ))+
ggtitle("Observaciones por municipio")+
xlab("Municipio")+
ylab("Observaciones")
La variable nombre_comercial contiene información sobre
las estaciones de servicios en donde se recolecto la información del
valor de la gasolina. Esta varibale contiene un gran cantidad de niveles
que no permiten un análisis directo de cada una de las categorias. Por
lo cual, comenzaremos transformando la variable a factor y examinando si
contiene datos faltantes:
df$nombre_comercial <- as.factor(df$nombre_comercial)
length(which(is.na(df$nombre_comercial)))
## [1] 0
Debido a que no se encontraron datos faltantes, se procedera a constrir una tabla de frecuencia con datos descriptivos:
Tabla_comercio <- df %>%
group_by(nombre_comercial) %>%
count() %>%
ungroup() %>%
mutate(Proporcion = `n` / sum(`n`)) %>%
arrange(Proporcion) %>%
mutate(Porcentaje = scales::percent(Proporcion))
Tabla_comercio
## # A tibble: 5,910 × 4
## nombre_comercial n Proporcion Porcentaje
## <fct> <int> <dbl> <chr>
## 1 e.d.scombustibleslaperlita 1 0.00000373 0.00037%
## 2 ancombustiblespacificrs 2 0.00000746 0.00075%
## 3 conexioncuarentaycincosas 2 0.00000746 0.00075%
## 4 e.d.s.playamar 2 0.00000746 0.00075%
## 5 e.d.saguaclara 2 0.00000746 0.00075%
## 6 edscombustiblesbecerras.a.s 2 0.00000746 0.00075%
## 7 edseledencampestre 2 0.00000746 0.00075%
## 8 edsnuevaesperanza 2 0.00000746 0.00075%
## 9 estaciondegasolinaoronegro 2 0.00000746 0.00075%
## 10 estaciondeservicio4m 2 0.00000746 0.00075%
## # ℹ 5,900 more rows
Una vez más debido al gran numero de categoria nos limitamos a graficas solo los 10 con más observaciones:
Tabla_comercio <- Tabla_comercio %>%
arrange(desc(Proporcion))
ggplot(head(Tabla_comercio, 10), aes(x = reorder(nombre_comercial, -n), y = n)) +
geom_bar(stat = "identity", position = position_dodge(), fill = "cyan4") +
coord_flip() +
theme(
plot.title = element_text(hjust = 0.5),
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
axis.text.y = element_text(angle = 0, hjust = 0.5),
plot.margin = margin(10, 10, 10, 20)
) +
ggtitle("Observaciones por Comercio") +
xlab("Comercio") +
ylab("Observaciones")
Finalmente, se examinara la variable valor_precio la
cual contiene el valor de la gasolina en cada una de las estaciones del
país. Primeramente comenzaremos graficando un histograma y un diagramas
de caja que nos ayudara estudiar la distribución de la variable y
posible datos atipicos:
A <- ggplot(df, aes(x = valor_precio)) +
geom_histogram(binwidth = 3600, fill = "cyan4", color = "black") +
labs(title = "Histograma", x = "Valor de la gasolina", y = "Frecuencia") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5), # Centrar el título
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
plot.margin = margin(10, 20, 10, 10)
)
B <- ggplot(df, aes(x = "", y = valor_precio)) +
geom_boxplot(fill = "cyan4", color = "black") +
labs(title = "Boxplot", x = "", y = "Valor de la gasolina") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5), # Centrar el título
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
plot.margin = margin(10, 10, 10, 20)
)
grid.arrange(A, B, ncol = 2)
Los graficos revelan la existencia de valores atipicos en la variable
valor_precio . Asi mismo, se encuentran valores no
aceptables en la varibale (p.e valor de gasolina 0). Por lo cual, antes
de proseguir utilizaremos el filtro de Hampel y el método de los
percentiles para estudiar más a profundidad estas observaciones.
# Calcular los límites usando el filtro de Hampel
lower_bound <- median(df$valor_precio, na.rm = TRUE) - 3 * mad(df$valor_precio, constant = 1, na.rm = TRUE)
upper_bound <- median(df$valor_precio, na.rm = TRUE) + 3 * mad(df$valor_precio, constant = 1, na.rm = TRUE)
# Identificar los índices de los outliers
outlier_ind <- which(df$valor_precio < lower_bound | df$valor_precio > upper_bound)
# Crear una columna que marque los outliers
df$Hampel <- ifelse(1:nrow(df) %in% outlier_ind, 1, 0)
# Ver el número de outliers
length(outlier_ind)
## [1] 30771
lower_bound <- quantile(df$valor_precio, 0.025)
upper_bound <- quantile(df$valor_precio, 0.975)
outlier_ind <- which(df$valor_precio < lower_bound | df$valor_precio > upper_bound)
# Crear una columna que marque los outliers
df$Perc <- ifelse(1:nrow(df) %in% outlier_ind, 1, 0)
# Ver el número de outliers
length(outlier_ind)
## [1] 13386
table(df$Hampel, df$Perc)
##
## 0 1
## 0 230611 6561
## 1 23946 6825
Ahora se examinaran el número de datos atipicos en función a la diferentes categorias presentes en la base de datos:
df %>%
group_by(bandera) %>%
summarise(
total = n(),
Casos_Hampel = sum(Hampel),
Casos_perc = sum(Perc),
Procentaje_Hampel = round((sum(Hampel)/30771) * 100,3 ),
Procentaje_perc = round((sum(Perc)/13386) * 100,3 ))
## # A tibble: 23 × 6
## bandera total Casos_Hampel Casos_perc Procentaje_Hampel Procentaje_perc
## <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 ayatawacoop 8057 44 524 0.143 3.92
## 2 biomax 32225 2052 809 6.67 6.04
## 3 brio 719 83 34 0.27 0.254
## 4 coomulpinort 8918 120 1958 0.39 14.6
## 5 discom 2917 106 8 0.344 0.06
## 6 discowacoop 3895 0 1724 0 12.9
## 7 ecos 3613 283 4 0.92 0.03
## 8 esso 3502 622 294 2.02 2.20
## 9 octano 2221 251 0 0.816 0
## 10 petrdecol 3495 2 13 0.006 0.097
## # ℹ 13 more rows
df %>%
group_by(producto) %>%
summarise(
total = n(),
Casos_Hampel = sum(Hampel),
Casos_perc = sum(Perc),
Procentaje_Hampel = round((sum(Hampel)/30771) * 100,3 ),
Procentaje_perc = round((sum(Perc)/13386) * 100,3 ))
## # A tibble: 3 × 6
## producto total Casos_Hampel Casos_perc Procentaje_Hampel Procentaje_perc
## <fct> <int> <dbl> <dbl> <dbl> <dbl>
## 1 diesel 108205 154 6519 0.5 48.7
## 2 extra 32400 29714 6618 96.6 49.4
## 3 gasolinamotor 127338 903 249 2.94 1.86
Notamos que el procentaje de datos atipicos depende del tipo de producto, espacialmente al utlizar el filtro de Hampel. Debido a esto procederemos a construir un diagrama de cajas por grupos del valor de la gasolina:
ggplot(df[df$valor_precio < 300000
,], aes(x = "", y = valor_precio, group = producto)) +
geom_boxplot(fill = "cyan4", color = "black") +
labs(title = "Boxplot", x = "", y = "Valor de la gasolina") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5), # Centrar el título
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
plot.margin = margin(10, 10, 10, 20)
)
Los datos muestran que aun cuando se considera el tipo de producto, los valores mayores a 50.000 se desvian considerablemente de las distribuciones. Para confirmar estas observaciones se aplicara la prueba de Rosner:
test <- rosnerTest(df$valor_precio, k = 100)
test <- test$all.stats
outlier_ind <- test[test$Outlier == TRUE,]$Obs.Num
df$Rosner <- ifelse(1:nrow(df) %in% outlier_ind, 1, 0)
Los resultados del test de Rosener indica que los valores mayores a 30.000 como datos atipicos, pero no se marcaron los valores en cero. En vista de esto, se procedera a reemplazar los valores atipicos y aquellos los menores a 1000 como faltantes.
df$valor_precio <- ifelse(df$valor_precio < 1000 | df$Rosner == TRUE, NA, df$valor_precio)
Ahora volveremos a graficar el valor del combustible:
ggplot(df, aes(x = "", y = valor_precio, group = producto)) +
geom_boxplot(fill = "cyan4", color = "black") +
labs(title = "Boxplot", x = "", y = "Valor de la gasolina") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5), # Centrar el título
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
plot.margin = margin(10, 10, 10, 20)
)
Finalmente, calcularemos estadisticas descriptivas de la varibale
valor_precio:
df %>%
get_summary_stats(valor_precio, type = "five_number")
## # A tibble: 1 × 7
## variable n min max q1 median q3
## <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 valor_precio 267843 1000 30000 9350 10880 13849
Ahora procederemos a tratar con los datos faltantes. Primero calcularemos el procentaje de datos faltantes en la base de datos.
aggr(df, numbers = TRUE, col = c("navyblue", "red"),
cex.axis = 0.7, gap = 3, ylab = c("Proportion of missingness", "Missingness pattern"))
(length(which(is.na(df$valor_precio)))/nrow(df)*100)
## [1] 0.03732137
El analisis muestra que la unica varibale que contiene datos
faltantes es el valor del combustible, siendo aproximadamente un 0.04%
del total de observaciones. Para esto utilizaremos la libreria
mice:
imp <- mice(df, m=5, maxit=50, method ='pmm', seed=500, printFlag = F)
Los resultados indican que no hay espacio disponible para realizar la imputación, presumiblemente debido al tamaño de la base de datos. En vista de esto, utilizaremos la mediana para imputar los datos de la variable. Este método se seleccionó por dos razones: 1) la inspección de los datos indica que no siguen una distribución normal, y 2) la baja proporción de datos faltantes sugiere que la pérdida de varianza asociada con la imputación simple puede ser aceptable. Sin embargo, se reconocen las ventajas de la imputación múltiple, y se sugiere realizar este ejercicio cuando se disponga de mayores recursos computacionales.
df_antes_imput <- df$valor_precio
df[which(is.na(df$valor_precio)),]$valor_precio <- median(df$valor_precio, na.rm = T)
Para comprobar la efectividad de la imputación, comprobaremos si existe algún cambio en la distribución de la varibale antes y despues de la imputción:
df_A <- as.data.frame(cbind(df_antes_imput,rep("antes",length(df_antes_imput))))
colnames(df_A) <- c("valor","imput")
df_B <- as.data.frame(cbind(df$valor_precio,rep("despues",length(df_antes_imput))))
colnames(df_B) <- c("valor","imput")
df_imput <- rbind(df_A,df_B)
levene_test(df_imput, as.numeric(valor) ~ as.factor(imput)) # Los datos son homocedasticos
## # A tibble: 1 × 4
## df1 df2 statistic p
## <int> <int> <dbl> <dbl>
## 1 1 535784 0.0219 0.882
wilcox.test(df_antes_imput,df$valor_precio)
##
## Wilcoxon rank sum test with continuity correction
##
## data: df_antes_imput and df$valor_precio
## W = 3.5883e+10, p-value = 0.9999
## alternative hypothesis: true location shift is not equal to 0
Los prueba de wilcoxon revela que la distribución de la variable no
cambio despues de la imputación. Por lo cual se concluye que el proceso
se llevo a cabo adecuadamente. Concluiremos esta sección graficando la
variable valor_precio una ultima vez:
ggplot(df, aes(x = "", y = valor_precio)) +
geom_boxplot(fill = "cyan4", color = "black") +
labs(title = "Boxplot", x = "", y = "Valor de la gasolina") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5), # Centrar el título
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
plot.margin = margin(10, 10, 10, 20)
)
Antes de proceder a visualizar los precios de la gasolina en el mapa de Colombia, realizaremos algunos análisis y visualizaciones adicionales para obtener una comprensión más profunda de los datos. Primero, examinaremos la distribución de los precios del combustible por tipo utilizando un diagrama de caja.
El diagrama de caja muestra la distribución de precios para diferentes tipos de combustibles. Este gráfico es útil para identificar la variabilidad y los valores atípicos en los precios de cada tipo de combustible.
ggplot(df, aes(x = producto, y = valor_precio, fill = producto)) +
geom_boxplot() +
labs(title = "Distribucion de Precios por Tipo de Combustible",
x = "Producto",
y = "Precio") +
theme_minimal() +
theme(legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1),
plot.title = element_text(hjust = 0.5))
scale_y_continuous(labels = scales::comma)
## <ScaleContinuousPosition>
## Range:
## Limits: 0 -- 1
Este gráfico sugiere que la gasolina extra tiende a tener un valor promedio más alto en comparación con la gasolina motor y el diésel. Esto se alinea con las tendencias generales del mercado de los últimos años.
A continuación, visualizaremos el precio promedio del diésel en cada departamento del país. Esta visualización permitirá identificar las regiones donde el precio del diésel es más alto.
resum <- df[df$producto == "diesel",] %>% group_by(departamento) %>%
summarise(precio=mean(valor_precio))
ggplot(resum, aes(x=reorder(
departamento,-precio),y=precio))+
geom_bar(stat="identity", position=position_dodge(),fill="darkolivegreen")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina diesel segun departamento")+
xlab("Departamentos")+
ylab("precio promedio")
Se observa que los departamentos de Vaupés, Amazonas y Guainía presentan los precios más altos para el diésel.
Ahora procederemos a visualizar el precio promedio de la gasolina extra en los diferentes departamentos del país.
resum <- df[df$producto == "extra",] %>% group_by(departamento) %>%
summarise(precio=mean(valor_precio))
ggplot(resum, aes(x=reorder(
departamento,-precio),y=precio))+
coord_cartesian(ylim = c(0, 25000)) +
geom_bar(stat="identity", position=position_dodge(),fill="cyan4")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina extra segun departamento")+
xlab("Departamentos")+
ylab("precio promedio")
Finalmente, visualizaremos el precio promedio de la gasolina motor en cada departamento.
resum <- df[df$producto == "gasolinamotor",] %>% group_by(departamento) %>%
summarise(precio=mean(valor_precio))
ggplot(resum, aes(x=reorder(
departamento,-precio),y=precio))+
geom_bar(stat="identity", position=position_dodge(),fill="red")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina motor segun departamento")+
xlab("Departamentos")+
ylab("precio promedio")
Por último, examinaremos el precio promedio de cada tipo de gasolina según la marca o bandera. Esto nos permitirá comparar cómo varían los precios entre diferentes marcas.
resum <- df[df$producto == "gasolinamotor",] %>% group_by(bandera) %>%
summarise(precio=mean(valor_precio))
ggplot(resum, aes(x=reorder(
bandera,-precio),y=precio))+
coord_cartesian(ylim = c(0, 20000)) +
geom_bar(stat="identity", position=position_dodge(),fill="red")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina motor segun bandera")+
xlab("Departamentos")+
ylab("precio promedio")
resum <- df[df$producto == "extra",] %>% group_by(bandera) %>%
summarise(precio=mean(valor_precio))
ggplot(resum, aes(x=reorder(
bandera,-precio),y=precio))+
coord_cartesian(ylim = c(0, 20000)) +
geom_bar(stat="identity", position=position_dodge(),fill="cyan4")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina motor segun bandera")+
xlab("Departamentos")+
ylab("precio promedio")
resum <- df[df$producto == "diesel",] %>% group_by(bandera) %>%
summarise(precio=mean(valor_precio))
ggplot(resum, aes(x=reorder(
bandera,-precio),y=precio))+
coord_cartesian(ylim = c(0, 12000)) +
geom_bar(stat="identity", position=position_dodge(),fill="darkolivegreen")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina motor segun bandera")+
xlab("Departamentos")+
ylab("precio promedio")
En la ultima sección de este ejercicio se graficara el precio de la gasolina motor en cada departamente de colombia utilizando un mapa de Colombia.
mapa_col <- st_read("C:/Users/jcami/OneDrive/Escritorio/DATAVIZ/Departamentos_Junio_2024_shp/Departamentos_Junio_2024_shp/Departamento.shp", quiet = FALSE)
## Reading layer `Departamento' from data source
## `C:\Users\jcami\OneDrive\Escritorio\DATAVIZ\Departamentos_Junio_2024_shp\Departamentos_Junio_2024_shp\Departamento.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 33 features and 6 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 4047822 ymin: 1090467 xmax: 5684465 ymax: 3053707
## Projected CRS: MAGNA-SIRGAS 2018 / Origen-Nacional
mapa_col <- st_make_valid(mapa_col)
Ahora se extraen los nombres de los departamentos desde la base de datos y el archivo shapefile. Los nombres se convierten a minúsculas y se eliminan caracteres especiales para facilitar la comparación. Esto asegura que los nombres de los departamentos en ambas fuentes sean comparables.
# Extraer los nombres de los departamentos desde la base de datos y el shapefile
dpto_1 <- tolower(df$departamento) # Base de datos
dpto_2 <- tolower(mapa_col$DeNombre)# Archivo shapefile
dpto_2 <- iconv(dpto_2, from = "UTF-8", to = "ASCII//TRANSLIT")
dpto_1 <- iconv(dpto_1, from = "UTF-8", to = "ASCII//TRANSLIT")
dpto_2 <- as.character(dpto_2)
dpto_1 <- as.character(dpto_1)
Se verifica qué nombres de la base de datos no están presentes en el shapefile. Se identifican los nombres de los departamentos que no coinciden y se corrigen manualmente para que coincidan con los del shapefile. Esto incluye ajustes como renombrar “bogotad.c.” a “cundinamarca” y “valledelcauca” a “valle del cauca”.
# Verificar qué nombres de la base de datos no están en el shapefile
M1 <- which(is.na(match(dpto_1, dpto_2)))
na_df <- df[M1,]
na_df$departamento <- droplevels(na_df$departamento)
table(na_df$departamento)
##
## bogotad.c. laguajira nortedesantander san andres islas
## 16031 13636 21752 267
## valledelcauca
## 18754
dpto_1 <- ifelse(dpto_1 == "bogotad.c.", "cundinamarca", dpto_1)
dpto_1 <- ifelse(dpto_1 == "nortedesantander", "norte de santander", dpto_1)
dpto_1 <- ifelse(dpto_1 == "valledelcauca", "valle del cauca", dpto_1)
dpto_1 <- ifelse(dpto_1 == "laguajira", "la guajira", dpto_1)
dpto_2 <- ifelse(dpto_2 == "san andres providencia y santa catalina", "san andres islas", dpto_2)
Verificamos que los nombres la base de datos y el archivos del mapa concuerden:
M2 <- which(is.na(match(dpto_1, dpto_2)))
na_df <- df[M2,]
na_df$departamento <- droplevels(na_df$departamento)
table(na_df$departamento)
## < table of extent 0 >
Se asignan códigos a los departamentos tanto en la base de datos como
en el shapefile para facilitar la combinación de datos. Luego, se
fusionan los datos de la base de datos con el shapefile utilizando la
función merge(), asegurando que se conserven todos los
datos del shapefile.
df$codigo <- dpto_1
mapa_col$codigo <- dpto_2
datos_unidos <- merge(st_drop_geometry(mapa_col), df, by = "codigo", all = TRUE, sort = FALSE)
Ahora se filtran los datos para obtener solo la información relevante sobre el producto “gasolinamotor”. Los datos se agrupan por departamento y se calcula el precio promedio de la gasolina motor en cada uno.
datos_unidos2 <- datos_unidos %>%
filter(producto == "gasolinamotor") %>%
group_by(codigo, DeCodigo, DeNombre, DeArea, DeNorma, SHAPE_Leng, SHAPE_Area) %>%
summarize(precio_prom_dpto = mean(valor_precio)) %>%
ungroup()
## `summarise()` has grouped output by 'codigo', 'DeCodigo', 'DeNombre', 'DeArea',
## 'DeNorma', 'SHAPE_Leng'. You can override using the `.groups` argument.
Se genera un resumen estadístico y se visualiza la distribución de los precios utilizando gráficos de caja y histogramas.
summary(datos_unidos2$precio_prom_dpto)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 10789 12628 12923 13160 13240 18459
breaks <- c(0, 12000, 13000, 14000, 16000, 20000)
# Crear el histograma
ggplot(datos_unidos2, aes(x = precio_prom_dpto)) +
geom_histogram(bins = length(breaks), fill = "cyan4", color = "black") +
scale_x_continuous(breaks = breaks) +
labs(title = "Histograma de precios de gasolina",
x = "Valor de la gasolina",
y = "Frecuencia") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5), # Centrar el título
axis.title.x = element_text(margin = margin(t = 15)),
axis.title.y = element_text(margin = margin(r = 15)),
plot.margin = margin(10, 20, 10, 10)
)
Se categorizan los precios promedio de la gasolina en rangos y se asignan colores a cada rango. Esto facilita la visualización de las diferencias en los precios de gasolina entre departamentos en el mapa.
datos_unidos2$precio_prom_dpto <- as.numeric(datos_unidos2$precio_prom_dpto)
precio_fac <- cut(datos_unidos2$precio_prom_dpto,
breaks = c(0, 12000,13000, 14000, 16000, 20000),
labels = c("a. < 12.000", "b. 12.000-13.000", "c. 13.000-14.000", "d. 14.000-16.000", "e. + 16.000"))
colores <- c("a. < 12.000" = "#66FFFF",
"b. 10.000-12.000" = "green",
"b. 12.000-14.000" = "yellow",
"c. 14.000-16.000" = "orange",
"d. +16.000" = "red")
Se asignan los colores a los datos de acuerdo con las categorías de precios. Se verifica que la longitud de los datos de precios coincida con el número de filas en el conjunto de datos antes de asignar los colores.
if (length(precio_fac) == nrow(datos_unidos2)) {
datos_unidos2$color <- colores[precio_fac]
cat("Colores asignados correctamente.\n")
} else {
stop("Error: La longitud de precio_fac no coincide con el número de filas en datos_unidos2.")
}
## Colores asignados correctamente.
Se fusionan los datos unificados con el shapefile, y se transforma el sistema de referencia espacial a WGS 84 (EPSG:4326) para la visualización en el mapa.
datos_unidos_final <- merge(mapa_col, datos_unidos2, by = "codigo")
datos_unidos_final <- st_transform(datos_unidos_final, crs = 4326)
st_crs(datos_unidos_final)
## Coordinate Reference System:
## User input: EPSG:4326
## wkt:
## GEOGCRS["WGS 84",
## ENSEMBLE["World Geodetic System 1984 ensemble",
## MEMBER["World Geodetic System 1984 (Transit)"],
## MEMBER["World Geodetic System 1984 (G730)"],
## MEMBER["World Geodetic System 1984 (G873)"],
## MEMBER["World Geodetic System 1984 (G1150)"],
## MEMBER["World Geodetic System 1984 (G1674)"],
## MEMBER["World Geodetic System 1984 (G1762)"],
## MEMBER["World Geodetic System 1984 (G2139)"],
## ELLIPSOID["WGS 84",6378137,298.257223563,
## LENGTHUNIT["metre",1]],
## ENSEMBLEACCURACY[2.0]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## CS[ellipsoidal,2],
## AXIS["geodetic latitude (Lat)",north,
## ORDER[1],
## ANGLEUNIT["degree",0.0174532925199433]],
## AXIS["geodetic longitude (Lon)",east,
## ORDER[2],
## ANGLEUNIT["degree",0.0174532925199433]],
## USAGE[
## SCOPE["Horizontal component of 3D system."],
## AREA["World."],
## BBOX[-90,-180,90,180]],
## ID["EPSG",4326]]
Finalmente, se utiliza la función leaflet para crear un
mapa interactivo que muestra los departamentos con un color que
representa el rango de precios de gasolina promedio. Se añade una
leyenda al mapa para identificar las categorías de precios.
leaflet(datos_unidos_final) %>%
addTiles() %>%
addPolygons(popup = ~DeNombre.x, color = "black", fillColor = ~color, weight = 1.1) %>%
addLegend(position = "topright",
pal = colorFactor(palette = c("#66FFFF","green", "yellow", "orange", "red"),
domain = levels(precio_fac)),
values = levels(precio_fac),
title = "Precio promedio de gasolina motor en 2023")