library(dplyr)
library(knitr)
library(kableExtra)
library(DT)
ruta <- file.choose()
datos_completos <- read.csv(ruta, stringsAsFactors = FALSE)
cat("Filas:", nrow(datos_completos), "| Columnas:", ncol(datos_completos), "\n")
## Filas: 47757 | Columnas: 24
str(datos_completos)
## 'data.frame': 47757 obs. of 24 variables:
## $ KID : int 1001106903 1001106572 1001106590 1001107343 1001108234 1001106684 1001107377 1001107386 1001107740 1001106710 ...
## $ DEPTH_OF_WELL : num 700 800 1400 1125 2940 ...
## $ CUMULATIVE_PRODUCTION : num 47225 275063 82624 7544 681006 ...
## $ AVG_PRODUCTION : num 859 5001 1758 377 24322 ...
## $ LATITUDE : num 37.1 38.8 37.5 37.8 37.1 ...
## $ LONGITUDE : num -95.9 -95.2 -96.3 -95.7 -101.3 ...
## $ YEARS_ACTIVE : num 55 55 47 20 28 55 20 48 48 55 ...
## $ SECTION : num 33 11 34 8 30 4 26 28 11 17 ...
## $ COUNTY_CODE : num 125 45 49 207 189 121 49 1 31 121 ...
## $ STATE_CODE : int 15 15 15 15 15 15 15 15 15 15 ...
## $ TOWNSHIP : num 33 15 29 26 33 17 30 26 23 16 ...
## $ RANGE : num 14 20 10 16 36 25 12 21 16 24 ...
## $ PRODUCES_OIL : num 1 1 1 1 0 1 1 1 1 1 ...
## $ PRODUCES_GAS : num 0 0 0 0 1 0 0 0 0 0 ...
## $ OPERATOR_NAME : chr "Horton, John" "Whitlow Energy, Inc." "Suerte Oil Company" "Patterson-Blackford" ...
## $ FIELD_NAME : chr "WAYSIDE-HAVANA" "BALDWIN" "DUNKLEBERGER" "ROSE EAST" ...
## $ PRODUCING_FORMATION : chr "UNKNOWN" "UNKNOWN" "UNKNOWN" "UNKNOWN" ...
## $ LONGITUDE_LATITUDE_SOURCE: chr "CENTER_OF_SECTION" "CENTER_OF_SECTION" "CENTER_OF_SECTION" "CENTER_OF_SECTION" ...
## $ PROD_LEVEL : chr "MEDIUM" "HIGH" "MEDIUM" "LOW" ...
## $ DEPTH_LEVEL : chr "SHALLOW" "SHALLOW" "SHALLOW" "SHALLOW" ...
## $ LIFE_STAGE : chr "OLD" "OLD" "OLD" "MATURE" ...
## $ AVG_PROD_LEVEL : chr "LOW" "MEDIUM" "MEDIUM" "LOW" ...
## $ TOWNSHIP_DIRECTION : chr "S" "S" "S" "S" ...
## $ RANGE_DIRECTION : chr "E" "E" "E" "E" ...
Variable Dependiente (Y): Producción Acumulada. Se seleccionó como variable dependiente porque representa la cantidad total de producción obtenida por un pozo durante toda su vida útil. Es el resultado que se desea analizar y explicar a partir de otra variable.
Variable Independiente (X): Años Activo. Se eligió como variable independiente porque indica el tiempo que un pozo ha permanecido en operación. En general, mientras más años ha estado activo un pozo, mayor es la producción que puede acumular, por lo que se espera que esta variable influya directamente sobre la producción acumulada.
datos <- datos_completos %>%
mutate(
YEARS_ACTIVE = as.numeric(YEARS_ACTIVE),
CUMULATIVE_PRODUCTION = as.numeric(CUMULATIVE_PRODUCTION)
) %>%
select(YEARS_ACTIVE, CUMULATIVE_PRODUCTION) %>%
na.omit() %>%
filter(YEARS_ACTIVE > 0, CUMULATIVE_PRODUCTION > 0)
nrow(datos)
## [1] 47757
str(datos)
## 'data.frame': 47757 obs. of 2 variables:
## $ YEARS_ACTIVE : num 55 55 47 20 28 55 20 48 48 55 ...
## $ CUMULATIVE_PRODUCTION: num 47225 275063 82624 7544 681006 ...
summary(datos)
## YEARS_ACTIVE CUMULATIVE_PRODUCTION
## Min. : 1.0 Min. : 1
## 1st Qu.:11.0 1st Qu.: 13030
## Median :19.0 Median : 51074
## Mean :24.3 Mean :144072
## 3rd Qu.:36.0 3rd Qu.:172967
## Max. :89.0 Max. :985283
nrow(datos)
## [1] 47757
condensado <- datos %>%
group_by(YEARS_ACTIVE) %>%
summarise(
n_valores = n(),
valores = paste(CUMULATIVE_PRODUCTION, collapse = ", "),
.groups = "drop"
) %>%
arrange(YEARS_ACTIVE)
condensado_html <- condensado %>%
mutate(
CUMULATIVE_PRODUCTION = paste0(
"<details><summary>", n_valores, " valor(es)</summary>", valores, "</details>"
)
) %>%
select(YEARS_ACTIVE, CUMULATIVE_PRODUCTION)
datatable(condensado_html, escape = FALSE, rownames = FALSE,
colnames = c("Años Activos", "Producción Acumulada (valores)"),
class = "stripe hover compact",
options = list(pageLength = 10))
plot(datos$YEARS_ACTIVE, datos$CUMULATIVE_PRODUCTION,
main = "Nube de puntos (todos los valores, sin agrupar)",
xlab = "Años Activos (X)",
ylab = "Producción Acumulada (Y)",
col = "#5b6b8c",
pch = 19,
cex = 0.6)
grid(col = "gray88")
La nube de puntos visualizada en la parte de arriba no permite conjeturar un modelo con claridad, por esta razón se optó por realizar una estrategia para depurar los datos y obtener un gráfico más limpio.
tabla_mediana <- datos %>%
group_by(YEARS_ACTIVE) %>%
summarise(
CUMULATIVE_PRODUCTION = median(CUMULATIVE_PRODUCTION),
.groups = "drop"
) %>%
arrange(YEARS_ACTIVE)
datatable(tabla_mediana, rownames = FALSE,
colnames = c("Años Activos", "Mediana de Producción Acumulada"),
class = "stripe hover compact",
options = list(pageLength = 10)) %>%
formatRound(columns = "CUMULATIVE_PRODUCTION", digits = 2)
write.csv(condensado[, c("YEARS_ACTIVE", "valores")], "tabla_condensada_por_anios.csv", row.names = FALSE)
write.csv(tabla_mediana, "tabla_mediana_por_anios.csv", row.names = FALSE)
plot(tabla_mediana$YEARS_ACTIVE, tabla_mediana$CUMULATIVE_PRODUCTION,
main = "Producción acumulada en función de los años activos",
xlab = "Años Activos (X)",
ylab = "Mediana de Producción Acumulada (Y)",
col = "#1b2a4a",
pch = 19,
cex = 1.2)
grid(col = "gray88")
La curva crece cerca de cero y se acelera con los años, sin bajar ni aplanarse. Se conjetura un modelo potencial:
\[y = a \cdot x^{b}\]
\[y = a \cdot x^{b}\]
El ajuste se realizó mediante regresión no lineal utilizando los datos originales, sin aplicar transformaciones logarítmicas. Esto permitió que el modelo se adaptara mejor a los valores altos de la variable Y, logrando un ajuste más representativo del comportamiento observado en los datos.
datos_potencial <- tabla_mediana %>%
filter(YEARS_ACTIVE > 0, CUMULATIVE_PRODUCTION > 0)
# Valores iniciales (a, b) a partir del ajuste log-log, usados solo
# como punto de partida para el algoritmo no lineal
modelo_log <- lm(log(CUMULATIVE_PRODUCTION) ~ log(YEARS_ACTIVE), data = datos_potencial)
b_inicial <- unname(coef(modelo_log)[2])
a_inicial <- unname(exp(coef(modelo_log)[1]))
modelo_nls <- nls(
CUMULATIVE_PRODUCTION ~ a * YEARS_ACTIVE^b,
data = datos_potencial,
start = list(a = a_inicial, b = b_inicial)
)
summary(modelo_nls)
##
## Formula: CUMULATIVE_PRODUCTION ~ a * YEARS_ACTIVE^b
##
## Parameters:
## Estimate Std. Error t value Pr(>|t|)
## a 16.058 11.723 1.37 0.174
## b 2.321 0.169 13.74 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 64910 on 87 degrees of freedom
##
## Number of iterations to convergence: 30
## Achieved convergence tolerance: 4.216e-06
a <- unname(coef(modelo_nls)["a"])
b <- unname(coef(modelo_nls)["b"])
# R² del nls, calculado sobre la escala original (1 - SSres/SStot)
pred_nls <- predict(modelo_nls)
ss_res <- sum((datos_potencial$CUMULATIVE_PRODUCTION - pred_nls)^2)
ss_tot <- sum((datos_potencial$CUMULATIVE_PRODUCTION - mean(datos_potencial$CUMULATIVE_PRODUCTION))^2)
r2_nls <- 1 - ss_res / ss_tot
kable(data.frame(Parámetro = c("a", "b", "R² (escala original)"),
Valor = round(c(a, b, r2_nls), 4)),
align = "lr") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE)
| Parámetro | Valor |
|---|---|
| a | 16.0581 |
| b | 2.3210 |
| R² (escala original) | 0.8349 |
plot(datos_potencial$YEARS_ACTIVE, datos_potencial$CUMULATIVE_PRODUCTION,
col = "#5b6b8c",
pch = 19,
cex = 1.2,
main = "Modelo de producción acumulada (mediana) en función de los años activos",
xlab = "Años Activos (X)",
ylab = "Mediana de Producción Acumulada (Y)")
grid(col = "gray88")
curve(a * x^b, add = TRUE, col = "#1b2a4a", lwd = 2)
legend("topleft",
legend = c("Datos reales", paste0("y = ", round(a, 2), " x^", round(b, 2))),
col = c("#5b6b8c", "#1b2a4a"),
pch = c(19, NA),
lty = c(NA, 1),
lwd = c(NA, 2),
bty = "n")
El coeficiente de correlación de Pearson (r) se calculó utilizando los datos originales de años activos y producción acumulada. Por su parte, el coeficiente de determinación (R²) se obtuvo a partir del modelo de regresión no lineal (nls), ajustado con los datos originales, a partir del cual se estimaron los parámetros a y b.
r <- cor(datos_potencial$YEARS_ACTIVE, datos_potencial$CUMULATIVE_PRODUCTION)
r2 <- r2_nls
resultado_test <- ifelse(abs(r) > 0.7, "Aceptado", "Rechazado")
tabla_test <- data.frame(
Test = "Correlación de Pearson (r = cor(x, y))",
`r` = round(r, 4),
`abs(r)` = round(abs(r), 4),
`R²` = round(r2, 4),
Criterio = "abs(r) > 0.7",
Resultado = resultado_test,
check.names = FALSE
)
kable(tabla_test, align = "lrrrrc", format = "html") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE) %>%
column_spec(6, bold = TRUE,
color = ifelse(resultado_test == "Aceptado", "#1b2a4a", "#6b6b6b"))
| Test | r | abs(r) | R² | Criterio | Resultado |
|---|---|---|---|---|---|
| Correlación de Pearson (r = cor(x, y)) | 0.8576 | 0.8576 | 0.8349 | abs(r) > 0.7 | Aceptado |
Dominios de las variables:
tabla_dominios <- data.frame(
Variable = c("Y (CUMULATIVE_PRODUCTION)", "X (YEARS_ACTIVE)"),
Dominio = c("R+ : Y ∈ (0, +∞)", "Z+ : X ∈ Z+}")
)
kable(tabla_dominios,
col.names = c("Variable", "Dominio"),
align = "lc") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE, position = "center", font_size = 14) %>%
column_spec(1, bold = TRUE, width = "6cm") %>%
column_spec(2, width = "6cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| Variable | Dominio |
|---|---|
| Y (CUMULATIVE_PRODUCTION) | R+ : Y ∈ (0, +∞) |
| X (YEARS_ACTIVE) | Z+ : X ∈ Z+} |
¿Existen valores de X que, utilizando la ecuación del modelo, den valores de Y fuera de su dominio?
En este caso no se presentan restricciones para el modelo, ya que el dominio de la variable independiente, Años Activos, está formado por los números enteros positivos. Al evaluar el valor mínimo del dominio (X=1), se obtiene un valor positivo de la variable dependiente, Producción Acumulada, el cual pertenece a su dominio. Además, al tratarse de una ecuación potencial con exponente positivo, la función es creciente, por lo que a medida que aumentan los años activos también aumenta la producción acumulada. En consecuencia, los valores de Y siempre serán reales positivos, cumpliendo con el dominio definido para la variable dependiente.
x_limite <- 0
cat("X =", x_limite, "\n")
## X = 0
x_min_ajuste <- min(datos_potencial$YEARS_ACTIVE)
cat("Menor valor de X usado en el ajuste:", x_min_ajuste, "\n")
## Menor valor de X usado en el ajuste: 1
Con base en el análisis realizado, se determinaron los rangos en los que el modelo presenta un desempeño adecuado, así como los intervalos en los que deja de ser efectivo.
Restricciones del modelo:
tabla_restricciones <- data.frame(
Variable = c("Y (CUMULATIVE_PRODUCTION)", "X (YEARS_ACTIVE)"),
Valido = c("Y > 0", paste0("X ≥ ", x_min_ajuste)),
Invalido = c("Y ≤ 0", paste0("X < ", x_min_ajuste, " (0 ; ", x_min_ajuste, ")"))
)
kable(tabla_restricciones,
col.names = c("Variable", "Rango válido", "Rango inválido"),
align = "lcc") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE, position = "center", font_size = 14) %>%
column_spec(1, bold = TRUE, width = "6cm") %>%
column_spec(2, width = "5cm") %>%
column_spec(3, width = "5cm") %>%
row_spec(0, bold = TRUE, background = "#f2f2f2")
| Variable | Rango válido | Rango inválido |
|---|---|---|
| Y (CUMULATIVE_PRODUCTION) | Y > 0 | Y ≤ 0 |
| X (YEARS_ACTIVE) | X ≥ 1 | X < 1 (0 ; 1) |
¿Cuánta producción acumulada se espera para un pozo con 10, 30 y 60 años activos?
Se responde reemplazando cada valor de X en la ecuación del modelo \(y = a \cdot x^{b}\):
Para X = 10 años activos: \(y = 16.058 \cdot 10^{2.321} = 3,362.53\) de producción acumulada.
Para X = 30 años activos: \(y = 16.058 \cdot 30^{2.321} = 43,057.84\) de producción acumulada.
Para X = 60 años activos: \(y = 16.058 \cdot 60^{2.321} = 215,146.8\) de producción acumulada.
nuevos_anios <- c(5, 10, 15, 20, 25, 30)
estimaciones <- data.frame(
YEARS_ACTIVE = nuevos_anios,
Produccion_Estimada = a * nuevos_anios^b
)
kable(estimaciones, digits = 2, format.args = list(big.mark = ","),
col.names = c("Años Activos", "Producción Acumulada Estimada"),
align = "lr") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE)
| Años Activos | Producción Acumulada Estimada |
|---|---|
| 5 | 672.95 |
| 10 | 3,362.53 |
| 15 | 8,617.27 |
| 20 | 16,801.54 |
| 25 | 28,201.66 |
| 30 | 43,057.84 |
Entre los años activos y la producción acumulada existe una relación de tipo potencial, cuya ecuación matemática es
\[y = 16.058 \cdot x^{2.321}\]
siendo y la producción acumulada y x los años activos, y donde el modelo es válido para x > 0 (años activos positivos), rango en el que fue ajustado.
Cuando los años activos son de 45, se espera una producción acumulada de 110,345.7.
La producción acumulada está influenciada en un 83.5% por los años activos, y en un 16.5% por otros factores.
El test de Pearson sobre los datos originales (r = 0.858) fue Aceptado.