1 Carga de Dataset

El primer paso crítico en cualquier análisis de datos es la importación correcta de la fuente de información. En esta sección, se utiliza la función read.csv() para cargar el archivo de datos externo al entorno de trabajo de R. Este proceso transforma los datos crudos en un “data frame”, permitiendo que el software reconozca las columnas y filas para su posterior manipulación.

database <- read.csv("database-_1_.csv", header = TRUE, sep = ",", dec = ".", check.names = FALSE)

raw_evacuaciones <- database$`Public Evacuations`
raw_evacuaciones <- raw_evacuaciones[raw_evacuaciones != ""]
raw_evacuaciones <- na.omit(raw_evacuaciones)

evacuaciones_obj <- floor(as.numeric(as.character(raw_evacuaciones)))
evacuaciones_num <- as.numeric(evacuaciones_obj)
evacuaciones_num <- na.omit(evacuaciones_num)
evacuaciones_obj <- evacuaciones_obj[!is.na(evacuaciones_num)]

# Filtrar índices lógicos (las evacuaciones deben ser mayores o iguales a 0)
valid_indices <- evacuaciones_num >= 0
evacuaciones_num <- evacuaciones_num[valid_indices]
evacuaciones_obj <- evacuaciones_obj[valid_indices]

2 Tabla de frecuencia

Una vez cargados los datos, el siguiente paso es la organización mediante tablas de frecuencia. Esta técnica estadística agrupa la información de una variable específica para contar cuántas veces ocurre cada evento (en este caso, la cantidad de personas evacuadas en cada incidente).

library(rmarkdown)

library(ggplot2)
## 
## Adjuntando el paquete: 'ggplot2'
## The following object is masked from 'package:e1071':
## 
##     element
# Tabla de frecuencia
datos <- read.csv("database-_1_.csv", stringsAsFactors = FALSE)
evacuaciones_limpias <- floor(as.numeric(datos$Public.Evacuations))
evacuaciones_limpias <- evacuaciones_limpias[!is.na(evacuaciones_limpias) & evacuaciones_limpias >= 0]

tabla_base <- table(evacuaciones_limpias)
ni <- as.vector(tabla_base)
Magnitud <- names(tabla_base)
hi <- round(ni/sum(ni)*100, 2)

diferencia <- 100 - sum(hi)
if(diferencia != 0){
  indice_max <- which.max(ni)
  hi[indice_max] <- hi[indice_max] + diferencia
}

Ni_asc <- cumsum(ni)
Ni_dsc <- rev(cumsum(rev(ni)))
Hi_asc <- cumsum(hi)            
Hi_dsc <- rev(cumsum(rev(hi)))

TDFMagnitudFin <- data.frame(
  "Evacuados" = Magnitud, 
  "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(TDFMagnitudFin, options = list(rows.print = 10))

3 Histograma Global de Cantidad Absoluta

En esta sección se analiza la magnitud definitiva de los incidentes mediante el conteo de personas evacuadas. La Regla de Sturges permite determinar el número óptimo de intervalos (\(k\)). Aquí evaluamos la concentración masiva de la siniestralidad dentro del primer intervalo de clase (el valor cero), donde la inmensa mayoría de los eventos no supusieron un riesgo habitacional.

variable_interes <- datos$Public.Evacuations
volumen <- na.omit(variable_interes)
k <- 1 + (3.322 * log10(length(volumen)))
R <- max(volumen) - min(volumen)
A <- R / floor(k) 

limit_zoom <- A 
datos_zoom <- datos %>%
  filter(!is.na(Public.Evacuations)) %>%
  filter(Public.Evacuations <= limit_zoom)

p_zoom <- ggplot(datos_zoom, aes(x = Public.Evacuations)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white", alpha = 0.8) +
  scale_x_continuous(labels = scales::comma_format()) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.05))) +
  
  labs(
    title = "Gráfica 1: Distribución Global de Evacuaciones", 
    x = "Personas Evacuadas",
    y = "Cantidad"
  ) +
  
  theme_classic() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

print(p_zoom)

4 Histograma local de cantidad absoluta del Rango Principal

Dado que la distribución global está completamente dominada por el valor 0, filtramos el análisis hacia un subgrupo manejable. El intervalo local se limitó de 0 a 50 evacuados para observar con mayor detalle los incidentes de impacto civil bajo o moderado, desempaquetando la primera porción del conjunto de datos.

library(ggplot2)
library(dplyr)
library(scales)

datos <- read.csv("database-_1_.csv")
variable_interes <- datos$Public.Evacuations

# ¡ZOOM EXTREMO! Ajustamos el límite a 3
limit_zoom <- 4

datos_zoom <- datos %>%
  filter(!is.na(Public.Evacuations)) %>%
  filter(Public.Evacuations <= limit_zoom)

p_zoom_5barras <- ggplot(datos_zoom, aes(x = Public.Evacuations)) +
  geom_histogram(binwidth = 1, fill = "steelblue", color = "white", alpha = 0.8) +
  
  # Forzamos las marcas del eje X para que vayan del 0 al 3, de uno en uno
  scale_x_continuous(breaks = seq(0, 4, by = 1), labels = scales::comma_format()) +
  
  coord_cartesian(ylim = c(0, 10)) +
  
  labs(
    title = "Gráfica No 2: Distribución local en el rango principal", 
    x = "Número de Personas Evacuadas",
    y = "Cantidad"
  ) +
  
  theme_classic() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.text.x = element_text(hjust = 0.5)
  )

print(p_zoom_5barras)

5 Histograma global de cantidad relativa del rango principal

Los histogramas ofrecen una representación visual de la densidad de los datos. Mientras que el histograma global muestra la magnitud total de la siniestralidad, los enfoques locales con amplamiento de datos que nos permiten ‘desempaquetar’ los rangos de baja escala, revelando la frecuencia exacta de pequeños desplazamientos que suelen quedar ocultos en la visualización general

library(ggplot2)
library(dplyr)
library(scales)

datos <- read.csv("database-_1_.csv")
limit_zoom <- 4

datos_zoom <- datos %>%
  filter(!is.na(Public.Evacuations)) %>%
  filter(Public.Evacuations <= limit_zoom)

p_zoom_5barras_pct <- ggplot(datos_zoom, aes(x = Public.Evacuations)) +
  geom_histogram(
    aes(y = after_stat(count / sum(count))), 
    binwidth = 1, 
    fill = "steelblue", 
    color = "white", 
    alpha = 0.8
  ) +
  # Eje X del 0 al 7 de uno en uno
  scale_x_continuous(breaks = seq(0, 4, by = 1), labels = scales::comma_format()) +
  coord_cartesian(ylim = c(0, 0.05)) + 
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.1)) +
  
  labs(
    title = "Gráfica No 3: Distribución porcentual global en el rango principal",
    x = "Personas Evacuadas",
    y = "Porcentaje (%)"
  ) +
  
  theme_classic() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.text.x = element_text(hjust = 0.5)
  )

print(p_zoom_5barras_pct)

6 Histograma local de cantidad relativa del rango principal

Esta sección se enfoca en el comportamiento rutinario de la industria, analizando los evacuados para observar la frecuencia de eventos de impactos.

library(ggplot2)
library(dplyr)
library(scales)

datos <- read.csv("database-_1_.csv")
limit_zoom <- 4

datos_zoom <- datos %>%
  filter(!is.na(Public.Evacuations)) %>%
  filter(Public.Evacuations <= limit_zoom)

p_zoom_5barras_pct <- ggplot(datos_zoom, aes(x = Public.Evacuations)) +
  geom_histogram(
    aes(y = after_stat(count / sum(count))), 
    binwidth = 1, 
    fill = "steelblue", 
    color = "white", 
    alpha = 0.8
  ) +
  # Eje X del 0 al 4 de uno en uno
  scale_x_continuous(breaks = seq(0, 4, by = 1), labels = scales::comma_format()) +
  
  # ZOOM Y al 1.0% (0.01 = 1.0%)
  coord_cartesian(ylim = c(0, 0.01)) + 
  scale_y_continuous(labels = scales::percent_format(accuracy = 0.1)) +
  
  labs(
    title = "Gráfica No 3: Distribución porcentual local en el rango principal",
    x = "Personas Evacuadas",
    y = "Porcentaje (%)"
  ) +
  
  theme_classic() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold"),
    axis.text.x = element_text(hjust = 0.5)
  )

print(p_zoom_5barras_pct)

7 Ojivas convinadas de la cantidad absoluta del rango principal

La ojiva es la herramienta clave para el análisis acumulativo. Al observar cómo se suman las frecuencias, podemos determinar con precisión en qué punto se alcanza el 50% o el 95% de la siniestralidad total. En esta variable, la curva ascendente es casi vertical al inicio, confirmando que la mayoría de los casos no escalan a niveles masivos de evacuación.

library(ggplot2)
library(dplyr)
library(scales)

datos <- read.csv("database-_1_.csv")
volumen <- na.omit(datos$Public.Evacuations)
datos_local <- volumen[volumen <= 7]

# Ajuste matemático para 7 escalones exactos
k_local <- 4
min_val <- 0
max_val <- 4
A_local <- 1 
Li_num <- seq(min_val, max_val - A_local, length.out = k_local)
Ls_num <- Li_num + A_local
ni_local <- numeric(k_local)

for(i in 1:k_local){
  if(i == k_local){
    ni_local[i] <- sum(datos_local >= Li_num[i] & datos_local <= max_val)
  } else {
    ni_local[i] <- sum(datos_local >= Li_num[i] & datos_local < Ls_num[i])
  }
}

Niasc <- cumsum(ni_local)
Nidsc <- rev(cumsum(rev(ni_local)))

datos_asc <- data.frame(x = c(min_val, Ls_num), y = c(0, Niasc), Tipo = "Ascendente")
datos_dsc <- data.frame(x = c(Li_num, max_val), y = c(Nidsc, 0), Tipo = "Descendente")
datos_ojivas_plot <- rbind(datos_asc, datos_dsc)

p_ojiva_cruzada_solida <- ggplot(datos_ojivas_plot, aes(x = x, y = y, color = Tipo, linetype = Tipo)) +
  geom_line(linewidth = 0.8) +
  geom_point(size = 2) +
  
  # Eje X del 0 al 7
  scale_x_continuous(breaks = seq(0, 4, by = 1), labels = scales::comma_format()) +
  
  scale_color_manual(values = c("Ascendente" = "black", "Descendente" = "blue")) +
  scale_linetype_manual(values = c("Ascendente" = "solid", "Descendente" = "solid")) +
  
  labs(
    title = "Gráfica 5: Distribución Acumulada Comparativa",
    x = "Personas Evacuadas",
    y = "Cantidad Acumulada",
    color = NULL,
    linetype = NULL
  ) +
  
  theme_bw() + 
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    legend.position = c(0.85, 0.5),
    legend.background = element_rect(color = "black", fill = "white"),
    axis.text = element_text(color = "black")
  )

print(p_ojiva_cruzada_solida)

8 Diagrama de caja del rango principal

El diagrama de cajas se utiliza para resumir la distribución a través de cuartiles y detectar visualmente los valores atípicos. En el caso de las evacuaciones, este gráfico es fundamental para demostrar cómo la mediana se ancla en el cero, desplazando cualquier incidente con evacuados a la categoría de anomalía o ‘outlier’.

library(ggplot2)
library(dplyr)


datos <- read.csv("database-_1_.csv")
evacuaciones <- na.omit(datos$Public.Evacuations)
evacuaciones_filtradas <- evacuaciones[evacuaciones >= 0 & evacuaciones <= 4]
par(mar = c(5, 6, 4, 2)) 

# 3. Dibujar el diagrama de caja
boxplot(evacuaciones_filtradas,
        horizontal = TRUE,
        col = "lightblue",         
        xlab = "Personas Evacuadas",
        medcol = "red", medlwd = 3,
        pch = 19,                  
        outline = TRUE,            
        ylim = c(0, 4)            
)
mtext("Gráfica 6: Distribución de Evacuaciones del rango principal",
      side = 3, line = 1.5, cex = 1.2, font = 2)
axis(1, at = 0:4, labels = 0:4)

AVISO IMPORTANTE!

Al observar la gráfica, el diagrama de caja adopta la forma de una única línea vertical anclada en el cero en lugar de su estructura tradicional. Esto no es un error visual, sino un reflejo fiel de la realidad de nuestra industria: cerca del 99.5% de los incidentes registrados no requirieron evacuaciones, provocando que el valor mínimo, los cuartiles y la mediana colapsen matemáticamente en el valor cero. Lejos de ser un problema estadístico, esta representación gráfica es una excelente prueba de resiliencia operativa que confirma la eficacia de los sistemas de contención y demuestra que, ante eventualidades rutinarias, el riesgo de desplazamiento para la población civil es prácticamente nulo.

9 Tabla indicador local

Los indicadores numéricos locales aportan el rigor matemático necesario para entender la “normalidad” de la operación, cuantificando la tendencia central en el rango de mayor ocurrencia.

library(e1071)
library(knitr)

# 1. Cargar y filtrar los datos (solo del 0 al 4)
datos <- read.csv("database-_1_.csv")
variable_raw <- na.omit(datos$Public.Evacuations)
variable_analisis <- variable_raw[variable_raw >= 0 & variable_raw <= 4]
n <- length(variable_analisis)

# 2. Configurar los escalones agrupados (5 valores: 0, 1, 2, 3 y 4)
k_local <- 5
Li <- 0:4
MC <- Li # Al ser números enteros, la Marca de Clase es el número exacto

# Conteo de frecuencias exactas para el rango
ni <- numeric(k_local)
for(i in 1:k_local){
  ni[i] <- sum(variable_analisis == Li[i])
}

# 3. Cálculos Estadísticos
media_agrupada <- sum(MC * ni) / sum(ni)
desviacion_estandar <- sd(variable_analisis)

# Intervalo de Confianza (95%)
error_estandar <- desviacion_estandar / sqrt(n)
margen_error <- 1.96 * error_estandar
ic_inferior <- media_agrupada - margen_error
ic_superior <- media_agrupada + margen_error
texto_media_intervalo <- paste0("[", format(round(ic_inferior, 4), big.mark=","), " - ", format(round(ic_superior, 4), big.mark=","), "]")

# Otras medidas
ri <- min(variable_analisis)
rs <- max(variable_analisis)
mediana <- median(variable_analisis)
t <- table(variable_analisis)
Mo <- as.numeric(names(t)[which.max(t)])
cv <- if (media_agrupada > 0) (desviacion_estandar / media_agrupada) * 100 else 0
As <- skewness(variable_analisis)
K <- kurtosis(variable_analisis)

# 4. Crear la Tabla
Tabla_local <- data.frame(
  "Evacuaciones (0-4)",
  format(ri, big.mark=","),
  format(rs, big.mark=","),
  texto_media_intervalo,  
  format(round(mediana, 2), big.mark=","),
  format(round(Mo, 2), big.mark=","),
  format(round(desviacion_estandar, 4), big.mark=","),
  paste(round(cv, 2), "%"),
  round(As, 2),
  round(K, 2)
)

colnames(Tabla_local) <- c("Variable","Min","Max","Media (IC 95%)","Mediana","Moda","Desv. S","CV","As","K")

# Imprimir la tabla en Markdown
kable(Tabla_local, format = "markdown", align = "c", caption = "Tabla 1: Indicadores Locales de Evacuaciones (Rango del 0 al 4).")
Tabla 1: Indicadores Locales de Evacuaciones (Rango del 0 al 4).
Variable Min Max Media (IC 95%) Mediana Moda Desv. S CV As K
Evacuaciones (0-4) 0 4 [0.0041 - 0.0176] 0 0 0.1653 1519.01 % 18.41 378.99

10 Tabla indicador global

Al contrastar las métricas locales frente a las globales, podemos medir con exactitud cómo los valores extremos inflan la desviación estándar y el coeficiente de asimetría.

library(e1071)
library(knitr)

# 1. Cargar todos los datos (sin filtros de límite)
datos <- read.csv("database-_1_.csv")
variable_analisis <- na.omit(datos$Public.Evacuations)
n <- length(variable_analisis)

# 2. Configurar escalones mediante la Regla de Sturges (Global)
k_global <- floor(1 + 3.322 * log10(n))
R <- max(variable_analisis) - min(variable_analisis)
A_global <- R / k_global
Li <- seq(min(variable_analisis), max(variable_analisis) - A_global, length.out = k_global)

# Ajuste por si el último límite no cubre el máximo
if(max(Li) + A_global < max(variable_analisis)) { 
   Li <- c(Li, tail(Li, 1) + A_global) 
   k_global <- k_global + 1
}
Ls <- Li + A_global
MC <- (Li + Ls) / 2

# Conteo de frecuencias para cada clase global
ni <- numeric(length(MC))
for(i in 1:length(MC)){
  if(i == length(MC)){
    ni[i] <- sum(variable_analisis >= Li[i] & variable_analisis <= (max(variable_analisis) + 0.001))
  } else {
    ni[i] <- sum(variable_analisis >= Li[i] & variable_analisis < Ls[i])
  }
}

# 3. Cálculos Estadísticos Globales
media_agrupada <- sum(MC * ni) / sum(ni)
desviacion_estandar <- sd(variable_analisis)

# Intervalo de Confianza (95%)
error_estandar <- desviacion_estandar / sqrt(n)
margen_error <- 1.96 * error_estandar
ic_inferior <- media_agrupada - margen_error
ic_superior <- media_agrupada + margen_error
texto_media_intervalo <- paste0("[", format(round(ic_inferior, 2), big.mark=","), " - ", format(round(ic_superior, 2), big.mark=","), "]")

# Otras medidas globales
ri <- min(variable_analisis)
rs <- max(variable_analisis)
mediana <- median(variable_analisis)
t <- table(variable_analisis)
Mo <- as.numeric(names(t)[which.max(t)])
cv <- if(media_agrupada > 0) (desviacion_estandar / media_agrupada) * 100 else 0
As <- skewness(variable_analisis)
K <- kurtosis(variable_analisis)

# 4. Crear la Tabla
Tabla_global <- data.frame(
  "Evacuaciones (Global)",
  format(ri, big.mark=","),
  format(rs, big.mark=","),
  texto_media_intervalo, 
  format(round(mediana, 2), big.mark=","),
  format(round(Mo, 2), big.mark=","),
  format(round(desviacion_estandar, 2), big.mark=","),
  paste(round(cv, 2), "%"),
  format(round(As, 2), big.mark=","),
  format(round(K, 2), big.mark=",")
)

colnames(Tabla_global) <- c("Variable","Min","Max","Media (IC 95%)","Mediana","Moda","Desv. S","CV","As","K")

# Imprimir la tabla en Markdown
kable(Tabla_global, format = "markdown", align = "c", caption = "Tabla 2: Indicadores Globales de Evacuaciones (Incluyendo Valores Atípicos).")
Tabla 2: Indicadores Globales de Evacuaciones (Incluyendo Valores Atípicos).
Variable Min Max Media (IC 95%) Mediana Moda Desv. S CV As K
Evacuaciones (Global) 0 700 [29.08 - 30.55] 0 0 18.15 60.88 % 32.2 1,131.01

11 Valores Atípicos

Esta sección se enfoca en los ‘Cisnes Negros’ del dataset: eventos de baja frecuencia pero impacto catastrófico. Identificar los valores atípicos permite aislar aquellos fallos críticos de seguridad donde la evacuación superó los rangos normales, siendo esta la información más valiosa para el diseño de planes de contingencia y gestión de crisis.

# 1. Cargar la totalidad de los datos
datos <- read.csv("database-_1_.csv")
variable_global <- na.omit(datos$Public.Evacuations)
variable_global <- variable_global[variable_global >= 0] # Asegurar valores válidos

# 2. Calcular valores atípicos usando la estadística de boxplot (Global)
stats_outliers_global <- boxplot.stats(variable_global)$out

# 3. Contar y obtener los extremos catastróficos
num_outliers_global <- length(stats_outliers_global)
minimooutliers_global <- if(num_outliers_global > 0) min(stats_outliers_global) else NA
maximooutliers_global <- if(num_outliers_global > 0) max(stats_outliers_global) else NA

# 4. Imprimir resultados en el reporte
cat("\n--- Análisis de Outliers de Evacuaciones Públicas ---\n")
## 
## --- Análisis de Outliers de Evacuaciones Públicas ---
cat("Número total de valores atípicos detectados:", num_outliers_global, "\n")
## Número total de valores atípicos detectados: 53
if(num_outliers_global > 0){
  cat("Outlier más bajo (Inicio de anomalía):", minimooutliers_global, "persona(s)\n")
  cat("Outlier más alto (Evento catastrófico):", maximooutliers_global, "persona(s)\n")
  cat("\nNota: La existencia de un valor máximo de", maximooutliers_global, "personas indica un evento de\nextrema gravedad que se aleja totalmente del comportamiento típico de la industria.")
} else {
  cat("No existen valores atípicos en la base de datos.")
}
## Outlier más bajo (Inicio de anomalía): 1 persona(s)
## Outlier más alto (Evento catastrófico): 700 persona(s)
## 
## Nota: La existencia de un valor máximo de 700 personas indica un evento de
## extrema gravedad que se aleja totalmente del comportamiento típico de la industria.

12 Conclusión

El análisis de Public Evacuations muestra una asimetría extrema: el 99.5% de los casos se concentran en el valor cero, lo que colapsa la caja estadística y demuestra que la operación es habitualmente segura para la población. No obstante, la existencia de 53 valores atípicos de hasta 700 evacuados revela que, aunque las fallas son improbables, su impacto no es lineal sino exponencial. En conclusión, la seguridad civil no depende de la gestión de incidentes promedio, sino de la prevención estratégica de estos eventos catastróficos o Black Swans, capaces de alterar drásticamente la estabilidad operativa.