AVISO IMPORTANTE!

Originalmente, mi variable de estudio seleccionada, el identificador del operador (Operator.ID), era de naturaleza cualitativa nominal, ya que únicamente consistía en un código numérico para etiquetar a la empresa responsable de la infraestructura, sin ningún orden inherente. Para enriquecer mi análisis de seguridad y poder establecer prioridades en la vigilancia regulatoria, decidí realizar una transformación profunda de los datos hacia una variable ordinal. Para ello, apliqué un criterio propio de “Nivel de Incidencia Operativa”, fundamentado en la frecuencia histórica acumulada de accidentes reportados por cada compañía. Se reclasificaron los operadores en tres niveles jerárquicos de impacto: Nivel 1: Operadores Menores Nivel 2: Actores Recurrentes Nivel 3: Gigantes Corporativo

1 Carga de datos

Importamos el archivo “database-1.csv” desde una ruta local y lo almacenamos en el objeto datos.

datos <- read.csv("database-_1_.csv")
zona<-datos$Operator.ID

2 Tabla de frecuencia

En este paso, extraje mi variable Operator.ID, calculé previamente el volumen total de incidentes de cada empresa en la base de datos para categorizarla, omití valores nulos y estructuré la tabla ordinal. Esto me permite revelar si la siniestralidad está dispersa de forma homogénea o si, por el contrario, se concentra en unos pocos actores de gran magnitud.

datos_operadores <- datos %>%
  group_by(Operator.ID) %>%
  mutate(total_accidentes = n()) %>%
  ungroup() %>%
  mutate(Categoria_por_Operador = case_when(
    is.na(Operator.ID) | Operator.ID == "" ~ "DESCONOCIDO",
    total_accidentes > 100 ~ "GIGANTES CORPORATIVOS",
    total_accidentes >= 20 & total_accidentes <= 100 ~ "ACTORES RECURRENTES",
    TRUE ~ "OPERADORES MENORES"
  )) %>%
  filter(Categoria_por_Operador != "DESCONOCIDO") %>%
  mutate(Nivel_Incidencia = case_when(
    Categoria_por_Operador == "OPERADORES MENORES" ~ 1,
    Categoria_por_Operador == "ACTORES RECURRENTES" ~ 2,
    Categoria_por_Operador == "GIGANTES CORPORATIVOS" ~ 3,
    TRUE ~ 0
  ))

TDF_agrupada <- datos_operadores %>%
  count(Nivel_Incidencia, Categoria_por_Operador, name = "ni") %>%
  arrange(desc(Nivel_Incidencia), desc(ni)) 

ni_total <- sum(TDF_agrupada$ni)
TDF_agrupada$hi <- (TDF_agrupada$ni / ni_total) * 100
TDF_agrupada$hi <- sprintf("%.2f", round(TDF_agrupada$hi, 2))

Sumatoria <- data.frame(
  Nivel_Incidencia = "",
  Categoria_por_Operador = "TOTAL",
  ni = ni_total,
  hi = "100.00"
)

TDF_final <- rbind(TDF_agrupada, Sumatoria)
colnames(TDF_final) <- c("Nivel Incidencia", "Categoría por Operador", "ni", "hi (%)")

kable(TDF_final, align = 'c', 
      caption = "Tabla 1: Cantidad de Accidentes por Nivel de Incidencia Operativa") %>%
  kable_styling(full_width = FALSE, position = "center", 
                bootstrap_options = c("striped", "hover", "condensed")) %>%
  row_spec(nrow(TDF_final), bold = TRUE, background = "#f2f2f2") %>%
  row_spec(which(TDF_final$`Nivel Incidencia` == "3"), bold = TRUE)
Tabla 1: Cantidad de Accidentes por Nivel de Incidencia Operativa
Nivel Incidencia Categoría por Operador ni hi (%)
3 GIGANTES CORPORATIVOS 1197 42.83
2 ACTORES RECURRENTES 866 30.98
1 OPERADORES MENORES 732 26.19
TOTAL 2795 100.00

3 Cantidad absoluta local de Accidentes por Categoría por Operador

En mi primera gráfica evidencio de manera absoluta la distribución de los siniestros. Resulta de sumo interés notar cómo el grupo selecto que denominé “Gigantes Corporativos” acumula una proporción enorme de los accidentes totales, confirmando mi hipótesis de que las megacorporaciones, por su gigantesco alcance en kilómetros de ductos, acaparan la mayor carga de reportes absolutos.

datos_grafico <- datos_operadores %>%
  mutate(Nivel_Incidencia_Label = case_when(
    Nivel_Incidencia == 1 ~ "1. Baja",
    Nivel_Incidencia == 2 ~ "2. Media",
    Nivel_Incidencia == 3 ~ "3. Alta"
  )) %>%
  count(Nivel_Incidencia_Label, Categoria_por_Operador, name = "ni")

ggplot(datos_grafico, aes(x = reorder(Categoria_por_Operador, -ni), y = ni, fill = Nivel_Incidencia_Label)) + 
  geom_bar(stat = "identity", width = 0.75, color = "black") + 
  scale_fill_manual(values = c(
    "1. Baja" = "#AED6F1",  
    "2. Media" = "#3498DB",  
    "3. Alta" = "#154360"    
  )) +
  labs(
    title = "Gráfica N1: Distribución absoluta por Incidencia Operativa",
    x = "Categoría por Operador",
    y = "Cantidad de Accidentes",
    fill = "Nivel de Incidencia"
  ) +
  theme_light() +
  theme(
    axis.text.x = element_text(angle = 15, hjust = 1, color = "black"),
    legend.position = "top"
  )

4 Cantidad Absoluta Global de Accidentes por Categoría por Operador

Para obtener una perspectiva global más rigurosa, decidí escalar el eje Y de mi gráfica al tamaño total de mi muestra (2,795 incidentes). Esta visualización me permite dimensionar el peso real que tiene cada categoría frente a la totalidad de fallas históricas. Aquí se aprecia visualmente que ningún nivel por sí solo alcanza a cubrir el total del panorama, demostrando que la accidentabilidad es un fenómeno fragmentado entre los Gigantes Corporativos y la suma masiva de Operadores Menores.

datos_grafico_global <- datos_operadores %>%
  mutate(Nivel_Incidencia_Label = case_when(
    Nivel_Incidencia == 1 ~ "1. Baja",
    Nivel_Incidencia == 2 ~ "2. Media",
    Nivel_Incidencia == 3 ~ "3. Alta"
  )) %>%
  count(Nivel_Incidencia_Label, Categoria_por_Operador, name = "ni")

ggplot(datos_grafico_global, aes(x = reorder(Categoria_por_Operador, -ni), y = ni, fill = Nivel_Incidencia_Label)) + 
  geom_bar(stat = "identity", width = 0.7, color = "black") + 
  scale_fill_manual(values = c(
    "1. Baja" = "#AED6F1",   
    "2. Media" = "#3498DB",  
    "3. Alta" = "#154360"    
  )) +
  scale_y_continuous(
    limits = c(0, 2795),        
    breaks = seq(0, 2795, 500)  
  ) +
  labs(
    title = "Gráfica N2: Distribución global de Accidentes por Incidencia Operativa",
    x = "Categoría por Operador",
    y = "Cantidad",
    fill = "Nivel de Incidencia"
  ) +
  theme_light() +
  theme(
    axis.text.x = element_text(angle = 15, hjust = 1, color = "black"),
    legend.position = "top"
  )

5 Cantidad relativa global de Accidentes por Categoría por Operador

Al convertir mis valores a proporciones porcentuales, el análisis relativo demuestra que los Gigantes Corporativos dominan más de un tercio del universo de fallos. No obstante, el hecho de que muchísimos Operadores Menores generen otra tajada tan significativa ilustra un hallazgo clave de mi estudio: la siniestralidad no depende únicamente de tener una red grande, sino que también delata las carencias estructurales o el mantenimiento insuficiente de las empresas más pequeñas.

datos_hi <- datos_operadores %>%
  mutate(Nivel_Incidencia_Label = case_when(
    Nivel_Incidencia == 1 ~ "1. Baja",
    Nivel_Incidencia == 2 ~ "2. Media",
    Nivel_Incidencia == 3 ~ "3. Alta"
  )) %>%
  count(Nivel_Incidencia_Label, Categoria_por_Operador, name = "ni") %>%
  mutate(hi_pct = (ni / sum(ni)) * 100)

ggplot(datos_hi, aes(x = reorder(Categoria_por_Operador, -hi_pct), y = hi_pct, fill = Nivel_Incidencia_Label)) +
  geom_bar(stat = "identity", width = 0.7, color = "black") +
  scale_fill_manual(values = c(
    "1. Baja" = "#AED6F1",
    "2. Media" = "#3498DB",  
    "3. Alta" = "#154360"    
  )) +
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, by = 20)) +
  labs(
    title = "Gráfica N3: Porcentaje de Accidentes por Incidencia Operativa",
    x = "Categoría por Operador",
    y = "Porcentaje (%)",
    fill = "Nivel de Incidencia"
  ) +
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 15, hjust = 1, color = "black"),
    legend.position = "top"
  )

6 Cantidad relativa local de Accidentes por Categoría por Operador

Si aislo la escala para enfocarme estrictamente en la distribución local, puedo confirmar nuevamente que la brecha entre los Gigantes Corporativos y los Operadores Menores es mínima en términos de responsabilidad porcentual de accidentes. Ambas fuerzas moldean casi en simetría las estadísticas de riesgo operativo.

datos_hi_local <- datos_operadores %>%
  mutate(Nivel_Incidencia_Label = case_when(
    Nivel_Incidencia == 1 ~ "1. Baja",
    Nivel_Incidencia == 2 ~ "2. Media",
    Nivel_Incidencia == 3 ~ "3. Alta"
  )) %>%
  count(Nivel_Incidencia_Label, Categoria_por_Operador, name = "ni") %>%
  mutate(hi_pct = (ni / sum(ni)) * 100)

ggplot(datos_hi_local, aes(x = reorder(Categoria_por_Operador, -hi_pct), y = hi_pct, fill = Nivel_Incidencia_Label)) +
  geom_bar(stat = "identity", width = 0.7, color = "black") +
  scale_fill_manual(values = c(
    "1. Baja" = "#AED6F1",
    "2. Media" = "#3498DB",  
    "3. Alta" = "#154360"    
  )) +
  labs(
    title = "Gráfica N4: Porcentaje local de Accidentes por Incidencia Operativa",
    x = "Categoría por Operador",
    y = "Porcentaje (%)",
    fill = "Nivel de Incidencia"
  ) +
  theme_classic() +
  theme(
    axis.text.x = element_text(angle = 15, hjust = 1, color = "black"),
    legend.position = "top"
  )

7 Diagrama Circular

Mi diagrama circular provee una representación cristalina del Principio de Pareto encubierto en mi base de datos: un porcentaje minúsculo de corporaciones altamente activas ocupa una porción gigante del pastel de accidentes, compartiendo la otra gran mitad con la suma acumulada de cientos de Operadores Menores.

datos_pastel <- datos_operadores %>%
  count(Categoria_por_Operador, name = "ni") %>%
  mutate(hi_pct = round((ni / sum(ni)) * 100, 1)) %>%
  mutate(
    Categoria_por_Operador = factor(Categoria_por_Operador, levels = c(
      "OPERADORES MENORES", "ACTORES RECURRENTES", "GIGANTES CORPORATIVOS"
    )),
    Nivel_Incidencia_Label = case_when(
      Categoria_por_Operador == "GIGANTES CORPORATIVOS" ~ "Alta",
      Categoria_por_Operador == "ACTORES RECURRENTES" ~ "Media",
      TRUE ~ "Baja"
    ),
    Nivel_Incidencia_Label = factor(Nivel_Incidencia_Label, levels = c("Alta", "Media", "Baja"))
  )

colores_operador <- c(
  "GIGANTES CORPORATIVOS" = "#154360",
  "ACTORES RECURRENTES" = "#3498DB",
  "OPERADORES MENORES" = "#AED6F1"
)

colores_niveles <- c(
  "Alta" = "#154360",
  "Media" = "#3498DB",
  "Baja" = "#AED6F1"
)

ggplot(datos_pastel, aes(x = "", y = hi_pct, fill = Categoria_por_Operador)) +
  geom_bar(stat = "identity", width = 1, color = "white", size = 1) +
  geom_point(aes(color = Nivel_Incidencia_Label), alpha = 0, size = 0) +
  coord_polar("y", start = 0) +
  geom_text(aes(label = paste0(hi_pct, "%")), 
            position = position_stack(vjust = 0.5), 
            color = "white", size = 4, fontface = "bold") +
  scale_fill_manual(values = colores_operador) +
  scale_color_manual(values = colores_niveles) + 
  labs(
    title = "Gráfica N5: Distribución Circular de Accidentes",
    fill = "Categoría por Operador",
    color = "Nivel de Incidencia" 
  ) +
  guides(
    fill = guide_legend(order = 1),
    color = guide_legend(order = 2, override.aes = list(alpha = 1, size = 5, shape = 15))
  ) +
  theme_void() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", margin = margin(b = 10)),
    legend.position = "right", 
    legend.text = element_text(size = 9),
    legend.title = element_text(face = "bold", size = 10)
  )
## Warning in geom_bar(stat = "identity", width = 1, color = "white", size = 1):
## Ignoring unknown parameters: `size`

8 Indicadores Estadísticos

En esta sección, calculé mis indicadores estadísticos sobre el comportamiento ordinal para resumir numéricamente hacia dónde se inclina el peso de las compañías en el escenario total de siniestros.

media_incidencia <- mean(datos_operadores$Nivel_Incidencia, na.rm = TRUE)
mediana_incidencia <- median(datos_operadores$Nivel_Incidencia, na.rm = TRUE)

frecuencias_incidencia <- TDF_agrupada %>%
  group_by(Nivel_Incidencia) %>%
  summarise(Total_ni = sum(ni)) %>%
  arrange(desc(Total_ni))

moda_incidencia <- frecuencias_incidencia$Nivel_Incidencia[1]

tabla_tendencia <- data.frame(
  Estadístico = c("Media", "Mediana", "Moda"),
  Valor = c(round(media_incidencia, 2), mediana_incidencia, moda_incidencia),
  Interpretación = c(
    "Promedio del nivel de incidencia corporativa (Escala 1 al 3)",
    "Punto central (el 50% de los accidentes pertenecen a este nivel o uno menor)",
    "Nivel de incidencia operativa que genera la mayor frecuencia absoluta de accidentes"
  )
)

kable(tabla_tendencia, align = c('l', 'c', 'l'), 
      caption = "Tabla N2: Medidas de Tendencia Central (Nivel Operativo)") %>%
  kable_styling(full_width = FALSE, position = "center", 
                bootstrap_options = c("striped", "hover", "condensed")) %>%
  row_spec(1:3, bold = TRUE, background = "#ffeeba")
Tabla N2: Medidas de Tendencia Central (Nivel Operativo)
Estadístico Valor Interpretación
Media 2.17 Promedio del nivel de incidencia corporativa (Escala 1 al 3)
Mediana 2.00 Punto central (el 50% de los accidentes pertenecen a este nivel o uno menor)
Moda 3.00 Nivel de incidencia operativa que genera la mayor frecuencia absoluta de accidentes

9 Conclusión

El análisis de mi variable Operator.ID revela un mercado fuertemente polarizado: unos pocos Gigantes Corporativos acumulan la misma cantidad de fallas que cientos de Operadores Menores juntos. Por ello, concluyo que se requiere un doble enfoque de auditoría: fiscalización estricta y constante para las grandes infraestructuras, acompañada de inspecciones y capacitación para fortalecer el débil músculo preventivo de las empresas más pequeñas.