Este documento hace parte del trabajo del curso de Analítica Predictiva de la Universidad Nacional de Colombia para la Maestría en Ingeniería y Especialización en Analítica. El alcance de este es la creación de modelos predictivos para la estimación de cantidad de accidentes en el valle del aburra a nivel semanal.
# Manipulación de datos
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(tidyr)
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
# Data modelling
library(recipes)
##
## Attaching package: 'recipes'
## The following object is masked from 'package:stats':
##
## step
library(Metrics)
library(glmnet)
## Loading required package: Matrix
##
## Attaching package: 'Matrix'
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
## Loaded glmnet 4.0-2
library(caret)
## Loading required package: lattice
## Loading required package: ggplot2
##
## Attaching package: 'caret'
## The following objects are masked from 'package:Metrics':
##
## precision, recall
library(MASS)
##
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
##
## select
library(rpart)
library(rpart.plot)
library(e1071)
# Visualización
library(ggplot2)
# Formato tablas
library(kableExtra)
##
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
##
## group_rows
df_train <- read.csv('../../data/processed/train_data.csv')
df_test <- read.csv('../../data/processed/test_data.csv')
special_dates_train <- read.csv('../../data/processed/special_date_monthly_2017.csv')
special_dates_test <- read.csv('../../data/processed/special_date_monthly_2018.csv')
kable(head(df_train)) %>%
kable_styling(bootstrap_options = "striped", full_width = F) %>%
scroll_box(width = "100%")
| X | DIA | PERIODO | CLASE | DIRECCION | DIRECCION_ENC | CBML | TIPO_GEOCOD | GRAVEDAD | BARRIO | COMUNA | DISENO | DIA_NOMBRE | MES | LONGITUD | LATITUD | FECHA |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 2014 | choque | cr 63 cl 94 | cr 063 094 000 00000 | no ubicada | solo danos | tramo de via | miercoles | 1 | -75.70382 | 6.221806 | 2014-01-01 19:00:00 | |||
| 2 | 1 | 2014 | choque | cl 30 cr 66 b | cl 030 066 b 000 00000 | 1602 | malla vial | solo danos | rosales | belen | interseccion | miercoles | 1 | -75.58727 | 6.231716 | 2014-01-01 07:40:00 |
| 3 | 1 | 2014 | choque | cr 52 cl 97 | cr 052 097 000 00000 | 0402 | malla vial | solo danos | san isidro | aranjuez | interseccion | miercoles | 1 | -75.56253 | 6.289907 | 2014-01-01 05:30:00 |
| 4 | 1 | 2014 | choque | tv 78 cl 65 | tv 078 065 000 00000 | 0519 | malla vial | solo danos | el progreso | castilla | tramo de via | miercoles | 1 | -75.57365 | 6.275473 | 2014-01-01 13:50:00 |
| 5 | 1 | 2014 | otro | cr 63 cl 50 | cr 063 050 000 00000 | 1101 | malla vial | solo danos | carlos e. restrepo | laureles estadio | tramo de via | miercoles | 1 | -75.57697 | 6.255457 | 2014-01-01 07:25:00 |
| 6 | 1 | 2014 | choque | cr 57 cl 51 | cr 057 051 000 00000 | 1006 | malla vial | solo danos | san benito | la candelaria | tramo de via | miercoles | 1 | -75.57481 | 6.254322 | 2014-01-01 04:15:00 |
Debido a que se deben armar ciertas agregaciones a nivel de conjunto de datos antes de crear los datasets de entrenamiento, se incluye una etapa de armado del conjunto de variables, no está asociado directamente con la limpieza de los datos. La base que se construye a continuación serán los cimientos para el modelo semanal.
df <- rbind(
df_train,
df_test
)
special_dates_train <- special_dates_train %>%
dplyr::select(PERIODO, MES, DIA, DIA_FESTIVO, FECHA_ESPECIAL, FESTIVO_FECHA_ESPECIAL)
special_dates_test <- special_dates_test %>%
dplyr::select(PERIODO, MES, DIA, DIA_FESTIVO, FECHA_ESPECIAL, FESTIVO_FECHA_ESPECIAL)
special_dates <- rbind(
special_dates_train,
special_dates_test
)
df <- merge(df, special_dates, by = c("PERIODO", "MES", "DIA"))
kable(head(df)) %>%
kable_styling(bootstrap_options = "striped", full_width = F) %>%
scroll_box(width = "100%")
| PERIODO | MES | DIA | X | CLASE | DIRECCION | DIRECCION_ENC | CBML | TIPO_GEOCOD | GRAVEDAD | BARRIO | COMUNA | DISENO | DIA_NOMBRE | LONGITUD | LATITUD | FECHA | DIA_FESTIVO | FECHA_ESPECIAL | FESTIVO_FECHA_ESPECIAL |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2014 | 1 | 1 | 1 | choque | cr 63 cl 94 | cr 063 094 000 00000 | no ubicada | solo danos | tramo de via | miercoles | -75.70382 | 6.221806 | 2014-01-01 19:00:00 | 1 | 1 | 1 | |||
| 2014 | 1 | 1 | 2 | choque | cl 30 cr 66 b | cl 030 066 b 000 00000 | 1602 | malla vial | solo danos | rosales | belen | interseccion | miercoles | -75.58727 | 6.231716 | 2014-01-01 07:40:00 | 1 | 1 | 1 |
| 2014 | 1 | 1 | 3 | choque | cr 52 cl 97 | cr 052 097 000 00000 | 0402 | malla vial | solo danos | san isidro | aranjuez | interseccion | miercoles | -75.56253 | 6.289907 | 2014-01-01 05:30:00 | 1 | 1 | 1 |
| 2014 | 1 | 1 | 4 | choque | tv 78 cl 65 | tv 078 065 000 00000 | 0519 | malla vial | solo danos | el progreso | castilla | tramo de via | miercoles | -75.57365 | 6.275473 | 2014-01-01 13:50:00 | 1 | 1 | 1 |
| 2014 | 1 | 1 | 5 | otro | cr 63 cl 50 | cr 063 050 000 00000 | 1101 | malla vial | solo danos | carlos e. restrepo | laureles estadio | tramo de via | miercoles | -75.57697 | 6.255457 | 2014-01-01 07:25:00 | 1 | 1 | 1 |
| 2014 | 1 | 1 | 6 | choque | cr 57 cl 51 | cr 057 051 000 00000 | 1006 | malla vial | solo danos | san benito | la candelaria | tramo de via | miercoles | -75.57481 | 6.254322 | 2014-01-01 04:15:00 | 1 | 1 | 1 |
Para el preprocesamiento de datos, se tiene en cuenta variables como rezagos de accidentes de la última semana de todas las clases y gravedades y las últimas 2 y 3 semanas de la clase de interes, al igual que accidentes en días como domingos y número de días festivos y/o especiales en la semana de interes.
df <- df %>%
mutate(SEMANA=week(FECHA))
special_dates <- special_dates %>%
mutate(
FECHA = as_date(paste(
PERIODO,
formatC(MES, width = 2, flag = 0),
formatC(DIA, width = 2, flag = 0),
sep='-'
))
) %>%
mutate(
SEMANA=week(FECHA)
)
armado_dataset_semanal <- function(df, objective_var){
# conjunto de datos semanal por clase
accidentes_por_clase <- df %>%
group_by(PERIODO, SEMANA, CLASE) %>%
summarise(n_accidentes=n(), .groups="drop") %>%
spread(CLASE, n_accidentes) %>%
mutate(PERIODO_LEAD=lead(PERIODO), SEMANA_LEAD=lead(SEMANA), rank = 1:length(PERIODO)) %>%
filter(rank < max(rank)) %>%
dplyr::select(-c(PERIODO, SEMANA, rank))
# conjunto de datos semanal por gravedad
accidentes_por_gravedad <- df %>%
group_by(PERIODO, SEMANA, GRAVEDAD) %>%
summarise(n_accidentes=n(), .groups="drop") %>%
spread(GRAVEDAD, n_accidentes) %>%
mutate(PERIODO_LEAD=lead(PERIODO), SEMANA_LEAD=lead(SEMANA), rank = 1:length(PERIODO)) %>%
filter(rank < max(rank)) %>%
dplyr::select(-c(PERIODO, SEMANA, rank))
# rezagos de la clase
rezagos_accidentes <- df %>%
filter(CLASE == objective_var) %>%
group_by(PERIODO, SEMANA) %>%
summarise(n_accidentes=n(), .groups="drop") %>%
mutate(
t_minus_1=lag(n_accidentes, n = 1),
t_minus_2=lag(n_accidentes, n = 2),
t_minus_3=lag(n_accidentes, n = 3)
) %>%
dplyr::select(PERIODO, SEMANA, t_minus_2, t_minus_3)
# Accidentes en los domingos
domingos <- df %>%
mutate(domingo = ifelse(DIA_NOMBRE == "domingo ", 1, 0)) %>%
group_by(PERIODO, SEMANA) %>%
summarise(accidentes_domingo=sum(domingo), .groups="drop") %>%
mutate(PERIODO_LEAD=lead(PERIODO), SEMANA_LEAD=lead(SEMANA), rank = 1:length(PERIODO)) %>%
filter(rank < max(rank)) %>%
dplyr::select(-c(PERIODO, SEMANA, rank))
# fechas especiales
fechas_especiales <- special_dates %>%
group_by(PERIODO, SEMANA) %>%
summarise(
dias_festivos=sum(DIA_FESTIVO),
dias_especiales=sum(FECHA_ESPECIAL),
dias_festivos_especiales=sum(FESTIVO_FECHA_ESPECIAL),
.groups="drop"
)
df_processed <- df %>%
filter(CLASE == objective_var) %>%
group_by(PERIODO, SEMANA) %>%
summarise(
numero_accidentes = n(),
.groups="drop"
) %>%
mutate(
SEMANA=factor(SEMANA)
) %>%
merge(accidentes_por_clase,
by.x = c("PERIODO", "SEMANA"), by.y = c("PERIODO_LEAD", "SEMANA_LEAD"), all.x = T) %>%
merge(accidentes_por_gravedad, by.x = c("PERIODO", "SEMANA"), by.y = c("PERIODO_LEAD", "SEMANA_LEAD"), all.x = T) %>%
merge(rezagos_accidentes, by = c("PERIODO", "SEMANA"), all.x = T) %>%
merge(domingos, by.x = c("PERIODO", "SEMANA"), by.y = c("PERIODO_LEAD", "SEMANA_LEAD"), all.x = T) %>%
replace_na(list(incendio=0, muerto=0)) %>%
filter(!is.na(t_minus_3)) %>%
merge(fechas_especiales, by = c("PERIODO", "SEMANA")) %>%
arrange(PERIODO, SEMANA, .by_group = T)
return(df_processed)
}
Se procede a realizar la construcción del conjunto de datos para la clase choque y hacer todo el pipeline de preprocesamiento para implementar el escalado y estandarización al conjunto de entrenamiento y validación.
df_preprocessed <- armado_dataset_semanal(df=df, objective_var = "choque")
df_choque_train <- df_preprocessed %>% filter(PERIODO!=2018) %>% dplyr::select(-c(PERIODO))
df_choque_test <- df_preprocessed %>% filter(PERIODO==2018) %>% dplyr::select(-c(PERIODO))
model.choque.recipe <- recipe(numero_accidentes ~ .,
data = df_choque_train)
model.choque.steps <- model.choque.recipe %>%
step_log(all_outcomes()) %>%
step_center(all_predictors(), -SEMANA) %>%
step_scale(all_predictors(), -SEMANA)
model.choque.prepared <- prep(model.choque.steps, training = df_choque_train)
df_choque_train <- bake(model.choque.prepared, df_choque_train)
df_choque_test <- bake(model.choque.prepared, df_choque_test)
Como primer intento, vale la pena evaluar un modelo de regresión lineal simple con stepwise variable selection para descartar variables que no entreguen valor.
modelo.choque <- lm(numero_accidentes ~ ., data=df_choque_train)
step.model.choque <- stepAIC(modelo.choque, direction = "both", trace = F, steps = 2000)
model.choque.summary <- summary(step.model.choque)
model.choque.summary
##
## Call:
## lm(formula = numero_accidentes ~ SEMANA + choque + volcamiento +
## herido + muerto + dias_festivos + dias_especiales + dias_festivos_especiales,
## data = df_choque_train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.31931 -0.04272 0.00204 0.04418 0.65037
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 6.091776 0.105166 57.925 < 2e-16 ***
## SEMANA2 0.120890 0.098511 1.227 0.221695
## SEMANA3 0.239065 0.104498 2.288 0.023559 *
## SEMANA4 0.211269 0.115874 1.823 0.070267 .
## SEMANA5 0.221504 0.114468 1.935 0.054874 .
## SEMANA6 0.257169 0.115558 2.225 0.027553 *
## SEMANA7 0.421244 0.124817 3.375 0.000942 ***
## SEMANA8 0.245216 0.118518 2.069 0.040271 *
## SEMANA9 0.279817 0.122708 2.280 0.024005 *
## SEMANA10 0.390873 0.123614 3.162 0.001899 **
## SEMANA11 0.267459 0.123258 2.170 0.031598 *
## SEMANA12 0.427632 0.128646 3.324 0.001117 **
## SEMANA13 0.277290 0.111819 2.480 0.014259 *
## SEMANA14 0.280529 0.113682 2.468 0.014731 *
## SEMANA15 0.286883 0.111210 2.580 0.010857 *
## SEMANA16 0.232139 0.111059 2.090 0.038295 *
## SEMANA17 0.317643 0.112979 2.812 0.005595 **
## SEMANA18 0.305886 0.119138 2.567 0.011228 *
## SEMANA19 0.422618 0.118738 3.559 0.000499 ***
## SEMANA20 0.299458 0.121294 2.469 0.014685 *
## SEMANA21 0.201198 0.117870 1.707 0.089915 .
## SEMANA22 0.266244 0.121587 2.190 0.030098 *
## SEMANA23 0.269983 0.116036 2.327 0.021328 *
## SEMANA24 0.287876 0.117162 2.457 0.015155 *
## SEMANA25 0.305695 0.119857 2.551 0.011766 *
## SEMANA26 0.223210 0.117207 1.904 0.058785 .
## SEMANA27 0.239772 0.110271 2.174 0.031254 *
## SEMANA28 0.248458 0.114295 2.174 0.031297 *
## SEMANA29 0.329809 0.115071 2.866 0.004757 **
## SEMANA30 0.278489 0.115904 2.403 0.017502 *
## SEMANA31 0.451454 0.119779 3.769 0.000236 ***
## SEMANA32 0.354359 0.138585 2.557 0.011558 *
## SEMANA33 0.329505 0.121372 2.715 0.007414 **
## SEMANA34 0.242358 0.119862 2.022 0.044969 *
## SEMANA35 0.341818 0.118529 2.884 0.004511 **
## SEMANA36 0.303472 0.129757 2.339 0.020676 *
## SEMANA37 0.318107 0.122775 2.591 0.010521 *
## SEMANA38 0.372854 0.122930 3.033 0.002857 **
## SEMANA39 0.328007 0.123207 2.662 0.008615 **
## SEMANA40 0.328143 0.127634 2.571 0.011121 *
## SEMANA41 0.208450 0.124516 1.674 0.096212 .
## SEMANA42 0.257197 0.115013 2.236 0.026822 *
## SEMANA43 0.239461 0.116181 2.061 0.041032 *
## SEMANA44 0.515072 0.118772 4.337 2.65e-05 ***
## SEMANA45 0.222118 0.129668 1.713 0.088797 .
## SEMANA46 0.251365 0.115131 2.183 0.030579 *
## SEMANA47 0.246092 0.112833 2.181 0.030750 *
## SEMANA48 0.274417 0.116180 2.362 0.019469 *
## SEMANA49 0.342478 0.115395 2.968 0.003495 **
## SEMANA50 0.245617 0.116066 2.116 0.035991 *
## SEMANA51 0.321246 0.116408 2.760 0.006513 **
## SEMANA52 0.180701 0.123848 1.459 0.146655
## SEMANA53 -1.907555 0.107141 -17.804 < 2e-16 ***
## choque 0.060202 0.019959 3.016 0.003009 **
## volcamiento 0.010564 0.008407 1.257 0.210888
## herido -0.035709 0.019263 -1.854 0.065747 .
## muerto -0.011589 0.008745 -1.325 0.187102
## dias_festivos -0.064942 0.016685 -3.892 0.000149 ***
## dias_especiales -0.069392 0.023987 -2.893 0.004390 **
## dias_festivos_especiales 0.057739 0.027524 2.098 0.037615 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.09639 on 149 degrees of freedom
## Multiple R-squared: 0.9441, Adjusted R-squared: 0.922
## F-statistic: 42.69 on 59 and 149 DF, p-value: < 2.2e-16
plot_estimation_errors <- function(y_true, y_pred, title){
min_value <- min(y_true, y_pred) - min(y_true, y_pred) * 0.1
max_value <- max(y_true, y_pred) + max(y_true, y_pred) * 0.1
plot(x=y_true,y=y_pred,
ylab="Predicciones",xlab="Observados",
xlim=c(min_value, max_value),ylim=c(min_value, max_value),
las=1, cex=1, pch=16,
main=title)
abline(a=0,b=1,lwd=2,col="black", lty=2)
R_vl<-cor(y_pred, y_true)
R_vl<-format(R_vl, digits = 3, nsmall = 3)
rmse_vl<- rmse(actual = y_true, predicted = y_pred)
rmse_vl<-format(rmse_vl,digits = 3,nsmall = 2)
grid()
legend("topleft",legend=paste0(c("Correlación: ","RMSE: "),c(R_vl,rmse_vl)), bty="n")
}
y_pred <- exp(predict(step.model.choque, df_choque_train))
y_true <- exp(df_choque_train$numero_accidentes)
train_rmse <- rmse(y_true, y_pred)
plot_estimation_errors(
y_true = y_true,
y_pred = y_pred,
title="Rendimiento de modelo semanal choque ~ Entrenamiento"
)
De primera instancia, se observa un muy buen ajuste de los datos de entrenamiento respecto al modelo de regresión lineal, sin embargo, para evaluar su capacidad de generalizar es necesario evaluarlo en el conjunto de validación.
y_pred <- exp(predict(step.model.choque, df_choque_test))
y_true <- exp(df_choque_test$numero_accidentes)
test_rmse <- rmse(y_true, y_pred)
test_train_ratio <- round(((test_rmse / train_rmse) - 1)*100, 3)
plot_estimation_errors(
y_true = y_true,
y_pred = y_pred,
title="Rendimiento de modelo mensual semanal ~ Validación"
)
Para el conjunto de validación, se puede ver un buen ajuste del modelo, sin embargo, respecto al rmse de entrenamiento se tiene una variación del 33.066% lo cual genera indicios de sobrentrenamiento. Procedemos a comparar directamente las funciones de distribución de probabilidad de los errores de entrenamiento vs los de validación.
plot_errors_density <- function(model, train, y_train, test, y_test, title){
# data extraction
y_pred <- exp(predict(model, train))
y_true <- exp(y_train)
train_error <- y_true - y_pred
y_pred <- exp(predict(model, test))
y_true <- exp(y_test)
test_error <- y_true - y_pred
#errors dataframe
errors_df <- data.frame(
errors = c(train_error, test_error),
error_type = c(
rep("train", length(train_error)),
rep("test", length(test_error))
)
)
# plotting
ggplot(errors_df, aes(x=errors, color=error_type)) +
geom_density() +
theme_classic() +
ggtitle(title) +
theme(plot.title = element_text(hjust = 0.5))
}
plot_errors_density(
model = step.model.choque,
train = df_choque_train,
y_train = df_choque_train$numero_accidentes,
test = df_choque_test,
y_test = df_choque_test$numero_accidentes,
title="Errores de entrenamiento y validación"
)
No se ven diferencias significativas, lo que si es claro es que el modelo en el conjunto de validación tiende a sobreestimar algunas observaciones por lo que se ve que la media no está centrada en cero sino que se ve levemente desplazada hacia la izquierda al igual que la función de distribución de probabilidad una mayor concentración de la masa a la izquierda.
Debido a esto, procedemos a utilizar métodos de regularización para realizar una selección de variables más adecuada.
train_lasso_model <- function(df_train, df_test, min_lambda=-2){
# Construcción del dataset
x_train <- model.matrix(numero_accidentes~. , df_train)[,-1]
y_train <- df_train$numero_accidentes
# malla de lambdas a probar
lambda_seq <- 10^seq(2, min_lambda, by = -.1)
# modelado
set.seed(42)
cv.lasso <- cv.glmnet(x_train, y_train,
alpha = 1,
lambda = lambda_seq,
nfolds = 5)
lasso.model <- glmnet(x_train, y_train, alpha = 1, lambda = cv.lasso$lambda.min)
y_train_pred <- predict(lasso.model, x_train)
train_rmse <- rmse(exp(y_train), exp(y_train_pred))
x_test <- model.matrix(numero_accidentes~. , df_test)[,-1]
y_test <- df_test$numero_accidentes
y_test_pred <- predict(lasso.model, x_test)
test_rmse <- rmse(exp(y_test), exp(y_test_pred))
response=list(
"training"=list(
"data"=x_train,
"response"=y_train,
"predictions"=y_train_pred,
"rmse"=train_rmse
),
"validation"=list(
"data"=x_test,
"response"=y_test,
"predictions"=y_test_pred,
"rmse"=test_rmse
),
"model"=lasso.model
)
return(response)
}
lasso.choque.results <- train_lasso_model(df_train = df_choque_train, df_test = df_choque_test)
plot_estimation_errors(
y_true = exp(lasso.choque.results$training$response),
y_pred = exp(lasso.choque.results$training$predictions),
title="Rendimiento de modelo mensual choque ~ Entrenamiento"
)
plot_estimation_errors(
y_true = exp(lasso.choque.results$validation$response),
y_pred = exp(lasso.choque.results$validation$predictions),
title="Rendimiento de modelo mensual choque ~ Validación"
)
test_train_ratio <- round((lasso.choque.results$validation$rmse / lasso.choque.results$training$rmse -1 )*100, 3)
Para el modelo de lasso en la clase choque, la variación del rmse de validación respecto a entrenamiento es de un 14.202 lo cual es cercano al 15%, por lo que procedemos a comparar las fpd de los errores de entrenamiento y validación.
plot_errors_density(
model = lasso.choque.results$model,
train = lasso.choque.results$training$data,
y_train = lasso.choque.results$training$response,
test = lasso.choque.results$validation$data,
y_test = lasso.choque.results$validation$response,
title="Errores de entrenamiento y validación"
)
Se puede evidenciar que ambas distribuciones son muy similares, sin embargo una leve diferencia en las medias. De esto, nos queda que el modelo más apropiado es el de lasso para la clase choque.
df_preprocessed <- armado_dataset_semanal(df=df, objective_var = "atropello")
df_atropello_train <- df_preprocessed %>% filter(PERIODO!=2018) %>% dplyr::select(-c(PERIODO))
df_atropello_test <- df_preprocessed %>% filter(PERIODO==2018) %>% dplyr::select(-c(PERIODO))
model.atropello.recipe <- recipe(numero_accidentes ~ .,
data = df_atropello_train)
model.atropello.steps <- model.atropello.recipe %>%
step_log(all_outcomes()) %>%
step_center(all_predictors(), -SEMANA) %>%
step_scale(all_predictors(), -SEMANA)
model.atropello.prepared <- prep(model.atropello.steps, training = df_atropello_train)
df_atropello_train <- bake(model.atropello.prepared, df_atropello_train)
df_atropello_test <- bake(model.atropello.prepared, df_atropello_test)
lasso.atropello.results <- train_lasso_model(df_train = df_atropello_train, df_test = df_atropello_test, min_lambda = -3)
plot_estimation_errors(
y_true = exp(lasso.atropello.results$training$response),
y_pred = exp(lasso.atropello.results$training$predictions),
title="Rendimiento de modelo mensual atropello ~ Entrenamiento"
)
plot_estimation_errors(
y_true = exp(lasso.atropello.results$validation$response),
y_pred = exp(lasso.atropello.results$validation$predictions),
title="Rendimiento de modelo mensual atropello ~ Validación"
)
test_train_ratio <- round((lasso.atropello.results$validation$rmse / lasso.atropello.results$training$rmse -1 )*100, 3)
plot_errors_density(
model = lasso.atropello.results$model,
train = lasso.atropello.results$training$data,
y_train = lasso.atropello.results$training$response,
test = lasso.atropello.results$validation$data,
y_test = lasso.atropello.results$validation$response,
title="Errores de entrenamiento y validación"
)
Para el modelo de atropellos, se ve una deficiencia en la calidad de las predicciones, en especial para el conjutno de validación, donde es claro un sobreestimación de las observaciones, sin embargo, en terminos de RMSE, es un modelo que no posee una gran variación respecto al conjunto de entrenamiento.
df_preprocessed <- armado_dataset_semanal(df=df, objective_var = "caida ocupante")
df_caida_ocupante_train <- df_preprocessed %>% filter(PERIODO!=2018) %>% dplyr::select(-c(PERIODO))
df_caida_ocupante_test <- df_preprocessed %>% filter(PERIODO==2018) %>% dplyr::select(-c(PERIODO))
model.caida_ocupante.recipe <- recipe(numero_accidentes ~ .,
data = df_caida_ocupante_train)
model.caida_ocupante.steps <- model.caida_ocupante.recipe %>%
step_log(all_outcomes()) %>%
step_center(all_predictors(), -SEMANA) %>%
step_scale(all_predictors(), -SEMANA)
model.caida_ocupante.prepared <- prep(model.caida_ocupante.steps, training = df_caida_ocupante_train)
df_caida_ocupante_train <- bake(model.caida_ocupante.prepared, df_caida_ocupante_train)
df_caida_ocupante_test <- bake(model.caida_ocupante.prepared, df_caida_ocupante_test)
lasso.caida_ocupante.results <- train_lasso_model(df_train = df_caida_ocupante_train, df_test = df_caida_ocupante_test, min_lambda = -3)
plot_estimation_errors(
y_true = exp(lasso.caida_ocupante.results$training$response),
y_pred = exp(lasso.caida_ocupante.results$training$predictions),
title="Rendimiento de modelo mensual caida_ocupante ~ Entrenamiento"
)
plot_estimation_errors(
y_true = exp(lasso.caida_ocupante.results$validation$response),
y_pred = exp(lasso.caida_ocupante.results$validation$predictions),
title="Rendimiento de modelo mensual caida_ocupante ~ Validación"
)
test_train_ratio <- round((lasso.caida_ocupante.results$validation$rmse / lasso.caida_ocupante.results$training$rmse -1 )*100, 3)
plot_errors_density(
model = lasso.caida_ocupante.results$model,
train = lasso.caida_ocupante.results$training$data,
y_train = lasso.caida_ocupante.results$training$response,
test = lasso.caida_ocupante.results$validation$data,
y_test = lasso.caida_ocupante.results$validation$response,
title="Errores de entrenamiento y validación"
)
Para caida ocupante, la variación del RMSE de validación respecto a entrenamiento es de un 11.725% lo cual es un buen indicativo de la capacidad de generalización del modelo. Adicional, las FDP de los errores de entrenamiento y validación no se diferencian mucho por lo que se ve que el modelo tiene un buen ajuste.
df_preprocessed <- armado_dataset_semanal(df=df, objective_var = "volcamiento")
df_volcamiento_train <- df_preprocessed %>% filter(PERIODO!=2018) %>% dplyr::select(-c(PERIODO))
df_volcamiento_test <- df_preprocessed %>% filter(PERIODO==2018) %>% dplyr::select(-c(PERIODO))
model.volcamiento.recipe <- recipe(numero_accidentes ~ .,
data = df_volcamiento_train)
model.volcamiento.steps <- model.volcamiento.recipe %>%
step_log(all_outcomes()) %>%
step_center(all_predictors(), -SEMANA) %>%
step_scale(all_predictors(), -SEMANA)
model.volcamiento.prepared <- prep(model.volcamiento.steps, training = df_volcamiento_train)
df_volcamiento_train <- bake(model.volcamiento.prepared, df_volcamiento_train)
df_volcamiento_test <- bake(model.volcamiento.prepared, df_volcamiento_test)
lasso.volcamiento.results <- train_lasso_model(df_train = df_volcamiento_train, df_test = df_volcamiento_test, min_lambda = -2)
plot_estimation_errors(
y_true = exp(lasso.volcamiento.results$training$response),
y_pred = exp(lasso.volcamiento.results$training$predictions),
title="Rendimiento de modelo mensual volcamiento ~ Entrenamiento"
)
plot_estimation_errors(
y_true = exp(lasso.volcamiento.results$validation$response),
y_pred = exp(lasso.volcamiento.results$validation$predictions),
title="Rendimiento de modelo mensual volcamiento ~ Validación"
)
test_train_ratio <- round((lasso.volcamiento.results$validation$rmse / lasso.volcamiento.results$training$rmse -1 )*100, 3)
plot_errors_density(
model = lasso.volcamiento.results$model,
train = lasso.volcamiento.results$training$data,
y_train = lasso.volcamiento.results$training$response,
test = lasso.volcamiento.results$validation$data,
y_test = lasso.volcamiento.results$validation$response,
title="Errores de entrenamiento y validación"
)
A pesar de que el modelo de volcamiento no posea una variación tan alta -2.724%, es evidente que el modelo se encuentra subestimando los datos, y el rango de las predicciones no es acorde al rango lo la variabilidad natural del fenomeno en estudio, es necesario profundizar para este modelo.
df_preprocessed <- armado_dataset_semanal(df=df, objective_var = "otro")
df_otro_train <- df_preprocessed %>% filter(PERIODO!=2018) %>% dplyr::select(-c(PERIODO))
df_otro_test <- df_preprocessed %>% filter(PERIODO==2018) %>% dplyr::select(-c(PERIODO))
model.otro.recipe <- recipe(numero_accidentes ~ .,
data = df_otro_train)
model.otro.steps <- model.otro.recipe %>%
step_log(all_outcomes()) %>%
step_center(all_predictors(), -SEMANA) %>%
step_scale(all_predictors(), -SEMANA)
model.otro.prepared <- prep(model.otro.steps, training = df_otro_train)
df_otro_train <- bake(model.otro.prepared, df_otro_train)
df_otro_test <- bake(model.otro.prepared, df_otro_test)
modelo.otro <- lm(numero_accidentes ~ ., data=df_otro_train)
step.model.otro <- stepAIC(modelo.otro, direction = "both", trace = F, steps = 2000)
model.otro.summary <- summary(step.model.otro)
model.otro.summary
##
## Call:
## lm(formula = numero_accidentes ~ SEMANA + incendio + otro + volcamiento +
## herido + t_minus_2 + t_minus_3 + dias_festivos + dias_especiales +
## dias_festivos_especiales, data = df_otro_train)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.55054 -0.08653 -0.00171 0.08862 0.69727
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.27814 0.17600 24.308 < 2e-16 ***
## SEMANA2 0.27741 0.18098 1.533 0.127461
## SEMANA3 0.39366 0.20096 1.959 0.052013 .
## SEMANA4 0.30154 0.19419 1.553 0.122624
## SEMANA5 0.25222 0.19126 1.319 0.189323
## SEMANA6 0.51760 0.19573 2.645 0.009069 **
## SEMANA7 0.40548 0.21382 1.896 0.059873 .
## SEMANA8 0.35929 0.19863 1.809 0.072520 .
## SEMANA9 0.30940 0.20533 1.507 0.133998
## SEMANA10 0.53931 0.20793 2.594 0.010456 *
## SEMANA11 0.24996 0.20918 1.195 0.234034
## SEMANA12 0.48539 0.21875 2.219 0.028025 *
## SEMANA13 0.21568 0.19019 1.134 0.258631
## SEMANA14 0.16919 0.19322 0.876 0.382673
## SEMANA15 0.29590 0.18722 1.581 0.116141
## SEMANA16 0.19678 0.18842 1.044 0.298027
## SEMANA17 0.24500 0.19287 1.270 0.205988
## SEMANA18 0.34719 0.19626 1.769 0.078968 .
## SEMANA19 0.43957 0.20284 2.167 0.031843 *
## SEMANA20 0.33075 0.20083 1.647 0.101710
## SEMANA21 0.48797 0.19694 2.478 0.014352 *
## SEMANA22 0.58663 0.21010 2.792 0.005931 **
## SEMANA23 0.32410 0.20649 1.570 0.118670
## SEMANA24 0.35902 0.20254 1.773 0.078369 .
## SEMANA25 0.27361 0.20370 1.343 0.181290
## SEMANA26 0.18691 0.19944 0.937 0.350209
## SEMANA27 0.26044 0.18444 1.412 0.160042
## SEMANA28 0.44733 0.19392 2.307 0.022462 *
## SEMANA29 0.34981 0.19725 1.773 0.078223 .
## SEMANA30 0.26717 0.19965 1.338 0.182908
## SEMANA31 0.52721 0.20121 2.620 0.009711 **
## SEMANA32 0.17184 0.22679 0.758 0.449839
## SEMANA33 0.37244 0.20675 1.801 0.073694 .
## SEMANA34 0.35525 0.20577 1.726 0.086374 .
## SEMANA35 0.45314 0.20266 2.236 0.026858 *
## SEMANA36 0.29861 0.22246 1.342 0.181577
## SEMANA37 0.34551 0.20510 1.685 0.094198 .
## SEMANA38 0.34223 0.21014 1.629 0.105547
## SEMANA39 0.40422 0.20613 1.961 0.051768 .
## SEMANA40 0.31852 0.21830 1.459 0.146672
## SEMANA41 0.30822 0.20775 1.484 0.140064
## SEMANA42 0.30943 0.19507 1.586 0.114841
## SEMANA43 0.33303 0.19601 1.699 0.091419 .
## SEMANA44 0.55917 0.20088 2.784 0.006082 **
## SEMANA45 0.25143 0.21831 1.152 0.251306
## SEMANA46 0.49746 0.19567 2.542 0.012045 *
## SEMANA47 0.07649 0.19573 0.391 0.696528
## SEMANA48 0.14629 0.19553 0.748 0.455536
## SEMANA49 0.20191 0.18869 1.070 0.286344
## SEMANA50 0.23886 0.18985 1.258 0.210331
## SEMANA51 0.35023 0.18725 1.870 0.063419 .
## SEMANA52 0.30142 0.19945 1.511 0.132857
## SEMANA53 -1.61770 0.18143 -8.916 1.74e-15 ***
## incendio 0.01935 0.01379 1.403 0.162665
## otro 0.04265 0.02007 2.125 0.035261 *
## volcamiento 0.03446 0.01431 2.409 0.017245 *
## herido -0.04758 0.02823 -1.685 0.094068 .
## t_minus_2 0.02649 0.01840 1.440 0.152097
## t_minus_3 0.03968 0.01835 2.163 0.032190 *
## dias_festivos -0.05970 0.02935 -2.034 0.043722 *
## dias_especiales -0.11317 0.04211 -2.688 0.008023 **
## dias_festivos_especiales 0.16453 0.04819 3.414 0.000827 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1687 on 147 degrees of freedom
## Multiple R-squared: 0.8287, Adjusted R-squared: 0.7576
## F-statistic: 11.65 on 61 and 147 DF, p-value: < 2.2e-16
plot_estimation_errors(
y_true = exp(df_otro_train$numero_accidentes),
y_pred = exp(predict(step.model.otro, df_otro_train)),
title="Rendimiento de modelo mensual otro ~ Entrenamiento"
)
plot_estimation_errors(
y_true = exp(df_otro_test$numero_accidentes),
y_pred = exp(predict(step.model.otro, df_otro_test)),
title="Rendimiento de modelo mensual otro ~ Validación"
)
plot_errors_density(
model = step.model.otro,
train = df_otro_train,
y_train = df_otro_train$numero_accidentes,
test = df_otro_test,
y_test = df_otro_test$numero_accidentes,
title="Errores de entrenamiento y validación"
)
Claramente el modelo de otros con una regresión lineal muestra indicios de sobreentrenamiento, vamos a intentar con una regresión lasso.
lasso.otro.results <- train_lasso_model(df_train = df_otro_train, df_test = df_otro_test, min_lambda = -2)
plot_estimation_errors(
y_true = exp(lasso.otro.results$training$response),
y_pred = exp(lasso.otro.results$training$predictions),
title="Rendimiento de modelo mensual otro ~ Entrenamiento"
)
plot_estimation_errors(
y_true = exp(lasso.otro.results$validation$response),
y_pred = exp(lasso.otro.results$validation$predictions),
title="Rendimiento de modelo mensual otro ~ Validación"
)
test_train_ratio <- round((lasso.otro.results$validation$rmse / lasso.otro.results$training$rmse -1 )*100, 3)
plot_errors_density(
model = lasso.otro.results$model,
train = lasso.otro.results$training$data,
y_train = lasso.otro.results$training$response,
test = lasso.otro.results$validation$data,
y_test = lasso.otro.results$validation$response,
title="Errores de entrenamiento y validación"
)
Aunque la variación del RMSE de entrenamiento a validación para la regresión lasso es menor que la regresión lineal, claramente el modelo está sobreestimando varias de las observaciones.
Se hace necesario evaluar nuevos modelos para está clase de accidentes.
Los modelos semanales tuvieron un buen desempeño a nivel general, a excepeción de los modelos de volcamiento y otros, los cuales sufrieron de tener rangos muy cerrados de estimación, por lo que no tienen en cuenta toda la variabilidad total del fenomeno de estudio. Es importante explorar nuevas variables que le permitan a los modelos capturar la variabilidad de estás clases.