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", header = TRUE, sep = ",", dec = ".", check.names = FALSE)

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.

library(dplyr)
library(knitr)
library(kableExtra)

# 1. Preparar datos y agrupar 
datos_operadores <- datos %>%
  mutate(Operator.ID = `Operator ID`) %>%
  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)) 

TDF_agrupada$hi_exacto <- (TDF_agrupada$ni / sum(TDF_agrupada$ni)) * 100
TDF_agrupada$decimal_exacto <- TDF_agrupada$hi_exacto / 100

TDF_agrupada$hi <- round(TDF_agrupada$hi_exacto, 2)
TDF_agrupada$decimal <- round(TDF_agrupada$decimal_exacto, 3)
dif_hi <- 100 - sum(TDF_agrupada$hi)
dif_dec <- 1 - sum(TDF_agrupada$decimal)

idx_max <- which.max(TDF_agrupada$ni)
TDF_agrupada$hi[idx_max] <- TDF_agrupada$hi[idx_max] + dif_hi
TDF_agrupada$decimal[idx_max] <- TDF_agrupada$decimal[idx_max] + dif_dec
TDF_agrupada$Nivel_Incidencia <- as.character(TDF_agrupada$Nivel_Incidencia)

Sumatoria <- data.frame(
  Nivel_Incidencia = "",
  Categoria_por_Operador = "Total", 
  ni = sum(TDF_agrupada$ni),
  hi = sum(TDF_agrupada$hi),
  decimal = sum(TDF_agrupada$decimal)
)
TDF_final <- rbind(TDF_agrupada[, c("Nivel_Incidencia", "Categoria_por_Operador", "ni", "hi", "decimal")], Sumatoria)
colnames(TDF_final) <- c("Nivel de incidencia", "Categoría por operador", "ni", "hi (%)", "fi")
titulo_formal <- "CUADRO N°1 <br/> Distribución de frecuencias de accidentes según el nivel de incidencia del operador de ductos en Estados Unidos, [2010-2016]"
kable(TDF_final, align = 'c', 
      digits = c(0, 0, 0, 2, 3)) %>%
  kable_styling(full_width = FALSE, position = "center", 
                bootstrap_options = c("striped", "hover", "condensed", "bordered")) %>%
  add_header_above(c(" " = 3, "Frecuencia relativa" = 2), bold = TRUE, background = "#D5D8DC") %>%
  add_header_above(setNames(5, titulo_formal), align = "center", escape = FALSE, bold = FALSE, background = "white") %>%
  row_spec(0, bold = TRUE) %>%
  row_spec(nrow(TDF_final), bold = TRUE, background = "#f2f2f2") %>%
  row_spec(which(TDF_final$`Nivel de incidencia` == "3"), bold = TRUE)
CUADRO N°1
Distribución de frecuencias de accidentes según el nivel de incidencia del operador de ductos en Estados Unidos, [2010-2016]
Frecuencia relativa
Nivel de incidencia Categoría por operador ni hi (%) fi
3 GIGANTES CORPORATIVOS 1197 42.83 0.428
2 ACTORES RECURRENTES 866 30.98 0.310
1 OPERADORES MENORES 732 26.19 0.262
Total 2795 100.00 1.000

3 Cantidad absoluta por Operadores de Ductos

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 por Operadores de Ductos

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 por Operadores de Ductos

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 de relativa por Operadores de Ductos

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"
  )

# 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`

7 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.

library(knitr)
library(kableExtra)

# 1. Creamos el dataframe solo con la variable solicitada
tabla_categorias <- data.frame(
  Variable = "Operadores de Ductos",
  Media = "N/A",
  Mediana = "N/A",
  Moda = "GIGANTES CORPORATIVOS"
)

# 2. Renderizamos con el diseño clásico solicitado
kable(tabla_categorias, 
      align = c('l', 'c', 'c', 'c'), 
      caption = "Tabla 2: Categorías de Operadores según Indicadores Estadísticos") %>%
  kable_classic(full_width = FALSE, html_font = "Georgia") %>%
  row_spec(0, bold = TRUE, color = "#6c757d") %>%
  column_spec(1, color = "#6c757d")
Tabla 2: Categorías de Operadores según Indicadores Estadísticos
Variable Media Mediana Moda
Operadores de Ductos N/A N/A GIGANTES CORPORATIVOS

AVISO IMPORANTE!

Al categorizar los operadores en niveles de riesgo, nuestra variable resultante es cualitativa ordinal en formato de texto. Estadísticamente, no es posible calcular promedios ni medianas aritméticas sobre etiquetas categóricas, ya que carecen de distancias numéricas equivalentes. Por lo tanto, la única medida de tendencia central representativa y matemáticamente correcta para este análisis descriptivo es la Moda, indicando que el grupo con mayor frecuencia de accidentes recae en la categoría que vemos en la tabla (“GIGANTES CORPORATIVOS”).

8 Conclusión

La variable “Operador de ductos”, presenta como valor más frecuente es gigantes corporativos, con una participación destacada en la muestra.