library("readr")
library("DT")
library("ggplot2")
library("dplyr")
library("VIM")
library("naniar")
library("minqa")
library("mice")
library("tidyverse")
library("hrbrthemes")
library("gridExtra")
library("missForest")
library("outliers")
library("EnvStats")
library("sf")
library("leaflet")
library("stringr")ACT5 - VCD
Enunciados
Valentina Cabrera
Realice un análisis descriptivo completo y actualizado a julio 31 2024, incluyendo graficos, tablas y georreferenciación de la base de datos
Accidentalidad_en_Barranquilla.csv. Deben entregar un script .R con los códigos usados.Realice una exploración y un análisis descriptivo completo (incluyendo tablas de resumen y gráficos) de la base de datos disponible en este enlace. Incluya gráficos que presenten los datos sobre un mapa de Colombia, para visualizar la distribución geográfica de los precios de los combustibles en el país (2018-2024).
Desarrollo
Librerías
Punto 1
Base de datos
data <- read_csv("C:/Users/valcd/Downloads/Accidentalidad_en_Barranquilla_20240826.csv")
datatable( data[1:10, ], caption = "Base de datos: Accidentalidad en Barranquilla", options = list( scrollX = TRUE, scrollY = "450px", paging=FALSE ) )Análisis: características de la base de datos
Esta base de datos, proporcionada por la Alcaldía Distrital de Barranquilla, contiene información relacionada con los accidentes de tránsito que han ocurrido en la ciudad desde 2018 hasta el 30 de junio de 2024, según los informes policiales de accidente de tránstito (IPAT). Cada una de las columnas, proporciona información específica sobre cada evento, como por ejemplo la fecha y lugar dónde ocurrió, la cantidad de heridos, gravedad del suceso, etc.
En este caso, contamos con 25610 observaciones, las cuales representan cada accidente, y con 11 variables las cuales están nombradas como
names(data) [1] "FECHA_ACCIDENTE" "HORA_ACCIDENTE"
[3] "GRAVEDAD_ACCIDENTE" "CLASE_ACCIDENTE"
[5] "SITIO_EXACTO_ACCIDENTE" "CANT_HERIDOS_EN _SITIO_ACCIDENTE"
[7] "CANT_MUERTOS_EN _SITIO_ACCIDENTE" "CANTIDAD_ACCIDENTES"
[9] "AÑO_ACCIDENTE" "MES_ACCIDENTE"
[11] "DIA_ACCIDENTE"
summary(data) FECHA_ACCIDENTE HORA_ACCIDENTE GRAVEDAD_ACCIDENTE CLASE_ACCIDENTE
Length:25610 Length:25610 Length:25610 Length:25610
Class :character Class :character Class :character Class :character
Mode :character Mode :character Mode :character Mode :character
SITIO_EXACTO_ACCIDENTE CANT_HERIDOS_EN _SITIO_ACCIDENTE
Length:25610 Min. : 1.000
Class :character 1st Qu.: 1.000
Mode :character Median : 1.000
Mean : 1.472
3rd Qu.: 2.000
Max. :42.000
NA's :15626
CANT_MUERTOS_EN _SITIO_ACCIDENTE CANTIDAD_ACCIDENTES AÑO_ACCIDENTE
Min. :1.000 Min. :1 Length:25610
1st Qu.:1.000 1st Qu.:1 Class :character
Median :1.000 Median :1 Mode :character
Mean :1.036 Mean :1
3rd Qu.:1.000 3rd Qu.:1
Max. :2.000 Max. :2
NA's :25358
MES_ACCIDENTE DIA_ACCIDENTE
Length:25610 Length:25610
Class :character Class :character
Mode :character Mode :character
También podemos observar que de las 11 variables, 3 de ellas, cant_heridos_en_sitio_accidente, cant_muertos_en_sitio_accidente y cantidad_accidentes, son numéricas y las 8 restantes son categóricas.
Análisis: variable según su tipo
Numéricas:
cant_heridos_en_sitio_accidente
summary(data$`CANT_HERIDOS_EN _SITIO_ACCIDENTE`)Min. 1st Qu. Median Mean 3rd Qu. Max. NA's 1.000 1.000 1.000 1.472 2.000 42.000 15626Lo que podemos mencionar sobre esta variable es que el promedio de personas heridas es aproximadamente 1 por accidente, sin embargo, se nota que hay casos en donde esto varió y hubo un alto número de personas afectadas, como es visible en la gráfica la cual cuenta con una gran cantidad de datos atípicos.
cant_muertos_en_sitio_accidente
summary(data$`CANT_MUERTOS_EN _SITIO_ACCIDENTE`)Min. 1st Qu. Median Mean 3rd Qu. Max. NA's 1.000 1.000 1.000 1.036 1.000 2.000 25358Lo que podemos mencionar sobre esta variable es que el promedio de personas muertas es 1 por accidente, sin embargo, se nota que estos valores varían entre 1 y 2 dada la existencia del dato atípico.
cantidad_accidentes
summary(data$CANTIDAD_ACCIDENTES)Min. 1st Qu. Median Mean 3rd Qu. Max. 1 1 1 1 1 2Lo que podemos mencionar sobre esta variable es que el promedio de accidentes por hora específica en cada fecha es 1, sin embargo, se nota que estos valores varían entre 1 y 2 dada la existencia del dato atípico
Categóricas:
fecha_accidente
frec_fecha<- data %>% count(data$FECHA_ACCIDENTE) %>% arrange(desc(n)) datatable(frec_fecha)Es decir, el día que hubo mayor cantidad de accidentes fue el 8 de junio del año 2018.
hora_accidente
frec_hora<- data %>% count(data$HORA_ACCIDENTE) %>% arrange(desc(n)) datatable(frec_hora)Es decir que, según los datos recolectados, los accidentes tendían a ocurrir en su mayoría en horas de la tarde, más específicamente a las 3pm y 4pm.
gravedad_accidente
Esta es una variable que cuenta con 3 posibles casos:
[1] "Con heridos" "Solo daños" "Con muertos"factor_gravedad<-as.factor(data$GRAVEDAD_ACCIDENTE) frec_gravedad<-summary(factor_gravedad) barplot(frec_gravedad,col = "lightblue", main = "Frecuencia del tipo de gravedad del accidente")A partir de este diagrama de barras, se puede observar que la mayoría de los accidentes no tuvieron consecuencias graves para las personas, ya que en su mayoría solo se reportaron daños materiales. Además, el número de fallecidos es significativamente menor en comparación con los otros dos casos:
Con heridosySolo daños.clase_accidente
Esta variable cuenta con 6 posibles casos:
[1] "Atropello" "Choque" "Caida Ocupante" "Volcamiento" [5] "Otro" "Incendio"factor_clase<-as.factor(data$CLASE_ACCIDENTE) frec_clase<-summary(factor_clase) barplot(frec_clase, las = 2, col = "lightblue", main = "Frecuencia de cada clase de accidente")A partir de esta gráfica, se puede observar claramente que la mayor cantidad de accidentes se debe, de manera significativa, al tipo
Choque. El resto de las clases de accidentes se distribuye casi de manera proporcional entre las demás categorías, considerando la frecuencia deAtropelloligeramente mayor.año_accidente
frec_año<-table(data$AÑO_ACCIDENTE) barplot(frec_año, col = "lightblue", main = "Frecuencia de año de ocurrencia del accidente")Se puede observar que desde el año
2018hasta el año actual, la tendencia de ocurrencia de accidentes ha disminuido. El año con el mayor número de accidentes registrados fue2018, mientras que2020tuvo la menor frecuencia. A su vez, siguiendo la idea del principio, se nota que a partir del2021, se mantiene un patrón de descenso continuo en la frecuencia de accidentes en la ciudad de Barranquilla.mes_accidente
frec_mes<-table(data$MES_ACCIDENTE) print(frec_mes)April August December February January July June March 2010 1918 2189 2477 2349 1932 2103 2446 May November October September 2121 1995 2090 1980barplot(frec_mes,las =2, col = "lightblue", main = "Frecuencia de mes de ocurrencia del accidente")Se puede observar que la cantidad de accidentes por mes es bastante similar, con valores cercanos a aproximadamente 2000. El mes con el mayor número de accidentes acumulados es
febrero, con2477accidentes, mientras que el mes con el menor número esagosto, con1918registros.dia_accidente
frec_dia<-table(data$DIA_ACCIDENTE) print(frec_dia)Fri Mon Sat Sun Thu Tue Wed 3920 3774 3735 2577 3756 4009 3839barplot(frec_dia,las =2, col = "lightblue", main = "Frecuencia de día de ocurrencia del accidente")En este gráfico, se puede observar que la frecuencia de accidentes es bastante parecida entre la mayoría de los días de la semana, con la excepción notable del
domingo, que tiene una frecuencia visiblemente menor. Según los registros de este estudio, el día con la mayor cantidad de accidentes es elmartes, con4009incidentes, mientras que, como ya se mencionó, eldomingotiene la menor cantidad, con2577accidentes.
Filtrado de la base de datos
Para esta sección, vamos a filtrar la base de datos con diferentes casos y, a su vez, haremos uso de la función table para explorar los datos.
Accidentes de tipo choque ocurridos en los años 2018 y 2019
Haremos un breve análisis de este subconjunto usando las variables
dia_accidente,gravedad_accidenteymes_accidente.filtro1 <- data %>% filter(CLASE_ACCIDENTE == "Choque" & AÑO_ACCIDENTE %in% c(2018, 2019))frec_diafiltro1 <- table(filtro1$DIA_ACCIDENTE) print(frec_diafiltro1)Fri Mon Sat Sun Thu Tue Wed 1680 1602 1577 1002 1626 1727 1668Al analizar este nuevo subconjunto con respecto a los días de la semana, podemos observar que el
viernessigue siendo el día con mayor cantidad de accidentes, en este caso, solo de tipoChoque.frec_gravedadfiltro1 <- table(filtro1$GRAVEDAD_ACCIDENTE) print(frec_gravedadfiltro1)Con heridos Con muertos Solo daños 2725 46 8111Durante los años
2018y2019, los accidentes clasificados comoChoqueresultaron mayoritariamente en daños materiales. Además, es importante destacar que la frecuencia deCon muertosen estos accidentes fue significativamente menor en comparación con el número deCon heridosySolo daños.frec_mesfiltro1 <- table(filtro1$MES_ACCIDENTE) print(frec_mesfiltro1)April August December February January July June March 940 919 931 874 885 856 849 930 May November October September 911 909 916 962Podemos observar que los accidentes de tipo
Choquedurante los años2018y2019se distribuyeron de manera casi uniforme a lo largo de los 12 meses. La cantidad de incidentes por mes se aproxima a los 900. En comparación con la base de datos original, el mes con la menor cantidad de choques acumulados esjunio, con849incidentes, mientras que el mes con la mayor frecuencia esseptiembre, con962incidentes.Accidentes ocurridos los días sábado y domingo en el primer trimestre de cada año sin muertos
Haremos un breve análisis de este subconjunto usando las variables
año_accidenteyclase_accidente.filtro2 <- data %>% filter(DIA_ACCIDENTE %in% c("Sat", "Sun") & MES_ACCIDENTE %in% c("January", "February", "March") & GRAVEDAD_ACCIDENTE %in% c("Con heridos", "Solo daños"))frec_añofiltro2 <- table(filtro2$AÑO_ACCIDENTE) print(frec_añofiltro2)2018 2019 2020 2021 2022 2023 2024 344 346 287 275 292 107 105En este subconjunto de datos, se puede observar un comportamiento similar al de la base de datos principal, con una ligera tendencia a la disminución de accidentes a lo largo de los años. Sin embargo, se destaca que en
2019y2022, la frecuencia de accidentes es ligeramente superior en comparación con el año anterior. El año con la mayor cantidad de accidentes, según los filtros establecidos, fue2019, con346registros, mientras que el año con la menor cantidad fue2024, con105registros.frec_clasefiltro2 <- table(filtro2$CLASE_ACCIDENTE) print(frec_clasefiltro2)Atropello Caida Ocupante Choque Incendio Otro 107 13 1619 2 8 Volcamiento 7Para este resultado, se puede mencionar que la cantidad de accidentes de tipo
Choque, según los filtros establecidos, es significativamente mayor en comparación con las demás clases de accidente. La cantidad de accidentes de esta clase es de1619, mientras que la clase con la menor frecuencia esIncendio, con solo2registros. Este subconjunto, analizado desde la variable declase_accidente, refleja el mismo comportamiento que la base de datos original, ya que la distribución de los accidentes en otras clases es relativamente igual, siendo la frecuencia deAtropelloligeramente superior.Accidentes ocurridos entre 2019 y 2021 en febrero
Haremos un breve análisis de este subconjunto usando las variables
dia_accidente,gravedad_accidenteyclase_accidente.filtro3 <- data %>% filter(AÑO_ACCIDENTE %in% c(2019, 2020, 2021) & MES_ACCIDENTE == "February")frec_diafiltro3 <- table(filtro3$DIA_ACCIDENTE) print(frec_diafiltro3)Fri Mon Sat Sun Thu Tue Wed 196 203 213 118 182 179 179Durante el mes de
febreroen los años2019,2020y2021, se puede afirmar que el día con la mayor cantidad de accidentes fue elsábado, con213incidentes. Por otro lado, eldomingoregistró la menor cantidad de accidentes, con118registros, siendo este el único valor significativamente distante de los demás días, que se mantienen cerca de los 200 registros.frec_gravedadfiltro3 <- table(filtro3$GRAVEDAD_ACCIDENTE) print(frec_gravedadfiltro3)Con heridos Con muertos Solo daños 372 4 894Se puede decir que la mayor parte de los accidentes, según el filtro establecido, resultó en daños materiales, ya que la categoría
Solo dañoses la que cuenta con la mayor cantidad de incidentes, alcanzando894registros. Esto es significativamente más alto en comparación con las categoríasCon heridosyCon muertos.frec_clasefiltro3 <- table(filtro3$CLASE_ACCIDENTE) print(frec_clasefiltro3)Atropello Caida Ocupante Choque Otro Volcamiento 52 4 1205 4 5Este subconjunto, al igual que los anteriormente analizados, muestra una alta frecuencia en la clase de accidente
Choque, que es significativamente mayor en comparación con las demás clases. Además, se observa que, nuevamente, la categoríaAtropellotiene una frecuencia ligeramente superior a las otras, mientras que las demás clases de accidente presentan una distribución de datos más uniforme.Accidentes de tipo atropello ocurridos en octubre y diciembre ocurridos a las 6 am y 6 pm
Haremos un breve análisis de este subconjunto usando las variables
dia_accidenteygravedad_accidente.filtro4 <- data %>% filter(data$CLASE_ACCIDENTE == "Atropello" & data$MES_ACCIDENTE == c("October", "December") & data$HORA_ACCIDENTE == c("06:00:00:am", "06:00:00:pm"))frec_diafiltro4 <- table(filtro4$DIA_ACCIDENTE) print(frec_diafiltro4)Thu Wed 1 1En este caso, se puede observar que solo 2 registros cumplen con los filtros establecidos. De estos, uno ocurrió un
martesy el otro unjueves.frec_gravedadfiltro4 <- table(filtro4$GRAVEDAD_ACCIDENTE) print(frec_gravedadfiltro4)Con heridos 2En cuanto a la gravedad de los accidentes, ambos registros resultaron en la categoría
Con heridos, lo que explica que su frecuencia sea de2.Accidentes ocurridos a las 3, 4 y 5, de la mañana y tarde, en los años 2022 y 2023
Haremos un breve análisis de este subconjunto usando las variables
mes_accidente,gravedad_accidenteyclase_accidente.filtro5 <- data %>% filter(HORA_ACCIDENTE %in% c("03:00:00:am", "04:00:00:am", "05:00:00:am", "03:00:00:pm", "04:00:00:pm", "05:00:00:pm") & AÑO_ACCIDENTE %in% c(2022, 2023))filtro5_mañana <- filtro5 %>% filter(HORA_ACCIDENTE %in% c("03:00:00:am", "04:00:00:am", "05:00:00:am")) filtro5_tarde <- filtro5 %>% filter(HORA_ACCIDENTE %in% c("03:00:00:pm", "04:00:00:pm", "05:00:00:pm"))frec_mesf5mañana <- table(filtro5_mañana$MES_ACCIDENTE) print(frec_mesf5mañana)April August December February January July June March 6 5 4 4 1 4 7 5 May November September 2 4 2frec_mesf5tarde <- table(filtro5_tarde$MES_ACCIDENTE) print(frec_mesf5tarde)April August December February January July June March 31 14 8 30 24 27 28 37 May November October September 19 8 6 7Podemos observar una clara diferencia en la cantidad de accidentes entre la mañana y la tarde por mes en los años estudiados. A partir de estos datos, se puede afirmar que, al comparar las horas seleccionadas, era más probable que ocurrieran accidentes durante la tarde que en la mañana. Esto se evidencia en que la frecuencia más alta en el grupo de horas de la
mañanaes de7accidentes, mientras que en latardealcanza los37.frec_gravedadf5mañana <- table(filtro5_mañana$GRAVEDAD_ACCIDENTE) print(frec_gravedadf5mañana)Con heridos Con muertos Solo daños 26 6 12frec_gravedadf5tarde <- table(filtro5_tarde$GRAVEDAD_ACCIDENTE) print(frec_gravedadf5tarde)Con heridos Con muertos Solo daños 114 5 120Nuevamente, se observa una gran diferencia en las frecuencias de los tipos de consecuencias, excluyendo la categoría
Con muertos, ya que para los dos grupos son prácticamente iguales. Según los resultados, se puede inferir que la cantidad de accidentes es mayor en la tarde dentro del grupo de horas escogidas, dado que la cantidad de registros por categoría de gravedad es significativamente mayor en la tarde comparado con la mañana.frec_clasef5mañana <- table(filtro5_mañana$CLASE_ACCIDENTE) print(frec_clasef5mañana)Atropello Choque Otro Volcamiento 3 38 1 2frec_clasef5tarde <- table(filtro5_tarde$CLASE_ACCIDENTE) print(frec_clasef5tarde)Atropello Caida Ocupante Choque Otro 16 1 221 1Para ambos grupos, los accidentes de tipo
Choquefueron los más frecuentes. Sin embargo, al igual que en los análisis previos, la frecuencia es significativamente mayor en el grupo de horas de la tarde.frec_cantidadf5mañana <- table(filtro5_mañana$CANTIDAD_ACCIDENTES) print(frec_cantidadf5mañana)1 44frec_cantidadf5tarde <- table(filtro5_tarde$CANTIDAD_ACCIDENTES) print(frec_cantidadf5tarde)1 239Finalmente, podemos corroborar que en efecto la cantidad de accidentes del grupo de la tarde es significativamente mayor al de la mañana.
Identificación de valores NA
Para identificar la existencia de los datos Not Available en la base de datos, vamos a contarlos y graficarlos.
Primeramente, calculemos cuántos hay por columna.
cantidad_na <- sapply(data, function(x) sum(is.na(x)))
for (columna in names(cantidad_na)) {
cat(columna, ":", cantidad_na[[columna]], "NA's \n") }FECHA_ACCIDENTE : 0 NA's
HORA_ACCIDENTE : 0 NA's
GRAVEDAD_ACCIDENTE : 0 NA's
CLASE_ACCIDENTE : 0 NA's
SITIO_EXACTO_ACCIDENTE : 0 NA's
CANT_HERIDOS_EN _SITIO_ACCIDENTE : 15626 NA's
CANT_MUERTOS_EN _SITIO_ACCIDENTE : 25358 NA's
CANTIDAD_ACCIDENTES : 0 NA's
AÑO_ACCIDENTE : 0 NA's
MES_ACCIDENTE : 0 NA's
DIA_ACCIDENTE : 0 NA's
Es decir, que de las 11 variables, solo 2, cant_heridos_en_sitio_accidente y cant_muertos_en_sitio_accidente, cuentan con datos faltantes. Nótese que, para la última mencionada, de las 25610 observaciones, 25318 son NA.
Ahora, veamos esto gráficamente.
gg_miss_var(data, show_pct = TRUE)Para este gráfico, haremos un renombre temporal de las variables por cuestiones de estética.
data1 <- rename(data, fecha = FECHA_ACCIDENTE, hora = HORA_ACCIDENTE, grave = GRAVEDAD_ACCIDENTE, clase = CLASE_ACCIDENTE, sitio = SITIO_EXACTO_ACCIDENTE, hurt = `CANT_HERIDOS_EN _SITIO_ACCIDENTE`, dead = `CANT_MUERTOS_EN _SITIO_ACCIDENTE`, cantidad = CANTIDAD_ACCIDENTES, año = AÑO_ACCIDENTE, mes = MES_ACCIDENTE, dia = DIA_ACCIDENTE)md.pattern(data1) fecha hora grave clase sitio cantidad año mes dia hurt dead
83 1 1 1 1 1 1 1 1 1 1 1 0
9901 1 1 1 1 1 1 1 1 1 1 0 1
169 1 1 1 1 1 1 1 1 1 0 1 1
15457 1 1 1 1 1 1 1 1 1 0 0 2
0 0 0 0 0 0 0 0 0 15626 25358 40984
En la primera gráfica, se muestra el porcentaje de datos que faltan en cada variable. Por ejemplo, alrededor del 55% de los datos en la variable cant_heridos_en_sitio_accidente son faltantes, y casi el 100% de los datos en cant_muertos_en_sitio_accidente también son NA.
La segunda gráfica confirma esta información, mostrando cuántos datos faltan en total por cada columna, lo que ayuda a entender mejor cuáles son las variables con más datos faltantes, tal como se calculó anteriormente.
Ahora, debido a que el porcentaje de NA’s de la variable cant_muertos_en_sitio_accidente es notablemente significativo, se puede borrar la columna.
data2 <- data[, -which(names(data) == "CANT_MUERTOS_EN _SITIO_ACCIDENTE")]
datatable(
data2[1:10, ], caption = "Base de datos: Accidentalidad en Barranquilla", options =list( scrollX = TRUE, scrollY = "450px", paging=FALSE ) )Por otra parte, cant_heridos_en_sitio_accidente puede considerarse una variable de tipo factor debido a que sus valores no son continuos, por lo tanto, realizar una imputación sobre ella no es realmente necesario. Esto se debe a que los NA’s en esta variable reflejan adecuadamente la información sobre las consecuencias del accidente. De hecho, observamos que los NA’s aparecen cuando la variable gravedad_accidente toma los valores Con muertos o Solo daños, lo que indica que la ausencia de datos en cant_heridos_en_sitio_accidente va de la mano con gravedad_accidente.
Detección de datos atípicos
Dado que las variables numéricas en este conjunto de datos pueden ser vistas y trabajadas como factores, la detección de valores atípicos no es la estrategia más efectiva. En vez de buscar anomalías, se optará por calcular tablas de frecuencias para analizar cómo se distribuyen las diferentes categorías. Este método proporcionará una visión clara de la distribución de los valores en estas variables discretas, facilitando un análisis más pertinente y ajustado a la naturaleza de los datos.
cant_heridos_en_sitio_accidente
frec_heridos <- data2 %>% count(data2$`CANT_HERIDOS_EN _SITIO_ACCIDENTE`) %>% arrange(desc(n)) datatable(frec_heridos)Es evidente que hay una gran diferencia entre las frecuencias de los distintos valores que toma
cant_heridos_en_sitio_accidente.cantidad_accidentes
frec_accidentes <- data2 %>% count(data2$CANTIDAD_ACCIDENTES) %>% arrange(desc(n)) datatable(frec_accidentes)Es muy notable la significante diferencia entre los 2 valores que toma la variable
cantidad_accidentes.
Punto 2
Para este ejercicio, hay que, en primer lugar, unir todas las bases de datos para convertirlas en una sola.
#AÑO2023
d1<-read_csv("C:/Users/valcd/Downloads/precios_t12023.csv")Rows: 66700 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (6): BANDERA, NOMBRE COMERCIAL, PRODUCTO, FECHA REGISTRO, DEPARTAMENTO, ...
dbl (1): VALOR PRECIO
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
d2<-read_csv("C:/Users/valcd/Downloads/precios_t22023.csv")Rows: 74112 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (6): BANDERA, NOMBRE COMERCIAL, PRODUCTO, FECHA REGISTRO, DEPARTAMENTO, ...
dbl (1): VALOR PRECIO
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
d3<-read_csv("C:/Users/valcd/Downloads/precios_t32023.csv")Rows: 62339 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (6): BANDERA, NOMBRE COMERCIAL, PRODUCTO, FECHA REGISTRO, DEPARTAMENTO, ...
dbl (1): VALOR PRECIO
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
d4<-read_csv("C:/Users/valcd/Downloads/precios_t42023.csv")Rows: 64792 Columns: 7
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (6): BANDERA, NOMBRE COMERCIAL, PRODUCTO, FECHA REGISTRO, DEPARTAMENTO, ...
dbl (1): VALOR PRECIO
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Base de datos
data23 <- bind_rows(d1,d2,d3,d4)
datatable(
data23[1:10, ],
caption = "Base de datos: Combustibles en Colombia",
options = list(
scrollX = TRUE,
scrollY = "450px",
paging=FALSE
)
)Análisis: características de la base de datos
Esta base de datos, proporcionada por la página oficial de SICOM (sistema de información de la cadena de distribución de combustibles del Ministerio de Minas y Energía), brinda información relacionada con los precios de los combustibles distribuidos alrededor de Colombia durante el año 2023. Cada una de las columnas, ofrece información específica, como por ejemplo la empresa, el tipo de combustible y el precio de este.
En este caso, contamos con 267943 observaciones y con 7 variables las cuales están nombradas como
names(data23)[1] "BANDERA" "NOMBRE COMERCIAL" "PRODUCTO" "FECHA REGISTRO"
[5] "DEPARTAMENTO" "MUNICIPIO" "VALOR PRECIO"
summary(data23) BANDERA NOMBRE COMERCIAL PRODUCTO FECHA REGISTRO
Length:267943 Length:267943 Length:267943 Length:267943
Class :character Class :character Class :character Class :character
Mode :character Mode :character Mode :character Mode :character
DEPARTAMENTO MUNICIPIO VALOR PRECIO
Length:267943 Length:267943 Min. : 0
Class :character Class :character 1st Qu.: 9350
Mode :character Mode :character Median : 10880
Mean : 12023
3rd Qu.: 13849
Max. :14750147
A su vez, podemos observar que de las 7 variables, solo 1, valor precio, es de tipo numérica.
Análisis: variable según tipo
Numéricas:
valor precio
summary(data23$`VALOR PRECIO`)Min. 1st Qu. Median Mean 3rd Qu. Max. 0 9350 10880 12023 13849 14750147boxplot(data23$`VALOR PRECIO`,main="Precio del combustible")La variable muestra la presencia de valores extremos significativamente alejados de la mayoría de los datos. Según el análisis realizado, los precios del combustible en cada ubicación se concentran principalmente en el rango de 9000 a 14000 pesos, aproximadamente. En consecuencia, el valor extremo superior observado,
14750147, se destaca claramente como un dato atípico en comparación con el resto de los precios registrados.
Categóricas:
bandera
unique(data23$BANDERA)[1] "TERPEL" "TEXACO" "PRIMAX" [4] "ZEUSS " "BIOMAX" "PETROMIL " [7] "ESSO" "PETROBRAS" "BRIO " [10] "PUMA" "PLUS MAS" "ECOS" [13] "OCTANO" "AYATAWACOOP" "DISCOWACOOP" [16] "COOMULPINORT" "DISCOM" "P Y B" [19] "PETRODECOL" "PETRDECOL" "SAVE" [22] "PROXXON" "ZAPATA Y VELASQUEZ "frec_bandera<- data23 %>% count(data23$BANDERA) %>% arrange(desc(n)) datatable(frec_bandera)Es decir que, entre todas las comercializadoras,
Terpeles la que más distribuye los diferentes tipos de combustible en el país.producto
factor_producto<-as.factor(data23$PRODUCTO) frec_producto <- summary(factor_producto)barplot(frec_producto,col = "lightblue", main = "Frecuencia del tipo de combustible")Se observa que la distribución del combustible tipo
Extraes considerablemente inferior en comparación con los otros dos tipos. Además, es relevante mencionar que las frecuencias deDieselyGasolina Motorno difieren significativamente entre sí y que, entre ambos, el combustible más comercializado en Colombia durante el año2023fue laGasolina Motor, con un total de127338registros.departamento
frec_departamento<- data23 %>% count(data23$DEPARTAMENTO) %>% arrange(desc(n)) datatable(frec_departamento)Por lo tanto, podemos afirmar que el departamento en donde más se comercializó los 3 tipos de combustible durante el
2023fue enNariño, con31054registros.
Identificación de valores NA
Primeramente, calculemos de manera matemática la cantidad de datos faltantes.
cantidad_na_23 <- sapply(data23, function(x) sum(is.na(x)))
for (columna in names(cantidad_na_23)) {
cat(columna, ":", cantidad_na_23[[columna]], "NA's \n")
}BANDERA : 0 NA's
NOMBRE COMERCIAL : 0 NA's
PRODUCTO : 0 NA's
FECHA REGISTRO : 0 NA's
DEPARTAMENTO : 0 NA's
MUNICIPIO : 0 NA's
VALOR PRECIO : 0 NA's
Es decir que la base de datos no contiene ningún dato de tipo NA.
Detección de datos atípicos
Hagamos uso de la herramienta boxplot para identificar la existencia de outliers.
ggplot(data23, aes(x = PRODUCTO, y = `VALOR PRECIO`)) +
geom_boxplot() +
scale_y_continuous(limits = c(0, 30000), labels = scales::comma) +
labs(x = "Tipo de Producto", y = "Valor Precio", title = "Boxplot de Precio por Tipo de Producto") +
theme_classic()Warning: Removed 33 rows containing non-finite outside the scale range
(`stat_boxplot()`).
A partir de esto, observamos que hay una evidente existencia de datos atípicos en la variable valor precio. Por tanto, se procederá a realizar la imputación de estos.
Verifiquemos la normalidad de la variable
ks.test(data23$`VALOR PRECIO`, "pnorm", mean = mean(data23$`VALOR PRECIO`, na.rm = TRUE), sd = sd(data23$`VALOR PRECIO`, na.rm = TRUE))Warning in ks.test.default(data23$`VALOR PRECIO`, "pnorm", mean =
mean(data23$`VALOR PRECIO`, : ties should not be present for the one-sample
Kolmogorov-Smirnov test
Asymptotic one-sample Kolmogorov-Smirnov test
data: data23$`VALOR PRECIO`
D = 0.42971, p-value < 2.2e-16
alternative hypothesis: two-sided
Al hacer la prueba de Kolmogorov-Smirnov, se obtuvo un \(p\) valor inferior a 0.05, esto indica que los datos no siguen una distribución normal. Es decir, la imputación debe realizarse haciendo uso de la mediana.
Q1 <- quantile(data23$`VALOR PRECIO`, 0.25, na.rm = TRUE)
Q3 <- quantile(data23$`VALOR PRECIO`, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lim_inf <- Q1 - 1.5 * IQR
lim_sup <- Q3 + 1.5 * IQR
mediana <- median(data23$`VALOR PRECIO`, na.rm = TRUE)
data23$`VALOR PRECIO` <- ifelse(data23$`VALOR PRECIO` < lim_inf | data23$`VALOR PRECIO` > lim_sup, mediana, data23$`VALOR PRECIO`)Ahora, observemos que en efecto se realizaron cambios en data23.
summary(data23$`VALOR PRECIO`) Min. 1st Qu. Median Mean 3rd Qu. Max.
3500 9350 10880 11856 13750 20597
Filtrado de la base de datos
Para este ejercicio, vamos a filtrar la base según un solo tipo de combustible: Gasolina Motor y luego procederemos a analizarla con respecto a las diferentes columnas.
data23_1 <- data23 %>% filter(data23$PRODUCTO == "GASOLINA MOTOR")
datatable(
data23_1[1:10, ],
caption = "Base de datos: Combustibles de Gasolina Motor",
options = list(
scrollX = TRUE,
scrollY = "450px",
paging=FALSE
)
)Debido a que existen varios tipos de valores en BANDERA, vamos a analizarlo con los 4 de mayor frecuencia encontrados con la base original, con el fin de observar si hubo algún cambio en el comportimiento. Estos son Terpel, Primax, Biomax y Texaco.
empresa <- data23_1 %>% filter(data23_1$BANDERA %in% c("TERPEL", "PRIMAX", "BIOMAX", "TEXACO"))
frec_empresa <- table(empresa$BANDERA, empresa$PRODUCTO)
barplot(frec_empresa,
beside = TRUE, # Agrupa las barras
col = c("lightblue", "lightgreen", "lightcoral", "lightpink"),
legend.text = TRUE,
main = "Frecuencia de empresas por Gasolina Motor",
xlab = "Empresa",
ylab = "Frecuencia",
las = 1)Es posible observar que, una vez más, la bandera Terpel es la que más comercializó combustible, en este caso de tipo Gasolina Motor, mientras que la que menos lo hizo fue Texaco. Por otro lado, se evidencia un cambio en el comportamiento entre Biomax y Primax pues para este producto, Biomax presenta una mayor frecuencia de comercialización. Sin embargo, al analizar los tres tipos de combustibles conjuntamente, Primax es la que se distribuyó con mayor frecuencia.
Realizaremos esto mismo con los 4 valores de DEPARTAMENTO con mayor frecuencia.
departamento <- data23_1 %>% filter(data23_1$DEPARTAMENTO %in% c("NARIÑO", "ANTIOQUIA", "NORTE DE SANTANDER", "VALLE DEL CAUCA"))
frec_dpto <- table(departamento$DEPARTAMENTO, departamento$PRODUCTO)
barplot(frec_dpto,
beside = TRUE, # Agrupa las barras
col = c("lightblue", "lightgreen", "lightcoral", "lightpink"),
legend.text = TRUE,
main = "Frecuencia de departamentos por Gasolina Motor",
xlab = "Empresa",
ylab = "Frecuencia",
las = 1)Observamos que este subconjunto tiene el mismo comportamiento de la base de datos original para estos 4 departamentos.
Ahora, veamos cómo se comporta el precio de este tipo de combustible por departamento.
result <- data23_1 %>%
group_by(DEPARTAMENTO) %>%
summarise(precio_promedio = mean(`VALOR PRECIO`, na.rm = TRUE)) %>%
arrange((precio_promedio))
datatable(result)Creemos un rango para definir el nivel en el que se encuentra el promedio, esto servirá más adelante para realizar el mapa.
rangos <- result %>%
mutate(rango_precio = cut(precio_promedio,
breaks = c(10000, 12000, 14000, 16000),
labels = c("Bajo", "Medio", "Alto"),
right = FALSE)) %>%
arrange(rango_precio)
datatable(rangos)Se puede concluir que la mayoría de departamentos se encuentra en el rango establecido Medio, pues de los 32, solo NARIÑO y CESAR son de tipo BAJO, y en el caso de tipo ALTO solo encontramos a SAN ANDRES, GUAVIARE, VAUPES, GUAINIA y AMAZONAS.