Justificación de la variable A diferencia de los meses, las horas del día representan una escala temporal de alta granularidad. La “Hora de reinicio” se considera una variable cuantitativa continua (o discreta si tomamos la hora exacta como número entero). Sin embargo, al contar con 24 posibles resultados distintos, analizarla valor por valor difumina las tendencias estructurales. Por ello, se la somete a un proceso de agrupación por intervalos (clases). Al agrupar las 24 horas en bloques definidos (ej. bloques de 4 horas: [0-4), [4-8), etc.), podemos identificar fácilmente si el restablecimiento de los sistemas ocurre predominantemente en la madrugada, la mañana o la tarde, logrando un panorama probabilístico mucho más útil para la gestión operativa y de recursos técnicos.
Primero, leemos la base de datos y aislamos únicamente el componente “Hora” (0 a 24) de la fecha en la que el sistema logró restablecerse, aplicando una limpieza rigurosa para homogeneizar los formatos de fecha y omitir los registros vacíos.
datos <- read.csv("database-_1_.csv", stringsAsFactors = FALSE, check.names = FALSE)
raw <- datos$`Restart Date/Time`
raw <- raw[raw != "" & !is.na(raw)]
# Parseo robusto de múltiples formatos de fecha y hora
fechas <- as.POSIXct(raw, format="%m/%d/%Y %H:%M")
fechas[is.na(fechas)] <- as.POSIXct(raw[is.na(fechas)], format="%m/%d/%Y")
fechas[is.na(fechas)] <- as.POSIXct(raw[is.na(fechas)], format="%d/%m/%Y %H:%M")
fechas[is.na(fechas)] <- as.POSIXct(raw[is.na(fechas)], format="%Y-%m-%d")
# Extracción de la hora en formato numérico
horas_limpias <- as.numeric(format(fechas, "%H"))
horas_limpias <- horas_limpias[!is.na(horas_limpias)]
#Tabla de Frecuencia por Intervalos
A continuación, creamos intervalos de 4 horas de amplitud. Esto dividirá el día en 6 franjas clave. También añadimos la “Marca de Clase” (punto medio del intervalo) necesaria para cálculos matemáticos.
# 1. Definir amplitud y límites numéricos
amplitud <- 4
limites <- seq(0, 24, by = amplitud)
# --- NUEVO: Dar formato de hora y minuto (HH:MM) a los límites ---
limites_formato <- sprintf("%02d:00", limites)
n <- length(limites)
# Generar etiquetas automáticamente con el nuevo formato
etiquetas <- paste0("[", limites_formato[-n], " - ", limites_formato[-1], ")")
# Forzamos a que el último intervalo tenga el corchete cerrado "]" al final
etiquetas[length(etiquetas)] <- paste0("[", limites_formato[n-1], " - ", limites_formato[n], "]")
# 2. Agrupar la variable en intervalos usando las etiquetas formateadas
# IMPORTANTE: breaks sigue usando 'limites' (números), labels usa 'etiquetas' (texto 00:00)
horas_agrupadas <- cut(horas_limpias, breaks = limites, labels = etiquetas, right = FALSE, include.lowest = TRUE)
# 3. Creación de tabla base
tabla_base <- table(horas_agrupadas)
Intervalo <- names(tabla_base)
ni <- as.vector(tabla_base)
hi <- round(ni / sum(ni) * 100, 2)
# Ajuste por pérdida de decimales
diferencia <- 100 - sum(hi)
if(diferencia != 0){
indice_max <- which.max(ni)
hi[indice_max] <- hi[indice_max] + diferencia
}
# 4. Frecuencias Acumuladas
Ni_asc <- cumsum(ni)
Ni_dsc <- rev(cumsum(rev(ni)))
Hi_asc <- cumsum(hi)
Hi_dsc <- rev(cumsum(rev(hi)))
# 5. Marca de clase
xi <- limites[-n] + (amplitud / 2)
# 6. Construcción del Dataframe
TDFHoras <- data.frame(
"Intervalos" = Intervalo,
"MC" = xi,
"ni" = ni,
"hi (%)" = hi,
"Ni Asc" = Ni_asc,
"Ni Dsc" = Ni_dsc,
"Hi Asc" = Hi_asc,
"Hi Dsc" = Hi_dsc,
check.names = FALSE
)
paged_table(TDFHoras, options = list(rows.print = 6))
La visualización a través del histograma nos permite ver claramente en qué bloques horarios se logra restablecer el sistema. Al estar agrupado, las barras representan intervalos completos de tiempo.
library(ggplot2)
TDFHoras$Intervalos <- reorder(TDFHoras$Intervalos, TDFHoras$`MC`)
p_ni <- ggplot(TDFHoras, aes(x = Intervalos, y = ni)) +
geom_col(width = 0.9, fill = "steelblue", color = "black", alpha = 0.8, linewidth = 0.5) +
scale_y_continuous(expand = expansion(mult = c(0, 0.05))) +
labs(
title = "Gráfica No 1: Distribución local de reinicios por Intervalos de Hora",
x = "Franja Horaria del Día",
y = "Cantidad de Reactivaciones"
) +
theme_classic() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
axis.text.x = element_text(angle = 0, hjust = 0.5, color = "black"),
axis.text.y = element_text(color = "black"),
axis.line = element_line(linewidth = 0.5, color = "black")
)
print(p_ni)
Para comprender la verdadera magnitud de las recuperaciones en cada franja horaria respecto al panorama completo, es estrictamente necesario establecer el eje vertical (Y) con el límite máximo de la población total de estudio (2795 accidentes). Al fijar esta escala global, la gráfica actúa como un termómetro absoluto: evita distorsiones y permite dimensionar de forma objetiva qué porción del universo total de fallos logra ser restablecida en cada bloque temporal.
TDFHoras$Intervalos <- reorder(TDFHoras$Intervalos, TDFHoras$`MC`)
p_ni_global <- ggplot(TDFHoras, aes(x = Intervalos, y = ni)) +
geom_col(width = 0.9, fill = "steelblue", color = "black", alpha = 0.8, linewidth = 0.5) +
scale_y_continuous(limits = c(0, 2795), expand = expansion(mult = c(0, 0.05))) +
labs(
title = "Gráfica No 2: Distribución global de reinicios por Intervalos de Hora",
x = "Franja Horaria del Día",
y = "Cantidad de Reactivaciones"
) +
theme_classic() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
axis.text.x = element_text(angle = 0, hjust = 0.5, color = "black"),
axis.text.y = element_text(color = "black"),
axis.line = element_line(linewidth = 0.5, color = "black")
)
print(p_ni_global)
Con este gráfico normalizamos los datos para evaluar la probabilidad real de que un restablecimiento ocurra dentro de un bloque horario determinado a lo largo del día.
TDFHoras$Intervalos <- reorder(TDFHoras$Intervalos, TDFHoras$`MC`)
p_hi <- ggplot(TDFHoras, aes(x = Intervalos, y = `hi (%)`)) +
geom_col(width = 0.9, fill = "steelblue", color = "black", alpha = 0.8, linewidth = 0.5) +
scale_y_continuous(expand = expansion(mult = c(0, 0.05)),
labels = function(x) paste0(x, "%")) +
labs(title = "Gráfica No 3: Probabilidad local (%) de reinicio por Intervalo",
x = "Franja Horaria",
y = "Porcentaje") +
theme_classic() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
axis.text.x = element_text(angle = 0, hjust = 0.5, color = "black"),
axis.text.y = element_text(color = "black"),
axis.line = element_line(linewidth = 0.5, color = "black")
)
print(p_hi)
Para evaluar correctamente la probabilidad en términos absolutos, es crucial establecer la escala del eje vertical de 0% a 100%. Si el gráfico se ajustara automáticamente al porcentaje máximo obtenido, la barra llenaría casi toda la gráfica, dando la falsa impresión visual de que ese bloque abarca casi la totalidad de las reparaciones. Al fijar el tope en 100%, logramos un “espectro de probabilidad completo”, permitiendo dimensionar visualmente la verdadera tasa que representa cada franja horaria respecto al día entero.
library(ggplot2)
TDFHoras$Intervalos <- reorder(TDFHoras$Intervalos, TDFHoras$`MC`)
p_hi_global <- ggplot(TDFHoras, aes(x = Intervalos, y = `hi (%)`)) +
geom_col(width = 0.9, fill = "steelblue", color = "black", alpha = 0.8, linewidth = 0.5) +
scale_y_continuous(limits = c(0, 100),
expand = expansion(mult = c(0, 0.05)),
labels = function(x) paste0(x, "%")) +
labs(title = "Gráfica No 4: Probabilidad global (%) de reinicio por Intervalo",
x = "Franja Horaria",
y = "Porcentaje") +
theme_classic() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
axis.text.x = element_text(angle = 0, hjust = 0.5, color = "black"),
axis.text.y = element_text(color = "black"),
axis.line = element_line(linewidth = 0.5, color = "black")
)
print(p_hi_global)
Evaluamos la dispersión horaria cruda. A diferencia de los intervalos, la caja expone dónde se comprime el 50% central de las reactivaciones a lo largo de las 24 horas del reloj.
boxplot(horas_limpias,
horizontal = TRUE,
col = "steelblue",
ylim = c(0, 24),
main = "Gráfica 5: Rango Intercuartílico de la Hora de Reinicio",
xaxt = "n")
axis(1, at = seq(0, 24, by = 2), labels = seq(0, 24, by = 2), las = 1)
grid(nx = NULL, ny = NA, col = "lightgray", lty = "dotted")
Las ojivas ilustran la suma acumulativa a medida que transcurren las horas del día, revelando la aceleración de los restablecimientos operativos en horarios laborales frente a horas nocturnas.
TDFHoras$Index <- 1:nrow(TDFHoras)
p_ojiva <- ggplot(TDFHoras) +
geom_line(aes(x = Index, y = `Ni Asc`, color = "Ascendente", linetype = "Ascendente"), linewidth = 0.8) +
geom_point(aes(x = Index, y = `Ni Asc`, color = "Ascendente"), size = 2) +
geom_line(aes(x = Index, y = `Ni Dsc`, color = "Descendente", linetype = "Descendente"), linewidth = 0.8) +
geom_point(aes(x = Index, y = `Ni Dsc`, color = "Descendente"), size = 2) +
scale_x_continuous(breaks = TDFHoras$Index, labels = TDFHoras$Intervalos) +
scale_color_manual(name = NULL,
values = c("Ascendente" = "black", "Descendente" = "steelblue")) +
scale_linetype_manual(name = NULL,
values = c("Ascendente" = "longdash", "Descendente" = "solid")) +
labs(title = "Gráfica 6: Distribución acumulada de los reinicios",
x = "Franja Horaria",
y = "Cantidad Acumulada") +
theme_bw() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
legend.position = c(0.85, 0.85),
legend.background = element_rect(color = "black", fill = "white", linewidth = 0.5),
legend.key = element_blank(),
panel.grid.minor = element_blank(),
axis.text = element_text(color = "black")
)
print(p_ojiva)
Determinamos el marco matemático exacto en base a la data cruda, analizando hacia qué hora apunta el “centro de gravedad” de las recuperaciones operativas.
library(e1071)
library(knitr)
# 1. Rango
ri <- min(horas_limpias)
rs <- 24
# 2. Mediana
mediana <- median(horas_limpias)
# 3. Media Aritmética
media_aritmetica <- mean(horas_limpias)
# 4. Moda
t <- table(horas_limpias)
Mo <- as.numeric(names(t)[which.max(t)])
# 5. Desviación Estándar
desviacion_estandar <- sd(horas_limpias)
# 6. Coeficiente de Variabilidad
coeficiente_variabilidad <- (desviacion_estandar / media_aritmetica) * 100
# 7. Asimetría (Skewness)
As <- skewness(horas_limpias)
# 8. Curtosis
curtosis_val <- kurtosis(horas_limpias)
# --- PREPARACIÓN DE LA TABLA ---
Variable <- "Hora de Reinicio"
S_texto <- paste(round(desviacion_estandar, 2))
Tabla_indicadores <- data.frame(
Variable,
ri, # Mínimo automático (0)
rs, # Máximo
round(media_aritmetica, 2), # Media
mediana, # Mediana
Mo, # Moda
S_texto, # Desviación
round(coeficiente_variabilidad, 2),
round(As, 2),
round(curtosis_val, 2)
)
colnames(Tabla_indicadores) <- c("Variable","Mínimo","Máximo","Media","Me","Mo","S","Cv (%)","As","K")
# Imprimir Tabla
kable(Tabla_indicadores, format = "markdown", caption = "Tabla No. 1: Indicadores estadísticos de las horas de reinicio (Límite superior 24h).")
| Variable | Mínimo | Máximo | Media | Me | Mo | S | Cv (%) | As | K |
|---|---|---|---|---|---|---|---|---|---|
| Hora de Reinicio | 0 | 24 | 13.28 | 14 | 15 | 5.47 | 41.15 | -0.42 | -0.38 |
Buscamos comportamientos “no estándares” o atípicos con respecto a las horas habituales. Al ser el horario un factor repetitivo (0-24) es posible que no se detecten irregularidades muy alejadas estadísticamente de las colas.
stats_outliers <- boxplot.stats(horas_limpias)$out
num_outliers <- length(stats_outliers)
print(paste("Cantidad de horas atípicas:", num_outliers))
## [1] "Cantidad de horas atípicas: 0"
minimooutliers <- if(num_outliers > 0) min(stats_outliers) else "Ninguno"
maximooutliers <- if(num_outliers > 0) max(stats_outliers) else "Ninguno"
print(paste("Hora atípica mínima:", minimooutliers))
## [1] "Hora atípica mínima: Ninguno"
print(paste("Hora atípica máxima:", maximooutliers))
## [1] "Hora atípica máxima: Ninguno"
La variable “Hora de reinicio” fluctúa entre 0 y 24 y sus valores se encuentran alrededor de 13.28, con una desviación estándar de 5.47, siendo una variable heterogénea, cuyos valores se concentran en la parte media de la distribución con la agregación de valores atípicos de 0 outliers; por todo lo anterior, el comportamiento de la variable es regular.