# ==================================================================================
# PROYECTO FINAL: ANÁLISIS ESTADÍSTICO DE ACCIDENT LONGITUDE (15°)
# ==================================================================================
# 1. CARGA DE LIBRERÍAS
library(readxl)
library(ggplot2)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(gt)
library(janitor)
##
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
# 2. IMPORTACIÓN Y PROCESAMIENTO DE DATOS
database <- read_excel("database.xlsx")
## Warning: Expecting numeric in C2189 / R2189C3: got 'Accident Year'
## Warning: Expecting numeric in C2215 / R2215C3: got 'Accident Year'
nombre_var <- "Accident Longitude"
# CAMBIO A INTERVALOS DE 15 GRADOS
ANCHO_CLASE <- 15.0
datos_raw <- as.numeric(as.character(database[[nombre_var]])) %>% na.omit()
LIMITE_MIN <- floor(min(datos_raw))
LIMITE_MAX <- ceiling(max(datos_raw))
variable_filtrada <- datos_raw[datos_raw >= LIMITE_MIN & datos_raw <= LIMITE_MAX]
n_total <- length(variable_filtrada)
# 3. TABLA DE FRECUENCIAS (ni, hi, Ni asc, Ni desc)
cortes <- seq(LIMITE_MIN, LIMITE_MAX + ANCHO_CLASE, by = ANCHO_CLASE)
tabla_resumen <- data.frame(
Intervalo = cut(variable_filtrada, breaks = cortes, include.lowest = TRUE, right = FALSE)
) %>%
count(Intervalo, name = "ni") %>%
mutate(
hi = ni / sum(ni),
Ni_asc = cumsum(ni),
Ni_desc = n_total - Ni_asc + ni
)
# 4. VISUALIZACIÓN DE LA TABLA PROFESIONAL
tabla_resumen %>%
adorn_totals("row", name = "TOTAL") %>%
gt() %>%
fmt_number(columns = hi, decimals = 4) %>%
tab_header(title = "Distribución de Frecuencias (15°)",
subtitle = paste("Variable: Accident Longitude | n =", n_total))
| Distribución de Frecuencias (15°) |
| Variable: Accident Longitude | n = 2795 |
| Intervalo |
ni |
hi |
Ni_asc |
Ni_desc |
| [-159,-144) |
14 |
0.0050 |
14 |
2795 |
| [-129,-114) |
170 |
0.0608 |
184 |
2781 |
| [-114,-99) |
576 |
0.2061 |
760 |
2611 |
| [-99,-84) |
1735 |
0.6208 |
2495 |
2035 |
| [-84,-69) |
295 |
0.1055 |
2790 |
300 |
| [-69,-54) |
1 |
0.0004 |
2791 |
5 |
| [-24,-9) |
1 |
0.0004 |
2792 |
4 |
| [81,96) |
1 |
0.0004 |
2793 |
3 |
| [96,111] |
2 |
0.0007 |
2795 |
2 |
| TOTAL |
2795 |
1.0000 |
17414 |
10536 |
# 5. HISTOGRAMA DE FRECUENCIAS (INTERVALOS DE 15°)
ggplot(tabla_resumen, aes(x = Intervalo, y = ni)) +
geom_col(fill = "#2C3E50", color = "white", alpha = 0.9) +
geom_text(aes(label = ni), vjust = -0.5, fontface = "bold", size = 4) +
theme_minimal() +
labs(title = "Frecuencia de Accidentes por Longitud (Intervalos de 15°)",
x = "Rango Longitudinal (Grados)",
y = "Número de Accidentes") +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
axis.text.x = element_text(face = "bold", size = 10))

# 6. CRUCE DE OJIVAS
puntos_x <- seq(LIMITE_MIN, LIMITE_MIN + (nrow(tabla_resumen) * ANCHO_CLASE), by = ANCHO_CLASE)
df_ojiva <- data.frame(
X = rep(puntos_x, 2),
Y = c(c(0, tabla_resumen$Ni_asc), c(n_total, tabla_resumen$Ni_desc[-1], 0)),
Tipo = rep(c("Ascendente", "Descendente"), each = length(puntos_x))
)
ggplot(df_ojiva, aes(x = X, y = Y, color = Tipo, group = Tipo)) +
geom_line(linewidth = 1.2) + geom_point(shape = 16, size = 3) +
scale_color_manual(values = c("#27AE60", "#E74C3C")) +
theme_minimal() + labs(title = "Análisis de Frecuencia Acumulada: Longitude", y = "Total Acumulado")

# 7. BOXPLOT DE DISPERSIÓN (CORREGIDO PARA LONGITUD)
ggplot(data.frame(v = variable_filtrada), aes(x = "", y = v)) +
geom_boxplot(fill = "#AED6F1", color = "#1B4F72", outlier.color = "red", outlier.shape = 16) +
coord_flip() + theme_bw() +
labs(title = "Dispersión Geográfica: Accident Longitude", y = "Grados de Longitud", x = "Distribución")

# 8. CONCLUSIÓN GENERAL (Texto Unificado)
cat("\nCONCLUSIÓN GENERAL: El estudio estadístico de la variable Accident Longitude revela que la siniestralidad no se distribuye de manera uniforme, sino que presenta concentraciones críticas en rangos longitudinales específicos identificados mediante el histograma de frecuencias. A través del cruce de ojivas ascendentes y descendentes, se localizó el punto de equilibrio donde se acumula el 50% de los incidentes, proporcionando una métrica de centralidad espacial que, sumada a la identificación de valores atípicos (outliers) en el boxplot de dispersión, permite diagnosticar las zonas de mayor riesgo en el eje Este-Oeste del territorio. Este análisis, respaldado por una frecuencia relativa (hi) que suma exactamente 1.0000, garantiza la integridad del procesamiento de los 2,795 registros y ofrece una base científica sólida para la planificación de medidas de seguridad vial.\n")
##
## CONCLUSIÓN GENERAL: El estudio estadístico de la variable Accident Longitude revela que la siniestralidad no se distribuye de manera uniforme, sino que presenta concentraciones críticas en rangos longitudinales específicos identificados mediante el histograma de frecuencias. A través del cruce de ojivas ascendentes y descendentes, se localizó el punto de equilibrio donde se acumula el 50% de los incidentes, proporcionando una métrica de centralidad espacial que, sumada a la identificación de valores atípicos (outliers) en el boxplot de dispersión, permite diagnosticar las zonas de mayor riesgo en el eje Este-Oeste del territorio. Este análisis, respaldado por una frecuencia relativa (hi) que suma exactamente 1.0000, garantiza la integridad del procesamiento de los 2,795 registros y ofrece una base científica sólida para la planificación de medidas de seguridad vial.