1. Introducción

Este documento presenta un modelo de regresión múltiple en tres dimensiones para el análisis de pozos petroleros en Ontario. El objetivo es predecir la Profundidad Total basándose en la elevación del terreno y la profundidad vertical verdadera.

2. Carga y Procesamiento de Datos

library(scatterplot3d)
library(plotly)

# Carga de datos
# Asegúrate de que el archivo .csv esté en la misma carpeta que este archivo .Rmd
datos <- read.csv("Petroleo_Ontaro.csv", 
                  header = TRUE, sep = ";", dec = ".", fill = TRUE)

# Conversión y limpieza de formatos numéricos
datos$TOTAL_DEPTH <- as.numeric(gsub(",", ".", as.character(datos$TOTAL_DEPTH)))
datos$GROUND_ELEVATION <- as.numeric(gsub(",", ".", as.character(datos$GROUND_ELEVATION)))
datos$TRUE_VERTICAL_DEPTH <- as.numeric(gsub(",", ".", as.character(datos$TRUE_VERTICAL_DEPTH)))

# Filtrado de valores nulos (NA)
datos_filtrados <- subset(datos, 
                          !is.na(TOTAL_DEPTH) & 
                          !is.na(GROUND_ELEVATION) & 
                          !is.na(TRUE_VERTICAL_DEPTH))

3. Limpieza de Outliers

Se aplica el criterio del Rango Intercuartílico (IQR) para asegurar la robustez del modelo eliminando valores atípicos.

eliminar_outliers <- function(x) {
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR <- Q3 - Q1
  (x >= (Q1 - 1.5 * IQR)) & (x <= (Q3 + 1.5 * IQR))
}

sin_outliers <- eliminar_outliers(datos_filtrados$TOTAL_DEPTH) & 
                eliminar_outliers(datos_filtrados$GROUND_ELEVATION) & 
                eliminar_outliers(datos_filtrados$TRUE_VERTICAL_DEPTH)

datos_limpios <- datos_filtrados[sin_outliers, ]

df_reg <- data.frame(
  y  = datos_limpios$TOTAL_DEPTH,
  x1 = datos_limpios$GROUND_ELEVATION,
  x2 = datos_limpios$TRUE_VERTICAL_DEPTH
)

4. Ajuste del Modelo Estadístico

Se genera el modelo lineal múltiple.

modelo_multi <- lm(y ~ x1 + x2, data = df_reg)

# Resumen del modelo
summary(modelo_multi)
## 
## Call:
## lm(formula = y ~ x1 + x2, data = df_reg)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -64.42  -0.50  -0.30  -0.10 534.81 
## 
## Coefficients:
##               Estimate Std. Error  t value Pr(>|t|)    
## (Intercept)  1.8841424  0.7580924    2.485  0.01295 *  
## x1          -0.0097176  0.0037178   -2.614  0.00896 ** 
## x2           1.0010477  0.0003471 2883.831  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 9.636 on 21225 degrees of freedom
## Multiple R-squared:  0.9975, Adjusted R-squared:  0.9975 
## F-statistic: 4.228e+06 on 2 and 21225 DF,  p-value: < 2.2e-16
# Variables para el reporte
r2 <- summary(modelo_multi)$r.squared
coeficientes <- coef(modelo_multi)

5. Visualización Estática (3D)

par(mar = c(4,4,2,1))
scatter3d <- scatterplot3d(
  df_reg$x1, df_reg$x2, df_reg$y,
  pch = 16, color = alpha("steelblue", 0.6),
  xlab = "Elevación del terreno (m)",
  ylab = "Profundidad Vertical (TVD) (m)",
  zlab = "Profundidad Total (m)",
  main = "Plano de Regresión Ajustado",
  angle = 55
)

# Plano de regresión
x1_seq <- seq(min(df_reg$x1), max(df_reg$x1), length.out = 20)
x2_seq <- seq(min(df_reg$x2), max(df_reg$x2), length.out = 20)
grid <- expand.grid(x1 = x1_seq, x2 = x2_seq)
grid$y_pred <- predict(modelo_multi, newdata = grid)

coords <- scatter3d$xyz.convert(grid$x1, grid$x2, grid$y_pred)

# Líneas del plano
for (i in seq(1, nrow(grid), by = 20)) lines(coords$x[i:(i+19)], coords$y[i:(i+19)], col = "red")
for (j in 1:20) lines(coords$x[seq(j, nrow(grid), by = 20)], coords$y[seq(j, nrow(grid), by = 20)], col = "red")

6. Gráfico Interactivo

plot_ly() %>%
  add_markers(data = df_reg, x = ~x1, y = ~x2, z = ~y,
              marker = list(size = 3, color = 'steelblue'), name = "Datos Reales") %>%
  add_surface(x = matrix(grid$x1, 20, 20), y = matrix(grid$x2, 20, 20),
              z = matrix(grid$y_pred, 20, 20), opacity = 0.5, name = "Modelo", showscale = FALSE) %>%
  layout(scene = list(xaxis = list(title = "Elevación"),
                      yaxis = list(title = "TVD"),
                      zaxis = list(title = "Prof. Total")))

7. Estimación de Nuevos Valores

Realizamos el cálculo de la predicción para los valores solicitados (Elevación: 200m, TVD: 250m).

# Definir nuevos valores
nueva_obs <- data.frame(x1 = 200, x2 = 250)

# Calcular predicción
prediccion_fit <- predict(modelo_multi, newdata = nueva_obs)
valor_estimado <- round(prediccion_fit, 2)

Resultado del cálculo: 250.2 metros.

8. Conclusiones

La regresión múltiple muestra que tanto la elevación del terreno como la profundidad vertical influyen significativamente en la profundidad total. El modelo presenta un excelente ajuste (\(R^2 =\) 0.9975) y permite estimar con alta precisión la profundidad total a partir de estas variables.

Por ejemplo, al aplicar el modelo a un escenario con una elevación de 200 m y una profundidad vertical de 250 m, obtenemos una estimación de 250.2 metros para la profundidad total. Esto confirma la capacidad predictiva del modelo para la planificación de perforaciones.