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>, …
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")| 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 |
Para evaluar el desempeño del modelo, el conjunto de datos se divide en dos grupos:
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 % |
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:
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
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")| 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
El desempeño del modelo se evaluó mediante las métricas RMSE, MAE y R², las cuales permiten medir el error de predicción y la capacidad del modelo para explicar la variabilidad de los datos.
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étrica | Valor |
|---|---|
| RMSE (ft) | 552.610 |
| MAE (ft) | 332.548 |
| R² | 0.969 |
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.
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")| 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.
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.
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.