1. CARGAR LIBRERÍAS

library(readr)
library(dplyr)
library(caret)
library(randomForest)
library(ggplot2)
library(showtext)
library(sysfonts)
library(kableExtra) 

font_add_google("Lora", "Lora")
showtext_auto()

2. CARGAR DATA SET

datos_originales <- readr::read_csv2(
  "C:/Users/cordo/OneDrive/Desktop/ESTADISITCA/Oil__Gas____Other_Regulated_Wells__Beginning_1860.csv",
  locale = readr::locale(encoding = "Latin1")
)

head(datos_originales)
## # A tibble: 6 × 55
##   `API Well Number` `County Code` `API Hole Number` Sidetrack Completion
##               <dbl>         <dbl>             <dbl>     <dbl>      <dbl>
## 1           3.10e15             3              2670         0          0
## 2           3.10e15             3              4599         0          0
## 3           3.10e15             3              4842         0          0
## 4           3.10e15             3              5419         0          0
## 5           3.11e15           101             26525         0          0
## 6           3.11e15           121             23269         0          0
## # ℹ 50 more variables: `Well Name` <chr>, `Company Name` <chr>,
## #   `Operator Number` <dbl>, `Well Type` <chr>, `Map Symbol` <chr>,
## #   `Well Status` <chr>, `Status Date` <chr>, `Permit Application Date` <chr>,
## #   `Permit Issued Date` <chr>, `Date Spudded` <chr>,
## #   `Date of Total Depth` <chr>, `Completion Decade` <chr>,
## #   `Completion Year` <chr>, `Completion Month` <chr>, `Completion Day` <chr>,
## #   `Date Well Plugged` <chr>, `Date Well Confidentiality Ends` <chr>, …

3. PROCESAMIENTO DE DATOS

En esta etapa se realizó el preprocesamiento de los datos mediante la selección de las variables de interés, la eliminación de registros con valores faltantes, el renombrado de las columnas y la conversión de las variables categóricas al tipo factor para su utilización en el modelo Random Forest.

datos <- datos_originales %>%
  select(
    `True Vertical Depth, ft`,
    `Proposed Depth, ft`,
    `Elevation, ft`,
    `Surface Longitude`,
    `Surface Latitude`,
    County,
    Region,
    `Well Type`,
    `Original Well Type`,
    `Objective Formation`,
    `Producing Formation`,
    Slant,
    `Spacing Acres`
  ) %>%
  na.omit()

# Renombrar variables
colnames(datos) <- c(
  "TrueVerticalDepth",
  "ProposedDepth",
  "Elevation",
  "SurfaceLongitude",
  "SurfaceLatitude",
  "County",
  "Region",
  "WellType",
  "OriginalWellType",
  "ObjectiveFormation",
  "ProducingFormation",
  "Slant",
  "SpacingAcres"
)

# Convertir variables categóricas
datos$County <- as.factor(datos$County)
datos$Region <- as.factor(datos$Region)
datos$WellType <- as.factor(datos$WellType)
datos$OriginalWellType <- as.factor(datos$OriginalWellType)
datos$ObjectiveFormation <- as.factor(datos$ObjectiveFormation)
datos$ProducingFormation <- as.factor(datos$ProducingFormation)
datos$Slant <- as.factor(datos$Slant)

Se seleccionó como variable objetivo la Profundidad Vertical Verdadera (True Vertical Depth), debido a que es la variable que se desea predecir. Las demás variables fueron utilizadas como variables predictoras, ya que contienen información geográfica, geológica y operativa relevante para la estimación de la profundidad del pozo.

tabla_variables <- data.frame(
  `Tipo de variable` = c(
    "Objetivo",
    rep("Predictora", 12)
  ),
  Variable = c(
    "True Vertical Depth",
    "Proposed Depth",
    "Elevation",
    "Surface Longitude",
    "Surface Latitude",
    "County",
    "Region",
    "Well Type",
    "Original Well Type",
    "Objective Formation",
    "Producing Formation",
    "Slant",
    "Spacing Acres"
  ),
  check.names = FALSE
)

# Tabla 1 
kbl(tabla_variables, caption = "Variables utilizadas para el entrenamiento del modelo", align = "ll") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  ) %>%
  row_spec(1, bold = TRUE, color = "white", background = "#2c3e50") %>% 
  column_spec(1, bold = TRUE, width = "12em") %>%
  column_spec(2, width = "15em")
Variables utilizadas para el entrenamiento del modelo
Tipo de variable Variable
Objetivo True Vertical Depth
Predictora Proposed Depth
Predictora Elevation
Predictora Surface Longitude
Predictora Surface Latitude
Predictora County
Predictora Region
Predictora Well Type
Predictora Original Well Type
Predictora Objective Formation
Predictora Producing Formation
Predictora Slant
Predictora Spacing Acres

4. DIVISIÓN DE DATOS

Para evaluar el desempeño del modelo, el conjunto de datos se divide en dos grupos:

  • 70 % para entrenamiento.
  • 30 % para prueba.

Esta metodología permite comprobar la capacidad del modelo para realizar predicciones sobre datos que no fueron utilizados durante el entrenamiento.

set.seed(123)

indice <- createDataPartition(
  datos$TrueVerticalDepth,
  p = 0.70,
  list = FALSE
)

train <- datos[indice, ]
test <- datos[-indice, ]

tabla_division <- data.frame(
  Conjunto = c("Entrenamiento", "Prueba"),
  `Número de registros` = c(nrow(train), nrow(test)),
  Porcentaje = c("70 %", "30 %"),
  check.names = FALSE
)

# Tabla 2 
kbl(tabla_division, align = "lcc") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2c3e50") %>%
  column_spec(1, bold = TRUE, width = "12em")
Conjunto Número de registros Porcentaje
Entrenamiento 240 70 %
Prueba 100 30 %

5. CONSTRUCCIÓN DEL MODELO

La predicción realizada por un modelo Random Forest para problemas de regresión se obtiene mediante el promedio de las predicciones generadas por todos los árboles del bosque:

\[\hat{y}=\frac{1}{B}\sum_{b=1}^{B}T_b(x)\]

donde:

  • \(\hat{y}\) es la profundidad vertical verdadera predicha.
  • \(B\) representa el número de árboles del bosque (500 en este estudio).
  • \(T_b\) corresponds a la predicción realizada por el árbol \(b\).
  • \(x\) representa las variables predictoras utilizadas por el modelo.

6. ENTRENAMIENTO DEL MODELO

Durante el entrenamiento se emplearon 500 árboles de decisión, ya que este número proporciona un equilibrio adecuado entre la precisión del modelo y el tiempo de procesamiento.

modelo_rf <- randomForest(
  TrueVerticalDepth ~ .,
  data = train,
  ntree = 500,
  importance = TRUE
)

print(modelo_rf)
## 
## Call:
##  randomForest(formula = TrueVerticalDepth ~ ., data = train, ntree = 500,      importance = TRUE) 
##                Type of random forest: regression
##                      Number of trees: 500
## No. of variables tried at each split: 4
## 
##           Mean of squared residuals: 291382.6
##                     % Var explained: 96.65

7. PREDICCIONES

Una vez entrenado el modelo, se realizaron las predicciones utilizando el conjunto de prueba. En la siguiente tabla se presentan las primeras diez predicciones obtenidas y sus respectivos valores reales.

predicciones <- predict(
  modelo_rf,
  newdata = test
)

comparacion <- data.frame(
  `Valor real` = test$TrueVerticalDepth,
  `Valor predicho` = predicciones,
  check.names = FALSE
)

# Tabla 3 
knitr::kable(
  head(comparacion, 10),
  caption = "Primeras diez predicciones realizadas por el modelo"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2c3e50") %>%
  column_spec(1:2, width = "12em")
Primeras diez predicciones realizadas por el modelo
Valor real Valor predicho
1529 3501.066
9152 9380.539
9627 9310.540
10045 9808.960
2467 3680.861
9474 9722.810
4450 4355.293
3763 4726.860
9807 9853.040
7179 8420.280

Comparación de Valores Reales y Predichos

comparacion <- data.frame(
  Real = test$TrueVerticalDepth,
  Predicho = predicciones
)

head(comparacion)
##    Real Predicho
## 1  1529 3501.066
## 2  9152 9380.539
## 3  9627 9310.540
## 4 10045 9808.960
## 5  2467 3680.861
## 6  9474 9722.810

8. EVALUACIÓN DEL MODELO

El desempeño del modelo se evaluó mediante las métricas RMSE, MAE y , las cuales permiten medir el error de predicción y la capacidad del modelo para explicar la variabilidad de los datos.

8.1. Métricas de desempeño

rmse <- RMSE(predicciones, test$TrueVerticalDepth)
mae  <- MAE(predicciones, test$TrueVerticalDepth)
r2   <- R2(predicciones, test$TrueVerticalDepth)

tabla_metricas <- data.frame(
  `Métrica` = c("RMSE (ft)", "MAE (ft)", "R²"),
  Valor = c(rmse, mae, r2),
  check.names = FALSE
)

# Tabla 4 
knitr::kable(
  tabla_metricas,
  digits = 3,
  caption = "Métricas de desempeño del modelo."
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2c3e50") %>%
  column_spec(1, bold = TRUE, width = "12em") %>%
  column_spec(2, width = "10em")
Métricas de desempeño del modelo.
Métrica Valor
RMSE (ft) 552.610
MAE (ft) 332.548
0.969

8.2. Interpretación de resultados

Los resultados obtenidos evidencian un buen desempeño del modelo Random Forest para la predicción de la profundidad vertical verdadera. El coeficiente de determinación (R² = 0.969) indica que el modelo explica aproximadamente el 96.9 % de la variabilidad de los datos. Además, los valores de RMSE (552.61 ft) y MAE (332.548 ft) reflejan que el error de predicción es relativamente bajo, lo que demuestra que el modelo genera estimaciones precisas y confiables para el conjunto de datos analizado.

9. VISUALIZACIÓN DE RESULTADOS

9.1. Importancia de las Variables Predictoras

La importancia de las variables predictoras se evaluó mediante el indicador %IncMSE, el cual mide cuánto aumenta el error del modelo cuando una variable es alterada. Un valor más alto indica una mayor contribución de la variable a la capacidad predictiva del modelo.

# Cálculo de la importancia de las variables
importancia <- importance(modelo_rf)

# Tabla de importancia
tabla_importancia <- data.frame(
  Variable = rownames(importancia),
  `%IncMSE` = importancia[,1],
  check.names = FALSE,
  row.names = NULL
)

# Ordenar de mayor a menor importancia
tabla_importancia <- tabla_importancia[
  order(tabla_importancia$`%IncMSE`, decreasing = TRUE),
]

# Tabla 5
knitr::kable(
  tabla_importancia,
  digits = 2,
  row.names = FALSE,
  caption = "Importancia de las variables predictoras según el indicador %IncMSE."
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2c3e50") %>%
  column_spec(1, bold = TRUE, width = "15em") %>%
  column_spec(2, width = "10em")
Importancia de las variables predictoras según el indicador %IncMSE.
Variable %IncMSE
ProposedDepth 28.23
ProducingFormation 15.76
ObjectiveFormation 15.12
County 11.84
SurfaceLatitude 11.30
SurfaceLongitude 8.74
SpacingAcres 8.73
Elevation 8.45
Region 5.92
Slant 4.75
WellType 4.54
OriginalWellType 3.61
# Gráfico de Importancia 
ggplot(
  tabla_importancia,
  aes(
    x = reorder(Variable, `%IncMSE`),
    y = `%IncMSE`
  )
) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  scale_y_continuous(
    breaks = seq(0, 30, by = 5)
  ) +
  labs(
    title = "Importancia de las Variables Predictoras",
    x = "Variables predictoras",
    y = "%IncMSE"
  ) +
  theme_minimal(base_family = "Lora") +
  theme(
    plot.title = element_text(
      face = "bold",
      size = 18,        
      hjust = 0.5
    ),
    axis.title = element_text(
      face = "bold",
      size = 14        
    ),
    axis.text = element_text(
      size = 12        
    ),
    legend.title = element_text(
      face = "bold",
      size = 14
    ),
    legend.text = element_text(
      size = 12
    )
  )

Los resultados muestran que Proposed Depth fue la variable con mayor importancia para la predicción de la profundidad vertical verdadera, seguida por Producing Formation y Objective Formation. En contraste, variables como Original Well Type y Well Type presentaron una menor influencia en el desempeño del modelo.

9.2. Error del Modelo

El siguiente gráfico muestra la evolución del error del modelo conforme aumenta el número de árboles utilizados durante el entrenamiento.

# Datos del error del modelo
error_rf <- data.frame(
  Arboles = 1:modelo_rf$ntree,
  MSE = modelo_rf$mse
)

# Gráfico de Error con tipografía optimizada para publicación
ggplot(error_rf, aes(x = Arboles, y = MSE)) +
  geom_line(color = "steelblue", linewidth = 1) +
  labs(
    title = "Evolución del Error del Modelo",
    x = "Número de árboles de decisión",
    y = "Error cuadrático medio (MSE)"
  ) +
  theme_minimal(base_family = "Lora") +
  theme(
    plot.title = element_text(
      face = "bold",
      size = 18,        
      hjust = 0.5
    ),
    axis.title = element_text(
      face = "bold",
      size = 14        
    ),
    axis.text = element_text(
      size = 12        
    )
  )

El error del modelo disminuye conforme aumenta el número de árboles de decisión y posteriormente tiende a estabilizarse. Este comportamiento indica que el modelo alcanza una convergencia adecuada, por lo que la incorporación de un mayor número de árboles no produce mejoras significativas en el desempeño predictivo.

9.3. Comparación entre Valores Reales y Predichos

El siguiente gráfico compara los valores reales con los valores predichos por el modelo. Mientras más cercanos se encuentren los puntos a la línea roja, mayor será la precisión de las predicciones.

comparacion <- data.frame(
  Real = test$TrueVerticalDepth,
  Predicho = predicciones
)

# Gráfico de Dispersión con tipografía optimizada para publicación
ggplot(
  comparacion,
  aes(x = Real, y = Predicho)
) +
  geom_point(
    color = "steelblue",
    size = 2,
    alpha = 0.7
  ) +
  geom_abline(
    intercept = 0,
    slope = 1,
    color = "red",
    linewidth = 1
  ) +
  labs(
    title = "Comparación entre Valores Reales y Predichos",
    x = "Profundidad Vertical Real (ft)",
    y = "Profundidad Vertical Predicha (ft)"
  ) +
  theme_minimal(base_family = "Lora") +
  theme(
    plot.title = element_text(
      face = "bold",
      size = 18,        
      hjust = 0.5
    ),
    axis.title = element_text(
      face = "bold",
      size = 14       
    ),
    axis.text = element_text(
      size = 12        
    )
  )

Se observa que la mayoría de los valores predichos se encuentran próximos a la línea de referencia, lo que evidencia una alta concordancia entre los valores reales y los estimados por el modelo. Esto confirma la capacidad del algoritmo Random Forest para predecir con precisión la profundidad vertical verdadera.