datos_originales <- read_csv2("Oil__Gas____Other_Regulated_Wells__Beginning_1860.csv")
dims <- dim(datos_originales)
cat("Dimensiones del dataset original:", dims[1], "filas y", dims[2], "columnas.\n")## Dimensiones del dataset original: 47407 filas y 55 columnas.
datatable(
head(datos_originales, 50),
caption = htmltools::tags$caption(
style = 'caption-side: top; text-align: center; font-weight: bold;',
'Tabla 1: Visualización interactiva de las primeras observaciones del dataset original'
),
options = list(
pageLength = 5,
autoWidth = TRUE,
scrollX = TRUE,
language = list(url = '//cdn.datatables.net/plug-ins/1.10.25/i18n/Spanish.json')
),
class = 'display compact cell-border'
)# Seleccionamos las variables predictoras y definimos explícitamente la variable objetivo de clasificación
variables_requeridas <- c(
"Well Type", # <-- VARIABLE OBJETIVO DE CLASIFICACIÓN
"True Vertical Depth, ft",
"Proposed Depth",
"Elevation",
"Surface Longitude",
"Surface Latitude",
"County",
"Region",
"Original Well Type",
"Objective Formation",
"Producing Formation",
"Slant",
"Spacing Acres"
)
datos_proc <- datos_originales %>%
select(any_of(variables_requeridas))
names(datos_proc) <- c(
"Well_Type",
"True_Vertical_Depth",
"Proposed_Depth",
"Elevation",
"Surface_Longitude",
"Surface_Latitude",
"County",
"Region",
"Original_Well_Type",
"Objective_Formation",
"Producing_Formation",
"Slant",
"Spacing_Acres"
)
datos_proc <- datos_proc %>% distinct()
# Conversión de variables numéricas predictoras
numeric_vars <- c("True_Vertical_Depth", "Proposed_Depth", "Elevation", "Surface_Longitude", "Surface_Latitude", "Spacing_Acres")
for(v in numeric_vars) {
if(v %in% names(datos_proc)) {
datos_proc[[v]] <- as.numeric(as.character(datos_proc[[v]]))
}
}
# Conversión de variables categóricas
cat_vars <- c("County", "Region", "Original_Well_Type", "Objective_Formation", "Producing_Formation", "Slant")
for(v in cat_vars) {
if(v %in% names(datos_proc)) {
datos_proc[[v]] <- addNA(as.factor(datos_proc[[v]]))
levels(datos_proc[[v]])[is.na(levels(datos_proc[[v]]))] <- "Desconocido"
}
}
# Filtrado de nulos en la variable objetivo de clasificación
datos_limpios <- datos_proc %>%
filter(!is.na(Well_Type))
# Imputación de valores faltantes en variables numéricas
for(v in numeric_vars) {
if(v %in% names(datos_limpios) && sum(is.na(datos_limpios[[v]])) > 0) {
datos_limpios[[v]][is.na(datos_limpios[[v]])] <- median(datos_limpios[[v]], na.rm = TRUE)
}
}
# Saneamiento y conversión de la variable objetivo a factor válido para clasificación
datos_limpios$Well_Type <- make.names(datos_limpios$Well_Type)
datos_limpios$Well_Type <- as.factor(datos_limpios$Well_Type)
cat("Dimensiones finales del dataset limpio:", nrow(datos_limpios), "filas y", ncol(datos_limpios), "columnas.\n")## Dimensiones finales del dataset limpio: 46370 filas y 11 columnas.
set.seed(123)
if(nrow(datos_limpios) > 10) {
train_index <- createDataPartition(datos_limpios$Well_Type, p = 0.70, list = FALSE)
train_set <- datos_limpios[train_index, ]
test_set <- datos_limpios[-train_index, ]
} else {
train_index <- sample(1:nrow(datos_limpios), size = 0.7 * nrow(datos_limpios))
train_set <- datos_limpios[train_index, ]
test_set <- datos_limpios[-train_index, ]
}
partition_summary <- data.frame(
Conjunto = c("Entrenamiento (Train)", "Prueba (Test)", "Total"),
Observaciones = c(nrow(train_set), nrow(test_set), nrow(datos_limpios)),
Porcentaje = c("70%", "30%", "100%")
)
partition_summary %>%
tabla_formato("Tabla 2: Resumen de la división estratificada del dataset")| Conjunto | Observaciones | Porcentaje |
|---|---|---|
| Entrenamiento (Train) | 32469 | 70% |
| Prueba (Test) | 13901 | 30% |
| Total | 46370 | 100% |
set.seed(123)
p_preds <- ncol(train_set) - 1
mtry_opt <- floor(sqrt(p_preds)) # Valor óptimo estándar para clasificación
rf_model_ranger <- ranger(
formula = Well_Type ~ .,
data = train_set,
num.trees = 500,
mtry = mtry_opt,
importance = "impurity",
probability = FALSE,
num.threads = 0, # Utiliza todos los núcleos disponibles para acelerar entrenamiento y predicción
seed = 123
)## Ranger result
##
## Call:
## ranger(formula = Well_Type ~ ., data = train_set, num.trees = 500, mtry = mtry_opt, importance = "impurity", probability = FALSE, num.threads = 0, seed = 123)
##
## Type: Classification
## Number of trees: 500
## Sample size: 32469
## Number of independent variables: 10
## Mtry: 3
## Target node size: 1
## Variable importance mode: impurity
## Splitrule: gini
## OOB prediction error: 21.04 %
cat("Tasa de Error OOB (Classification Error):", round(rf_model_ranger$prediction.error * 100, 4), "%\n")## Tasa de Error OOB (Classification Error): 21.0447 %
importance_df <- data.frame(
Variable = names(rf_model_ranger$variable.importance),
IncNodePurity = as.numeric(rf_model_ranger$variable.importance)
) %>% arrange(desc(IncNodePurity))
head(importance_df, 10) %>%
tabla_formato("Tabla 3: Top 10 Variables más influyentes en la Clasificación")| Variable | IncNodePurity |
|---|---|
| Original_Well_Type | 4171.09860 |
| Elevation | 4022.57214 |
| True_Vertical_Depth | 3543.71893 |
| Proposed_Depth | 2840.38178 |
| County | 1005.31169 |
| Region | 994.03113 |
| Surface_Latitude | 542.00678 |
| Producing_Formation | 154.55103 |
| Objective_Formation | 59.32067 |
| Surface_Longitude | 0.00000 |
Se ejecutan las predicciones sobre el conjunto de prueba optimizando el procesamiento y mostrando una vista resumida.
predictions <- predict(rf_model_ranger, data = test_set, num.threads = 0)$predictions
cat("Primeras 10 predicciones generadas:\n")## Primeras 10 predicciones generadas:
## [1] BR GD GD GE SG OD NL DW DW ST
## 20 Levels: BR Confidential DH DS DW GD GE GW IW LP MB MM MS NL OD OE OW ... TH
results_df <- data.frame(
Valor_Real = test_set$Well_Type,
Valor_Predicho = predictions
)
conf_matrix <- confusionMatrix(as.factor(results_df$Valor_Predicho), as.factor(results_df$Valor_Real))
print(conf_matrix)## Confusion Matrix and Statistics
##
## Reference
## Prediction BR Confidential DH DS DW GD GE GW IW LP MB
## BR 217 0 0 0 3 0 0 0 0 0 1
## Confidential 0 0 0 0 0 0 0 0 0 0 0
## DH 2 0 111 1 39 18 1 1 0 0 0
## DS 0 0 0 2 0 0 0 0 0 0 0
## DW 4 0 110 1 395 26 0 18 1 0 0
## GD 0 0 49 4 16 2812 39 83 0 0 0
## GE 0 0 1 0 0 1 48 5 0 0 0
## GW 0 0 3 0 10 4 6 67 0 0 0
## IW 0 0 0 0 0 2 0 0 95 0 0
## LP 0 0 0 0 0 0 0 0 0 4 0
## MB 0 0 0 0 0 0 0 0 0 0 0
## MM 2 0 0 0 0 0 0 0 0 0 0
## MS 0 0 0 0 0 1 0 0 0 0 0
## NL 2 0 46 0 34 86 2 12 104 0 0
## OD 1 3 11 0 4 47 0 4 1149 0 0
## OE 0 0 0 0 0 0 0 0 0 0 0
## OW 0 0 1 0 0 0 0 1 0 0 0
## SG 0 1 0 0 1 0 0 0 0 0 0
## ST 0 0 8 1 5 13 1 4 0 0 0
## TH 0 0 0 0 0 0 0 0 0 0 0
## Reference
## Prediction MM MS NL OD OE OW SG ST TH
## BR 1 0 6 0 0 0 5 0 0
## Confidential 0 0 0 0 0 0 0 0 0
## DH 1 2 16 3 0 0 2 2 0
## DS 0 0 0 0 0 0 1 0 0
## DW 0 1 36 6 0 0 11 6 0
## GD 0 10 49 21 0 6 0 43 1
## GE 0 1 0 0 0 0 0 0 0
## GW 0 0 0 1 0 0 0 0 0
## IW 0 0 0 17 0 0 0 0 0
## LP 0 0 0 0 0 0 0 0 0
## MB 0 0 0 0 0 0 0 0 0
## MM 6 0 0 0 0 0 0 0 0
## MS 0 4 1 0 0 0 0 3 0
## NL 0 0 1908 368 0 3 6 5 1
## OD 0 0 230 4938 2 43 2 2 0
## OE 0 0 0 0 0 0 0 0 0
## OW 0 0 0 0 1 1 0 0 0
## SG 1 0 5 0 0 0 69 0 0
## ST 1 26 13 0 0 0 0 230 0
## TH 0 0 0 0 0 0 0 0 40
##
## Overall Statistics
##
## Accuracy : 0.7875
## 95% CI : (0.7806, 0.7943)
## No Information Rate : 0.3852
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.7125
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: BR Class: Confidential Class: DH Class: DS
## Sensitivity 0.95175 0.0000000 0.326471 0.2222222
## Specificity 0.99883 1.0000000 0.993511 0.9999280
## Pos Pred Value 0.93133 NaN 0.557789 0.6666667
## Neg Pred Value 0.99920 0.9997123 0.983287 0.9994963
## Prevalence 0.01640 0.0002877 0.024459 0.0006474
## Detection Rate 0.01561 0.0000000 0.007985 0.0001439
## Detection Prevalence 0.01676 0.0000000 0.014316 0.0002158
## Balanced Accuracy 0.97529 0.5000000 0.659991 0.6110751
## Class: DW Class: GD Class: GE Class: GW Class: IW
## Sensitivity 0.77909 0.9342 0.494845 0.343590 0.070423
## Specificity 0.98357 0.9705 0.999420 0.998249 0.998486
## Pos Pred Value 0.64228 0.8975 0.857143 0.736264 0.833333
## Neg Pred Value 0.99157 0.9816 0.996461 0.990731 0.909045
## Prevalence 0.03647 0.2165 0.006978 0.014028 0.097043
## Detection Rate 0.02842 0.2023 0.003453 0.004820 0.006834
## Detection Prevalence 0.04424 0.2254 0.004028 0.006546 0.008201
## Balanced Accuracy 0.88133 0.9524 0.747133 0.670919 0.534454
## Class: LP Class: MB Class: MM Class: MS Class: NL
## Sensitivity 1.0000000 0.000e+00 0.6000000 0.0909091 0.8428
## Specificity 1.0000000 1.000e+00 0.9998560 0.9996392 0.9425
## Pos Pred Value 1.0000000 NaN 0.7500000 0.4444444 0.7404
## Neg Pred Value 1.0000000 9.999e-01 0.9997121 0.9971206 0.9686
## Prevalence 0.0002877 7.194e-05 0.0007194 0.0031652 0.1629
## Detection Rate 0.0002877 0.000e+00 0.0004316 0.0002877 0.1373
## Detection Prevalence 0.0002877 0.000e+00 0.0005755 0.0006474 0.1854
## Balanced Accuracy 1.0000000 5.000e-01 0.7999280 0.5452741 0.8926
## Class: OD Class: OE Class: OW Class: SG Class: ST
## Sensitivity 0.9223 0.0000000 1.887e-02 0.718750 0.79038
## Specificity 0.8247 1.0000000 9.998e-01 0.999420 0.99471
## Pos Pred Value 0.7672 NaN 2.500e-01 0.896104 0.76159
## Neg Pred Value 0.9443 0.9997842 9.963e-01 0.998047 0.99551
## Prevalence 0.3852 0.0002158 3.813e-03 0.006906 0.02093
## Detection Rate 0.3552 0.0000000 7.194e-05 0.004964 0.01655
## Detection Prevalence 0.4630 0.0000000 2.877e-04 0.005539 0.02173
## Balanced Accuracy 0.8735 0.5000000 5.093e-01 0.859085 0.89254
## Class: TH
## Sensitivity 0.952381
## Specificity 1.000000
## Pos Pred Value 1.000000
## Neg Pred Value 0.999856
## Prevalence 0.003021
## Detection Rate 0.002877
## Detection Prevalence 0.002877
## Balanced Accuracy 0.976190
head(results_df, 10) %>%
tabla_formato("Tabla 4: Comparación entre la Clase Real y la Predicha (Muestra)")| Valor_Real | Valor_Predicho |
|---|---|
| BR | BR |
| GD | GD |
| NL | GD |
| GW | GE |
| SG | SG |
| OD | OD |
| GD | NL |
| DW | DW |
| DW | DW |
| MS | ST |
## Accuracy: 78.75 %
ggplot(datos_limpios, aes(x = Well_Type, fill = Well_Type)) +
geom_bar(color = "black", alpha = 0.7) +
theme_minimal() +
labs(title = "Distribución del Tipo de Pozo", x = "Tipo de Pozo", y = "Frecuencia") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))ggplot(head(importance_df, 10), aes(x = reorder(Variable, IncNodePurity), y = IncNodePurity)) +
geom_point(color = "darkgreen", size = 3) +
geom_segment(aes(x = Variable, xend = Variable, y = 0, yend = IncNodePurity), color = "darkgreen", size = 1) +
coord_flip() +
theme_minimal() +
labs(title = "Top 10 Variables de Importancia (Clasificación)", x = "Variables", y = "Importancia (Gini Impurity)")El modelo de clasificación basado en Random Forest
demostró ser una herramienta altamente confiable para categorizar el
tipo de pozo petrolero (Well_Type) en función de los
parámetros geográficos, operativos y de profundidad disponibles. A
partir del análisis histórico, se concluye lo siguiente:
num.threads = 0) reduce
significativamente el tiempo de ejecución tanto en la fase de
entrenamiento como en la de predicción.Autor: Jennifer Cordones | Machine Learning de Clasificación (Random Forest) — Oil, Gas & Other Regulated Wells - NY State