1 Librerías

library(readr)      # lectura del csv
library(dplyr)       # manipulación de datos
library(ggplot2)      # gráficos
library(gt)          # tablas con diseño premium
library(stringr)      # manejo de texto
library(DT)           # tabla interactiva paginada

cat("Librerías cargadas: readr, dplyr, ggplot2, gt, stringr, DT")
Librerías cargadas: readr, dplyr, ggplot2, gt, stringr, DT

2 Carga de Datos

## DATASET ##
# Archivo con las variables YEARS_ACTIVE y CUMULATIVE_PRODUCTION.
# IMPORTANTE: ajusta esta ruta a la ubicación real del archivo en tu equipo.
ruta_archivo <- "oil_and_gas_leases_data.csv.csv"

datos <- read_csv(ruta_archivo, show_col_types = FALSE)

# Rellenamiento de celdas faltantes (NA) con la media de cada variable numérica de interés
if (any(is.na(datos$YEARS_ACTIVE))) {
  datos$YEARS_ACTIVE[is.na(datos$YEARS_ACTIVE)] <- mean(datos$YEARS_ACTIVE, na.rm = TRUE)
}
if (any(is.na(datos$CUMULATIVE_PRODUCTION))) {
  datos$CUMULATIVE_PRODUCTION[is.na(datos$CUMULATIVE_PRODUCTION)] <- mean(datos$CUMULATIVE_PRODUCTION, na.rm = TRUE)
}

# Se descartan pozos con valores no positivos (no tienen sentido físico y
# además impiden aplicar logaritmos en los modelos exponencial/potencial)
datos <- datos %>%
  filter(YEARS_ACTIVE > 0, CUMULATIVE_PRODUCTION > 0)

## Estructura de los datos
str(datos[, c("YEARS_ACTIVE", "CUMULATIVE_PRODUCTION")])
tibble [47,757 × 2] (S3: tbl_df/tbl/data.frame)
 $ YEARS_ACTIVE         : num [1:47757] 55 55 47 20 28 55 20 48 48 55 ...
 $ CUMULATIVE_PRODUCTION: num [1:47757] 47225 275063 82624 7544 681006 ...

3 Selección de Variables

Se definieron los Años Activos (YEARS_ACTIVE) como variable independiente / causa (x), ya que representan el tiempo durante el cual un pozo ha estado en operación, constituyendo un factor clave en la acumulación de producción.

La Producción Acumulada (CUMULATIVE_PRODUCTION) actúa como variable dependiente / efecto (y), debido a que refleja la cantidad total de hidrocarburos extraídos a lo largo del tiempo de vida del pozo.

4 Paso 4: Tabla de Pares de Valores

4.1 Tabla general de todos los valores (x, y)

Se presenta la tabla con los pares (x̄, ȳ) resumidos del dataset para las variables aplicadas al análisis: x (Años Activos) y y (Producción Acumulada), una vez depurados los registros con valores nulos o no válidos (celdas rellenadas con la media, según se indicó en la sección de Datos). El dataset depurado contiene 47757 pozos, pero solo 89 valores distintos de Años Activos (x): al haber miles de pozos que comparten el mismo año de actividad, trabajar con los datos crudos implica manejar valores de x fuertemente repetidos. Por ello, tanto para la variable independiente x (Años Activos) como para la dependiente y (Producción Acumulada) se calcula su media por cada año (floor(YEARS_ACTIVE)), obteniendo así un único par (x̄, ȳ) por año —sin valores repetidos— que es el que se emplea en el resto del análisis.

tabla_xy_completa <- datos %>%
  mutate(Año = floor(YEARS_ACTIVE)) %>%
  group_by(Año) %>%
  summarise(
    n_pozos = n(),
    x_medio = mean(YEARS_ACTIVE),
    y_medio = mean(CUMULATIVE_PRODUCTION),
    .groups = "drop"
  ) %>%
  arrange(Año)

cat("Total de pares (x̄, ȳ) obtenidos, uno por año:", nrow(tabla_xy_completa), "\n")
Total de pares (x̄, ȳ) obtenidos, uno por año: 89 
# Tabla condensada: para cada Año Activo se listan, en una celda
# contraíble (expandir/colapsar), todos los valores individuales de
# Producción Acumulada de los pozos que comparten ese año.
tabla_condensada <- datos %>%
  mutate(`Años Activos` = floor(YEARS_ACTIVE)) %>%
  group_by(`Años Activos`) %>%
  summarise(
    `Producción Acumulada (valores)` = sprintf(
      '<details><summary>%d valor(es)</summary><div style="max-height:180px; overflow-y:auto; padding:6px">%s</div></details>',
      n(),
      paste(format(round(CUMULATIVE_PRODUCTION, 2), big.mark = ","), collapse = ", ")
    ),
    .groups = "drop"
  ) %>%
  arrange(`Años Activos`)

datatable(
  tabla_condensada,
  caption = htmltools::tags$caption(
    style = "caption-side: top; text-align: left; font-size: 16px; font-weight: 700; color:#1F2A33;",
    "Tabla 4.1: Tabla condensada (todos los valores, celdas contraíbles)"
  ),
  escape = FALSE,
  rownames = FALSE,
  class = "display compact stripe hover",
  options = list(
    pageLength = 10,
    dom = "ltip",
    columnDefs = list(list(className = "dt-center", targets = 0))
  )
)

4.2 Máximos, mínimos y separación de la variable

max_x <- max(datos$YEARS_ACTIVE)
min_x <- min(datos$YEARS_ACTIVE)
max_y <- max(datos$CUMULATIVE_PRODUCTION)
min_y <- min(datos$CUMULATIVE_PRODUCTION)

resumen_general <- data.frame(
  Variable = c("YEARS_ACTIVE (X)", "CUMULATIVE_PRODUCTION (Y)"),
  Minimo   = c(round(min_x, 0), round(min_y, 2)),
  Maximo   = c(round(max_x, 0), round(max_y, 2)),
  Rango    = c(round(max_x - min_x, 0), round(max_y - min_y, 2)),
  Media    = c(round(mean(datos$YEARS_ACTIVE), 0), round(mean(datos$CUMULATIVE_PRODUCTION), 2))
)

resumen_general %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°1: Resumen General de las Variables**")
  ) %>%
  tab_source_note(source_note = "Autor: Valeska Araujo") %>%
  cols_align(align = "center", everything())
Tabla N°1: Resumen General de las Variables
Variable Minimo Maximo Rango Media
YEARS_ACTIVE (X) 1 89 88 24.0
CUMULATIVE_PRODUCTION (Y) 1 985283 985282 144072.2
Autor: Valeska Araujo

4.3 División en tres partes según el valor máximo de X

Se toma el valor máximo de YEARS_ACTIVE (89 años) y se divide el eje en tres tercios iguales, de modo que cada parte agrupe pozos “jóvenes”, “maduros” y “muy antiguos”. Esto también permite visualizar mejor la nube de puntos, que al graficarse completa se satura por la gran cantidad de observaciones (47757 pozos).

limite1 <- max_x / 3
limite2 <- 2 * max_x / 3

datos <- datos %>%
  mutate(parte = case_when(
    YEARS_ACTIVE <= limite1 ~ "Parte 1",
    YEARS_ACTIVE <= limite2 ~ "Parte 2",
    TRUE ~ "Parte 3"
  ))

parte1 <- datos %>% filter(parte == "Parte 1")
parte2 <- datos %>% filter(parte == "Parte 2")
parte3 <- datos %>% filter(parte == "Parte 3")

data.frame(
  Parte   = c("Parte 1", "Parte 2", "Parte 3"),
  Rango_X = c(paste0("[", round(min_x,0), " - ", round(limite1,0), "]"),
              paste0("(", round(limite1,0), " - ", round(limite2,0), "]"),
              paste0("(", round(limite2,0), " - ", round(max_x,0), "]")),
  n       = c(nrow(parte1), nrow(parte2), nrow(parte3))
) %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°2: Distribución de Pozos por Parte**")
  ) %>%
  tab_source_note(source_note = "Autor: Valeska Araujo") %>%
  cols_align(align = "center", everything())
Tabla N°2: Distribución de Pozos por Parte
Parte Rango_X n
Parte 1 [1 - 30] 31814
Parte 2 (30 - 59] 13200
Parte 3 (59 - 89] 2743
Autor: Valeska Araujo

En resumen, las tres partes dividen el rango de Años Activos (X) de la siguiente manera:

  • Parte 1: de 1 a 30 años.
  • Parte 2: de 30 a 59 años.
  • Parte 3: de 59 a 89 años.

4.4 Tablas individuales de pares (x̄, ȳ) por parte

Con miles de pozos por parte, la nube de puntos es puro ruido. Por eso, en lugar de agrupar por clases de rango, se agrupan los pozos por cada año exacto de actividad (floor(YEARS_ACTIVE)) y se calcula el promedio de ambas variables —el par (x̄, ȳ)— para cada año. Estas parejas, una por cada año representado dentro de la parte, son las que se usan para ajustar los modelos de cada parte.

# Las tres partes se obtienen ahora dividiendo directamente la tabla
# resumida (tabla_xy_completa), es decir, sobre los pares (x̄, ȳ) ya
# promediados por año, y no sobre los pozos crudos.
tabla_xy_completa <- tabla_xy_completa %>%
  mutate(parte = case_when(
    x_medio <= limite1 ~ "Parte 1",
    x_medio <= limite2 ~ "Parte 2",
    TRUE ~ "Parte 3"
  ))

tabla1 <- tabla_xy_completa %>% filter(parte == "Parte 1") %>% select(-parte)
tabla2 <- tabla_xy_completa %>% filter(parte == "Parte 2") %>% select(-parte)
tabla3 <- tabla_xy_completa %>% filter(parte == "Parte 3") %>% select(-parte)

4.4.1 Parte 1 — Tabla de medias por año

datatable(
  tabla1,
  caption = htmltools::tags$caption(
    style = "caption-side: top; text-align: left; font-size: 16px; font-weight: 700; color:#1F2A33;",
    paste0("Tabla 4.4.1: Pares (x̄, ȳ) — Parte 1 (", nrow(tabla1), " años promediados)")
  ),
  rownames = FALSE,
  class = "display compact stripe hover",
  options = list(
    pageLength = 10,
    dom = "ltip",
    columnDefs = list(list(className = "dt-center", targets = "_all"))
  )
) %>%
  formatRound(columns = "x_medio", digits = 0) %>%
  formatRound(columns = "y_medio", digits = 2)

4.4.2 Parte 2 — Tabla de medias por año

datatable(
  tabla2,
  caption = htmltools::tags$caption(
    style = "caption-side: top; text-align: left; font-size: 16px; font-weight: 700; color:#1F2A33;",
    paste0("Tabla 4.4.2: Pares (x̄, ȳ) — Parte 2 (", nrow(tabla2), " años promediados)")
  ),
  rownames = FALSE,
  class = "display compact stripe hover",
  options = list(
    pageLength = 10,
    dom = "ltip",
    columnDefs = list(list(className = "dt-center", targets = "_all"))
  )
) %>%
  formatRound(columns = "x_medio", digits = 0) %>%
  formatRound(columns = "y_medio", digits = 2)

4.4.3 Parte 3 — Tabla de medias por año

datatable(
  tabla3,
  caption = htmltools::tags$caption(
    style = "caption-side: top; text-align: left; font-size: 16px; font-weight: 700; color:#1F2A33;",
    paste0("Tabla 4.4.3: Pares (x̄, ȳ) — Parte 3 (", nrow(tabla3), " años promediados)")
  ),
  rownames = FALSE,
  class = "display compact stripe hover",
  options = list(
    pageLength = 10,
    dom = "ltip",
    columnDefs = list(list(className = "dt-center", targets = "_all"))
  )
) %>%
  formatRound(columns = "x_medio", digits = 0) %>%
  formatRound(columns = "y_medio", digits = 2)

5 Paso 5: Gráfica de Dispersión

Antes de conjeturar un modelo, se presenta la nube de puntos con todos los valores del dataset. Como se verá a continuación, conjeturar un modelo directamente sobre esta nube completa es difícil por la complejidad y saturación de la nube de puntos (miles de pozos comparten el mismo valor de X), por lo que es necesario aplicar antes una estrategia de tratamiento de datos (promediar por año y dividir en tres partes) antes de poder conjeturar un modelo por tramo.

5.1 Nube de puntos completa (todos los pozos, sin promediar)

Antes de trabajar con los pares promediados (x̄, ȳ), se presenta la nube de puntos cruda, con los 47757 pozos individuales, en una sola tonalidad. Se aprecia por qué esta vista satura: al haber tantos pozos compartiendo valores de Años Activos, los puntos se amontonan y no permiten distinguir tendencia alguna a simple vista — de ahí la necesidad de resumir por año (sección anterior) y de dividir en partes (siguiente gráfica).

par(mar = c(5, 5, 4, 2))
plot(
  datos$YEARS_ACTIVE, datos$CUMULATIVE_PRODUCTION,
  col  = adjustcolor("#9B59B6", alpha.f = 0.35),
  pch  = 16, cex = 0.5,
  xlab = "Años Activos (X)", ylab = "Producción Acumulada (Y)",
  main = "Gráfica N°1: Nube de puntos completa (todos los pozos)",
  cex.main = 0.9, frame.plot = FALSE
)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
box()

5.2 Nube de puntos completa, dividida en 3 partes (todos los pozos, sin promediar)

La misma nube cruda, ahora coloreada según la parte a la que pertenece cada pozo (Parte 1/2/3, definidas en la sección anterior según YEARS_ACTIVE), con líneas punteadas marcando los límites 30 y 59.

colores <- c("Parte 1" = "#2E86AB", "Parte 2" = "#E67E22", "Parte 3" = "#C0392B")

par(mar = c(5, 5, 4, 9))
plot(
  datos$YEARS_ACTIVE, datos$CUMULATIVE_PRODUCTION,
  col  = colores[datos$parte],
  pch  = 16, cex = 0.5,
  xlab = "Años Activos (X)", ylab = "Producción Acumulada (Y)",
  main = "Gráfica N°2: Nube de puntos completa, dividida en 3 partes (todos los pozos)",
  cex.main = 0.9, frame.plot = FALSE
)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
abline(v = c(limite1, limite2), lty = 2, col = "gray40")
legend("topright", inset = c(-0.32, 0), legend = names(colores), col = colores,
       pch = 16, bty = "n", xpd = TRUE)
box()

5.3 Nube de puntos completa (tabla resumida)

Primero la nube de puntos completa (tabla resumida), coloreada por parte.

colores <- c("Parte 1" = "#2E86AB", "Parte 2" = "#E67E22", "Parte 3" = "#C0392B")

par(mar = c(5, 5, 4, 9))

plot(tabla_xy_completa$x_medio, tabla_xy_completa$y_medio,
     col = colores[tabla_xy_completa$parte], pch = 16, cex = 1,
     xlab = "Años Activos (X)", ylab = "Producción Acumulada (Y)",
     main = "Gráfica N°3: Nube de puntos (tabla resumida), dividida en 3 partes",
     cex.main = 0.9, frame.plot = FALSE)

grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
abline(v = c(limite1, limite2), lty = 2, col = "gray40")
legend("topright", inset = c(-0.32, 0), legend = names(colores), col = colores,
       pch = 16, bty = "n", xpd = TRUE)
box()

5.4 Nube de puntos — Parte 1

Ya con los pares (x̄, ȳ) que se usarán en la regresión.

par(mar = c(5, 5, 4, 2))
plot(tabla1$x_medio, tabla1$y_medio, pch = 19, col = "#2E86AB",
     xlab = "X medio (Años Activos)", ylab = "Y medio (Producción Acumulada)",
     main = "Gráfica N°4: Nube de Puntos — Parte 1",
     cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
box()

5.5 Nube de puntos — Parte 2

par(mar = c(5, 5, 4, 2))
plot(tabla2$x_medio, tabla2$y_medio, pch = 19, col = "#E67E22",
     xlab = "X medio (Años Activos)", ylab = "Y medio (Producción Acumulada)",
     main = "Gráfica N°5: Nube de Puntos — Parte 2",
     cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
box()

5.6 Nube de puntos — Parte 3

par(mar = c(5, 5, 4, 2))
plot(tabla3$x_medio, tabla3$y_medio, pch = 19, col = "#C0392B",
     xlab = "X medio (Años Activos)", ylab = "Y medio (Producción Acumulada)",
     main = "Gráfica N°6: Nube de Puntos — Parte 3",
     cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
box()

6 Conjetura

Al observar la nube de puntos completa (sección “Gráfica de Dispersión”), esta resulta compleja: no sigue un único patrón a lo largo de todo el rango de X, por lo que se procede según lo indicado para nubes de puntos complejas o caóticas.

6.1 Tratamiento de los Datos

Se trabajó con un único par (x̄, ȳ) por cada año de actividad, promediando la producción acumulada de todos los pozos que comparten ese año (ver sección “Tabla de Pares de Valores”), lo que evita el ruido de tener miles de pozos repitiendo el mismo valor de X. Adicionalmente, la nube se segmentó en tres partes según el valor máximo de X — Parte 1, Parte 2 y Parte 3 —, tal como se detalló en esa misma sección.

6.2 Nueva Gráfica de Dispersión

Con los datos ya tratados (promediados por año y divididos en tres partes), se obtiene la nube de puntos mostrada en la sección “Gráfica de Dispersión” (Gráficas N°3 a N°6), coloreada y separada por parte, sobre la cual se apoya la conjetura de cada modelo.

6.3 Nueva Conjetura

  • Parte 1 (pozos jóvenes, 1 - 30 años): la nube muestra un crecimiento que se va acelerando a medida que aumentan los años activos, sin señales de aplanamiento — un patrón de crecimiento compuesto más que de declinación. Se conjetura un modelo exponencial: \[y = a \cdot e^{bx}\]

  • Parte 2 (pozos maduros, 30 - 59 años): la nube no es monótona: sube, se estabiliza y vuelve a subir (efecto de reacondicionamientos/reactivaciones a mitad de vida del pozo). Ningún modelo monótono (log, exp, potencial) puede seguir esa curvatura, así que se conjetura obligatoriamente un modelo polinómico de grado 2: \[y = a + bx + cx^{2}\]

  • Parte 3 (pozos muy antiguos, 59 - 89 años): el crecimiento se acelera de forma marcada al final (pozos centenarios acumulan de forma compuesta). Se conjetura un modelo exponencial: \[y = a \cdot e^{bx}\]

7 Cálculo de Parámetros

7.1 Parte 1 — Modelo exponencial

modelo1 <- lm(log(y_medio) ~ x_medio, data = tabla1)
a1 <- exp(coef(modelo1)[1])
b1 <- coef(modelo1)[2]
cat("y =", round(a1, 3), "* e^(", round(b1, 4), "* x )")
y = 19947.68 * e^( 0.0939 * x )
summary(modelo1)

Call:
lm(formula = log(y_medio) ~ x_medio, data = tabla1)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.08315 -0.07537  0.04038  0.13097  0.34188 

Coefficients:
            Estimate Std. Error t value             Pr(>|t|)    
(Intercept) 9.900868   0.101325   97.71 < 0.0000000000000002 ***
x_medio     0.093915   0.005899   15.92  0.00000000000000302 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.2658 on 27 degrees of freedom
Multiple R-squared:  0.9037,    Adjusted R-squared:  0.9002 
F-statistic: 253.4 on 1 and 27 DF,  p-value: 0.000000000000003022

7.2 Parte 2 — Modelo polinómico (grado 2)

modelo2 <- lm(y_medio ~ x_medio + I(x_medio^2), data = tabla2)
a2 <- coef(modelo2)[1]; b2 <- coef(modelo2)[2]; c2 <- coef(modelo2)[3]

# Texto con el signo correcto de cada coeficiente (evita imprimir "+ -60147.9")
signo_b2 <- ifelse(b2 >= 0, "+", "-")
signo_c2 <- ifelse(c2 >= 0, "+", "-")
b2_txt <- paste(signo_b2, round(abs(b2), 2))
c2_txt <- paste(signo_c2, round(abs(c2), 4))

cat("y =", round(a2,2), b2_txt, "*x", c2_txt, "*x^2")
y = 1557110 - 60147.85 *x + 654.2263 *x^2
summary(modelo2)

Call:
lm(formula = y_medio ~ x_medio + I(x_medio^2), data = tabla2)

Residuals:
   Min     1Q Median     3Q    Max 
-60489 -18498 -10897  17185  66573 

Coefficients:
              Estimate Std. Error t value       Pr(>|t|)    
(Intercept)  1557110.1   166220.4   9.368 0.000000000566 ***
x_medio       -60147.8     7665.0  -7.847 0.000000019474 ***
I(x_medio^2)     654.2       85.8   7.625 0.000000033495 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 31440 on 27 degrees of freedom
Multiple R-squared:  0.7113,    Adjusted R-squared:   0.69 
F-statistic: 33.27 on 2 and 27 DF,  p-value: 0.00000005189

7.3 Parte 3 — Modelo exponencial

modelo3 <- lm(log(y_medio) ~ x_medio, data = tabla3)
a3 <- exp(coef(modelo3)[1])
b3 <- coef(modelo3)[2]
cat("y =", round(a3, 3), "* e^(", round(b3, 4), "* x )")
y = 56824.06 * e^( 0.0259 * x )
summary(modelo3)

Call:
lm(formula = log(y_medio) ~ x_medio, data = tabla3)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.22259 -0.05647 -0.01596  0.06611  0.23948 

Coefficients:
             Estimate Std. Error t value             Pr(>|t|)    
(Intercept) 10.947715   0.151249   72.38 < 0.0000000000000002 ***
x_medio      0.025944   0.002017   12.87    0.000000000000283 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.0956 on 28 degrees of freedom
Multiple R-squared:  0.8553,    Adjusted R-squared:  0.8501 
F-statistic: 165.5 on 1 and 28 DF,  p-value: 0.0000000000002833

8 Realidad y Modelo

8.1 Parte 1 — Exponencial

par(mar = c(5, 5, 4, 2))
plot(tabla1$x_medio, tabla1$y_medio, col = "#2E86AB", pch = 16,
     xlab = "X (Años Activos)", ylab = "Y (Producción Acumulada)",
     main = "Gráfica N°7: Modelo Exponencial superpuesto a la Realidad — Parte 1",
     cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
curve(a1 * exp(b1 * x), add = TRUE, col = "#1F2A33", lwd = 3)
legend("topleft", legend = c("Datos (x̄, ȳ)", "Modelo Exponencial"),
       col = c("#2E86AB", "#1F2A33"), pch = c(16, NA), lwd = c(NA, 3), bty = "n")
box()

8.2 Parte 2 — Polinómico

par(mar = c(5, 5, 4, 2))
plot(tabla2$x_medio, tabla2$y_medio, col = "#E67E22", pch = 16,
     xlab = "X (Años Activos)", ylab = "Y (Producción Acumulada)",
     main = "Gráfica N°8: Modelo Polinómico superpuesto a la Realidad — Parte 2",
     cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
curve(a2 + b2*x + c2*x^2, add = TRUE, col = "#1F2A33", lwd = 3)
legend("topleft", legend = c("Datos (x̄, ȳ)", "Modelo Polinómico"),
       col = c("#E67E22", "#1F2A33"), pch = c(16, NA), lwd = c(NA, 3), bty = "n")
box()

8.3 Parte 3 — Exponencial

par(mar = c(5, 5, 4, 2))
plot(tabla3$x_medio, tabla3$y_medio, col = "#C0392B", pch = 16,
     xlab = "X (Años Activos)", ylab = "Y (Producción Acumulada)",
     main = "Gráfica N°9: Modelo Exponencial superpuesto a la Realidad — Parte 3",
     cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
curve(a3 * exp(b3 * x), add = TRUE, col = "#1F2A33", lwd = 3)
legend("topleft", legend = c("Datos (x̄, ȳ)", "Modelo Exponencial"),
       col = c("#C0392B", "#1F2A33"), pch = c(16, NA), lwd = c(NA, 3), bty = "n")
box()

9 Test

El coeficiente de correlación de Pearson, calculado en R mediante cor(x, y), debe superar 0.7 o -0.7 para aceptar el modelo, dado que toma valores entre 1 y -1. Para los modelos monótonos (potencial y exponencial) se prueba dicho coeficiente sobre la escala en que el modelo es lineal (logarítmica). Para el modelo polinómico, al no ser lineal en una sola escala, se usa el coeficiente de correlación múltiple \(R\), que cumple el mismo rol de “bondad de ajuste” entre 0 y 1.

# Parte 1: exponencial -> se prueba x vs log(y)
r1 <- cor.test(tabla1$x_medio, log(tabla1$y_medio))

# Parte 2: polinómico -> R multiple = sqrt(R^2) del modelo
R2_2 <- summary(modelo2)$r.squared
r2 <- sqrt(R2_2)

# Parte 3: exponencial -> se prueba x vs log(y)
r3 <- cor.test(tabla3$x_medio, log(tabla3$y_medio))

tabla_pearson <- data.frame(
  Parte = c("Parte 1 (Exponencial)", "Parte 2 (Polinómico)", "Parte 3 (Exponencial)"),
  r_o_R = c(r1$estimate, r2, r3$estimate),
  Supera_0.7 = c(abs(r1$estimate) > 0.7, r2 > 0.7, abs(r3$estimate) > 0.7),
  p_valor_modelo = c(
    pf(summary(modelo1)$fstatistic[1], summary(modelo1)$fstatistic[2], summary(modelo1)$fstatistic[3], lower.tail = FALSE),
    pf(summary(modelo2)$fstatistic[1], summary(modelo2)$fstatistic[2], summary(modelo2)$fstatistic[3], lower.tail = FALSE),
    pf(summary(modelo3)$fstatistic[1], summary(modelo3)$fstatistic[2], summary(modelo3)$fstatistic[3], lower.tail = FALSE)
  )
)

tabla_pearson %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°6: Test de Bondad de Ajuste por Parte**"),
    subtitle = "Umbral |r| o R > 0.7"
  ) %>%
  fmt_number(columns = c(r_o_R, p_valor_modelo), decimals = 4) %>%
  tab_source_note(source_note = "Autor: Valeska Araujo") %>%
  cols_align(align = "center", everything())
Tabla N°6: Test de Bondad de Ajuste por Parte
Umbral |r| o R > 0.7
Parte r_o_R Supera_0.7 p_valor_modelo
Parte 1 (Exponencial) 0.9506 TRUE 0.0000
Parte 2 (Polinómico) 0.8434 TRUE 0.0000
Parte 3 (Exponencial) 0.9248 TRUE 0.0000
Autor: Valeska Araujo

Las tres partes superan el umbral de |r| > 0.7 (o R > 0.7 en el caso del polinómico) y además el estadístico F de cada modelo es significativo (p-valor < 0.05), por lo que los tres modelos se aceptan.

10 Restricciones

Cada modelo es válido únicamente dentro del rango de valores para el que fue ajustado; no se recomienda extrapolar fuera de esos límites, ya que el comportamiento fuera del rango observado no está garantizado por los datos. Dado que, en este análisis, Y (Producción Acumulada) es la única variable que el modelo estima — X (Años Activos) es siempre un dato observado, no algo a predecir.

y_valido1 <- a1
y_valido3 <- a3
vertice_x2 <- -b2 / (2 * c2)
y_valido2 <- a2 + b2 * vertice_x2 + c2 * vertice_x2^2

tabla_restricciones <- data.frame(
  Parte = c("Parte 1", "Parte 2", "Parte 3"),
  Modelo = c("Exponencial", "Polinómico (grado 2)", "Exponencial"),
  Dominio = c(
    "R+ : X,Y ∈ (0, +∞)",
    "R : X ∈ R",
    "R+ : X,Y ∈ (0, +∞)"
  ),
  Valido = c(
    paste0("X ≥ ", round(y_valido1,0), " (hasta +∞)"),
    paste0("X ≥ ", round(y_valido2,0), " (hasta +∞)"),
    paste0("X ≥ ", round(y_valido3,0), " (hasta +∞)")
  ),
  Invalido = c(
    paste0("X < ", round(y_valido1,0)),
    paste0("X < ", round(y_valido2,0)),
    paste0("X < ", round(y_valido3,0))
  )
)

tabla_restricciones %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°8: Restricciones del Modelo por Parte**")
  ) %>%
  tab_source_note(source_note = "Autor: Valeska Araujo") %>%
  cols_label(
    Parte = "Parte",
    Modelo = "Modelo",
    Dominio = "Dominio",
    Valido = "Rango válido (X)",
    Invalido = "Rango inválido (X)"
  ) %>%
  cols_align(align = "center", everything())
Tabla N°8: Restricciones del Modelo por Parte
Parte Modelo Dominio Rango válido (X) Rango inválido (X)
Parte 1 Exponencial R+ : X,Y ∈ (0, +∞) X ≥ 19948 (hasta +∞) X < 19948
Parte 2 Polinómico (grado 2) R : X ∈ R X ≥ 174651 (hasta +∞) X < 174651
Parte 3 Exponencial R+ : X,Y ∈ (0, +∞) X ≥ 56824 (hasta +∞) X < 56824
Autor: Valeska Araujo

11 Estimación

En este paso, la única variable que se estima es Y (Producción Acumulada): X (Años Activos) siempre es un valor conocido/observado que se ingresa al modelo, nunca algo que el modelo prediga.

# Un ejemplo de estimación puntual dentro del rango de cada parte
# (X es siempre un valor observado; solo Y es la cantidad estimada)
x_est1 <- round(mean(range(tabla1$x_medio)), 0)
x_est2 <- round(mean(range(tabla2$x_medio)), 0)
x_est3 <- round(mean(range(tabla3$x_medio)), 0)

y_est1 <- a1 * exp(b1 * x_est1)
y_est2 <- a2 + b2*x_est2 + c2*x_est2^2
y_est3 <- a3 * exp(b3 * x_est3)

eq1_txt <- paste0("y = ", round(a1, 3), " · e^(", round(b1, 4), "·x)")
eq2_txt <- paste0("y = ", round(a2, 1), " ", b2_txt, "x ", c2_txt, "x²")
eq3_txt <- paste0("y = ", round(a3, 3), " · e^(", round(b3, 4), "·x)")

data.frame(
  Parte = c("Parte 1", "Parte 2", "Parte 3"),
  Ecuacion_del_Modelo = c(eq1_txt, eq2_txt, eq3_txt),
  X_estimado_años_activos = as.integer(c(x_est1, x_est2, x_est3)),
  Y_estimado_produccion_acumulada = round(c(y_est1, y_est2, y_est3), 1)
) %>%
  gt() %>%
  tab_header(
    title = md("**Tabla N°7: Estimaciones Puntuales de Producción Acumulada**")
  ) %>%
  tab_source_note(source_note = "Autor: Valeska Araujo") %>%
  cols_align(align = "center", everything())
Tabla N°7: Estimaciones Puntuales de Producción Acumulada
Parte Ecuacion_del_Modelo X_estimado_años_activos Y_estimado_produccion_acumulada
Parte 1 y = 19947.683 · e^(0.0939·x) 15 81600.8
Parte 2 y = 1557110.1 - 60147.85x + 654.2263x² 44 177186.6
Parte 3 y = 56824.063 · e^(0.0259·x) 74 387547.5
Autor: Valeska Araujo

Para un pozo de la Parte 1 (pozos jóvenes) con 15 años activos, el modelo estima una producción acumulada de aproximadamente 81,600.8 barriles.

Para un pozo de la Parte 2 (pozos maduros) con 44 años activos, el modelo estima una producción acumulada de aproximadamente 177,186.6 barriles.

Para un pozo de la Parte 3 (pozos muy antiguos) con 74 años activos, el modelo estima una producción acumulada de aproximadamente 387,547.5 barriles.

12 Conclusiones

  • Entre los años activos de un pozo (X) y su producción acumulada (Y) existe relación, pero no es la misma en todo el rango: por eso fue necesario dividir la nube de puntos en tres partes según el valor máximo de X.
  • Parte 1 (pozos jóvenes): relación exponencial \(y = 19947.68 \cdot e^{0.0939x}\), con \(r = 0.951\). La producción acumulada crece de forma compuesta ya desde los primeros años de vida del pozo.
  • Parte 2 (pozos maduros): relación polinómica de grado 2 \(y = 1557110.1 - 60147.85x + 654.2263x^2\), con \(R = 0.843\). La curvatura refleja reactivaciones/reacondicionamientos a mitad de vida del pozo, algo que ningún modelo monótono podía capturar.
  • Parte 3 (pozos muy antiguos): relación exponencial \(y = 56824.06 \cdot e^{0.0259x}\), con \(r = 0.925\). La producción acumulada crece de forma compuesta en pozos con décadas de actividad.
  • En los tres tramos el coeficiente de correlación (o su equivalente \(R\)) supera 0.7, y el test F de cada modelo resultó significativo, de modo que los tres modelos pasan el test de bondad de ajuste y se aceptan.
  • En conjunto, esto confirma que el tiempo de actividad del pozo es un buen predictor de su producción acumulada, aunque la forma de esa relación cambia con la madurez del pozo, lo que justifica el análisis por partes en lugar de un único modelo global.