Modelo de Regresión Exponencial



1.Carga de librerías

library(readxl)
library(dplyr)
library(gt)

2.Carga de datos

datos <- read_excel("dataset_mundial_petro.xlsx")
cat("Número de registros:", nrow(datos), "\n")
## Número de registros: 49212
cat("Número de variables:", ncol(datos), "\n")
## Número de variables: 32

3.Selección de variables

La variable Discovery year (año de descubrimiento) actúa como variable independiente o causa (X), ya que el año en que se descubre un yacimiento condiciona directamente cuándo puede comenzar su producción. La variable Production start year (año de inicio de producción) actúa como variable dependiente o efecto (Y), ya que no es posible iniciar la producción sin haber descubierto previamente el yacimiento.

Nota: Este modelo usa la transformación \(\ln(Y) \sim X\) (exponencial), a diferencia del modelo potencial que usa \(\ln(Y) \sim \ln(X)\). Ambos modelos aplican a las mismas variables pero con diferente estructura matemática.

extraer_anio <- function(v) as.numeric(sub("^(\\d{4}).*", "\\1", as.character(v)))

x_raw <- as.numeric(datos$`Discovery year`)
y_raw <- extraer_anio(datos$`Production start year`)

cat("Registros con Discovery year:", sum(!is.na(x_raw)), "\n")
## Registros con Discovery year: 4935
cat("Registros con Production start year:", sum(!is.na(y_raw)), "\n")
## Registros con Production start year: 2233
cat("Pares completos (ambos con dato):", sum(!is.na(x_raw) & !is.na(y_raw)), "\n")
## Pares completos (ambos con dato): 1889
cat("X sin Y:", sum(!is.na(x_raw) & is.na(y_raw)), "\n")
## X sin Y: 3046
cat("Y sin X:", sum(is.na(x_raw) & !is.na(y_raw)), "\n")
## Y sin X: 344

4.Tabla de pares de valores

Se presenta un extracto (primeros 20 registros) tal como fueron extraídos del dataset, antes de cualquier depuración.

df_pares <- data.frame(x = x_raw, y = y_raw)

df_pares %>%
  head(20) %>%
  rename(`Año de Descubrimiento (X)`       = x,
         `Año de Inicio de Producción (Y)` = y) %>%
  gt() %>%
  tab_header(
    title    = md("**Tabla de Pares de Valores**"),
    subtitle = md("Valores originales sin depurar")
  ) %>%
  tab_source_note(source_note = "Autor: Grupo 5") %>%
  cols_align(align = "center", columns = everything()) %>%
  tab_options(
    table.border.top.color            = "black",
    table.border.bottom.color         = "black",
    table.border.top.style            = "solid",
    table.border.bottom.style         = "solid",
    column_labels.font.weight         = "bold",
    column_labels.border.top.color    = "black",
    column_labels.border.bottom.color = "black",
    column_labels.border.bottom.width = px(2),
    heading.border.bottom.color       = "black",
    heading.border.bottom.width       = px(2),
    table_body.hlines.color           = "grey",
    table_body.border.bottom.color    = "black"
  )
Tabla de Pares de Valores
Valores originales sin depurar
Año de Descubrimiento (X) Año de Inicio de Producción (Y)
1949 1951
2001 2009
1966 1969
1975 1979
1984 1987
1986 1998
1981 1981
2004 2005
NA NA
NA NA
1981 1985
1986 2002
1982 1987
2007 NA
1965 1965
2000 2010
2013 2020
2001 NA
1979 1983
1999 2021
Autor: Grupo 5

5.Gráfica

Gráfica original

plot(df_pares$x, df_pares$y,
     pch  = 20,
     col  = rgb(0.1, 0.4, 0.5, 0.4),
     xlab = "Año de Descubrimiento (X)",
     ylab = "Año de Inicio de Producción (Y)",
     main = "Relación entre el Año de Descubrimiento y el Año de Inicio de Producción (Datos Originales)")

Debido a la dispersión de los puntos observados, se procede a aplicar una estrategia de tratamiento de datos antes de proponer el modelo.

5.1 Descarte de pares incompletos

Un modelo exponencial ajusta en escala \(\ln(y)\) vs. \(x\): cualquier valor artificial (como rellenar Y faltante con la media global) introduce puntos falsos que no siguen la relación real entre las variables. Si muchos años X distintos no tienen dato real de Y, rellenarlos todos con el mismo valor genera una banda plana que distorsiona la tendencia y hunde la correlación. Por eso no se rellenan los faltantes: se descartan los registros que no tengan ambos valores.

df_pares <- df_pares %>%
  filter(is.na(y) | y > 0) %>%
  filter(is.na(x) | x > 0) %>%
  filter(!is.na(x), !is.na(y))

cat("Pares completos reales (ambos datos presentes):", nrow(df_pares), "\n")
## Pares completos reales (ambos datos presentes): 1889

5.2 Agrupación de múltiples Y por X

Cuando un mismo año de descubrimiento (X) tiene múltiples años de inicio de producción (Y), se calcula la media aritmética para obtener un único par representativo.

pares <- df_pares %>%
  filter(!is.na(x), !is.na(y)) %>%
  group_by(x) %>%
  summarise(y = mean(y, na.rm = TRUE), n = n(), .groups = "drop") %>%
  arrange(x)

cat("Pares únicos para el modelo:", nrow(pares), "\n")
## Pares únicos para el modelo: 98
cat("Rango de años:", min(pares$x), "-", max(pares$x), "\n")
## Rango de años: 1905 - 2023

5.3 Estrategia de depuración

Se utiliza el método IQR (Rango Intercuartílico) para identificar y eliminar valores atípicos, más robusto que la desviación estándar. Se eliminan además los años con un único registro original.

\[LI = Q_1 - 1.5 \times IQR \qquad LS = Q_3 + 1.5 \times IQR\]

Q1      <- quantile(pares$y, 0.25)
Q3      <- quantile(pares$y, 0.75)
IQR_val <- Q3 - Q1
lim_inf <- Q1 - 1.5 * IQR_val
lim_sup <- Q3 + 1.5 * IQR_val

cat("Q1:", round(Q1, 2), "\n")
## Q1: 1966.48
cat("Q3:", round(Q3, 2), "\n")
## Q3: 2008.54
cat("IQR:", round(IQR_val, 2), "\n")
## IQR: 42.06
cat("Límite inferior:", round(lim_inf, 2), "\n")
## Límite inferior: 1903.39
cat("Límite superior:", round(lim_sup, 2), "\n")
## Límite superior: 2071.62
pares_dep <- pares %>%
  filter(y >= lim_inf & y <= lim_sup) %>%
  filter(n > 1)

cat("\nPares antes de depuración:", nrow(pares), "\n")
## 
## Pares antes de depuración: 98
cat("Pares después de depuración:", nrow(pares_dep), "\n")
## Pares después de depuración: 85
cat("Pares eliminados:", nrow(pares) - nrow(pares_dep), "\n")
## Pares eliminados: 13

5.4 Tabla de pares depurados

pares_dep %>%
  select(x, y) %>%
  rename(`Año de Descubrimiento (X)`       = x,
         `Año de Inicio de Producción (Y)` = y) %>%
  mutate(`Año de Inicio de Producción (Y)` = round(`Año de Inicio de Producción (Y)`, 2)) %>%
  gt() %>%
  tab_header(
    title    = md("**Tabla de Pares Depurados**"),
    subtitle = md("Año de Descubrimiento y Año de Inicio de Producción")
  ) %>%
  tab_source_note(source_note = "Autor: Grupo 5") %>%
  cols_align(align = "center", columns = everything()) %>%
  tab_options(
    table.border.top.color            = "black",
    table.border.bottom.color         = "black",
    table.border.top.style            = "solid",
    table.border.bottom.style         = "solid",
    column_labels.font.weight         = "bold",
    column_labels.border.top.color    = "black",
    column_labels.border.bottom.color = "black",
    column_labels.border.bottom.width = px(2),
    heading.border.bottom.color       = "black",
    heading.border.bottom.width       = px(2),
    table_body.hlines.color           = "grey",
    table_body.border.bottom.color    = "black"
  )
Tabla de Pares Depurados
Año de Descubrimiento y Año de Inicio de Producción
Año de Descubrimiento (X) Año de Inicio de Producción (Y)
1918 1921.00
1927 1940.50
1928 1944.50
1932 1935.50
1937 1963.50
1938 1966.00
1941 1949.50
1944 1944.00
1945 1947.00
1946 1973.33
1948 1951.25
1949 1955.14
1951 1966.57
1952 1958.86
1953 1964.00
1954 1966.44
1955 1977.08
1956 1973.14
1957 1968.83
1958 1975.19
1959 1972.62
1960 1980.92
1961 1982.13
1962 1974.85
1963 1972.91
1964 1977.53
1965 1984.88
1966 1980.67
1967 1989.28
1968 1986.30
1969 1990.54
1970 1986.76
1971 1992.09
1972 1988.59
1973 1996.82
1974 1996.90
1975 1993.76
1976 1998.35
1977 1997.77
1978 1995.83
1979 1998.60
1980 1995.50
1981 1993.96
1982 1998.61
1983 1999.94
1984 1998.90
1985 2002.50
1986 2000.74
1987 2000.71
1988 2001.15
1989 2005.10
1990 2005.74
1991 2002.04
1992 2007.58
1993 2004.67
1994 2005.00
1995 2003.85
1996 2004.48
1997 2005.50
1998 2004.70
1999 2007.71
2000 2010.47
2001 2008.81
2002 2012.04
2003 2013.70
2004 2013.96
2005 2015.52
2006 2015.71
2007 2016.16
2008 2015.51
2009 2017.67
2010 2019.69
2011 2020.10
2012 2023.00
2013 2022.81
2014 2021.02
2015 2020.25
2016 2021.93
2017 2023.10
2018 2023.79
2019 2022.68
2020 2025.07
2021 2022.50
2022 2027.60
2023 2023.67
Autor: Grupo 5

5.5 Gráfica simplificada

plot(pares_dep$x, pares_dep$y,
     pch  = 20,
     col  = rgb(0.1, 0.4, 0.5, 0.6),
     xlab = "Año de Descubrimiento (X)",
     ylab = "Año de Inicio de Producción (Y)",
     main = "Relación entre el Año de Descubrimiento y el Año de Inicio de Producción")


6.Conjetura del modelo matemático

Observando la gráfica de los datos depurados, se propone un Modelo de Regresión Exponencial, ya que los datos presentan un crecimiento continuo donde la tasa de cambio es proporcional al valor actual de Y. Este modelo tiene la forma:

\[y = a \cdot e^{b \cdot x}\]

A diferencia del modelo potencial (\(y = a \cdot x^b\)), en el modelo exponencial la variable independiente X aparece en el exponente, lo que implica un crecimiento que se acelera de forma constante.


7.Transformación de la variable (Año Cero)

Al trabajar con X en años calendario (valores grandes como 1940-2020), la pendiente b de un modelo exponencial resulta extremadamente pequeña, y el intercepto a pierde interpretación práctica (representaría el valor de Y en el año 0 del calendario, algo sin sentido físico). Por esta razón, se define un año de referencia (año cero), de modo que la variable independiente pase a representar la antigüedad en años desde ese punto de referencia.

\[X_{transformada} = X - X_0\]

x0_referencia <- floor(min(pares_dep$x) / 10) * 10

pares_dep <- pares_dep %>%
  mutate(x_transformada = x - x0_referencia)

cat("Año de referencia (Año Cero, X0):", x0_referencia, "\n\n")
## Año de referencia (Año Cero, X0): 1910
cat("Variable X (original)      -> rango: [", min(pares_dep$x), ",", max(pares_dep$x), "]\n")
## Variable X (original)      -> rango: [ 1918 , 2023 ]
cat("Variable X (transformada)  -> rango: [", min(pares_dep$x_transformada), ",", max(pares_dep$x_transformada), "]\n")
## Variable X (transformada)  -> rango: [ 8 , 113 ]
cat("Variable Y (sin transformar) -> rango: [", round(min(pares_dep$y), 2), ",", round(max(pares_dep$y), 2), "]\n")
## Variable Y (sin transformar) -> rango: [ 1921 , 2027.6 ]

De aquí en adelante, el modelo se ajusta usando X transformada. Para cualquier predicción se recibirá un año calendario real y se convertirá internamente a la escala transformada; el resultado final se reporta siempre en la variable original (año calendario).


8.Cálculo de parámetros

Para ajustar el modelo exponencial se aplica la transformación logarítmica sobre Y, usando la variable X transformada:

\[\ln(y) = \ln(a) + b \cdot X_{transformada}\]

log_y <- log(pares_dep$y)
m_exp <- lm(log_y ~ x_transformada, data = pares_dep)

Pendiente e intercepto

coefs  <- coef(m_exp)
b_coef <- coefs[2]
a_coef <- exp(coefs[1])

cat("Intercepto (a) :", round(a_coef, 6), "\n")
## Intercepto (a) : 1929.836
cat("Pendiente  (b) :", round(b_coef, 8), "\n")
## Pendiente  (b) : 0.00045697
cat("\nEcuación del modelo (en X transformada):\n")
## 
## Ecuación del modelo (en X transformada):
cat("y =", round(a_coef, 4), "* exp(", round(b_coef, 6), "* (x -", x0_referencia, "))\n")
## y = 1929.836 * exp( 0.000457 * (x - 1910 ))

9.Comparación del modelo con la realidad

x_grid            <- seq(min(pares_dep$x), max(pares_dep$x), length.out = 400)
x_grid_transf      <- x_grid - x0_referencia
y_grid            <- a_coef * exp(b_coef * x_grid_transf)

plot(pares_dep$x, pares_dep$y,
     pch  = 20,
     col  = rgb(0.1, 0.4, 0.5, 0.6),
     xlab = "Año de Descubrimiento (X)",
     ylab = "Año de Inicio de Producción (Y)",
     main = "Superposición: Modelo Exponencial y Datos Reales")

lines(x_grid, y_grid, col = "firebrick3", lwd = 3)

legend("topleft",
       legend = c("Datos reales", "Modelo exponencial"),
       col    = c(rgb(0.1, 0.4, 0.5, 0.6), "firebrick3"),
       pch    = c(20, NA),
       lty    = c(NA, 1),
       lwd    = c(NA, 3),
       bty    = "n")

La curva se sigue viendo casi recta al graficarla sobre la ventana observada (los años reales de los datos, un tramo relativamente angosto de aproximadamente un siglo). Esto es una característica propia de los datos: el exponente b es pequeño porque el rango de Y también es pequeño (todos los años están entre 1900 y 2023 aproximadamente), por lo que en ese tramo puntual la curva exponencial se aproxima mucho a una recta. La transformación a año cero no cambia la forma de la curva en sí, pero sí corrige el problema real de fondo: evita que el intercepto a sea un número sin sentido físico (el valor de Y en el año 0 del calendario) y permite reportar e interpretar el modelo con una escala coherente con los datos.


10.Test de Pearson

Correlación lineal

r <- cor(pares_dep$x_transformada, log(pares_dep$y))
cat("Correlación de Pearson (r):", round(r, 4), "\n")
## Correlación de Pearson (r): 0.9692

Coeficiente de determinación

En modelos no lineales como el exponencial, el coeficiente de determinación R² no se calcula, ya que no aplica como medida de bondad de ajuste fuera de rectas o planos. La calidad del ajuste se evalúa visualmente mediante la superposición y mediante la correlación de Pearson entre X transformada y \(\ln(Y)\).


11.Restricciones

Paso 1 - Dominio de X: la variable independiente (año de descubrimiento, transformada a antigüedad desde el año cero) está definida para valores enteros no negativos dentro del rango observado.

cat("Dominio de X (transformada): [", min(pares_dep$x_transformada), ",", max(pares_dep$x_transformada), "]\n")
## Dominio de X (transformada): [ 8 , 113 ]
cat("Dominio de X (año calendario equivalente): [", min(pares_dep$x), ",", max(pares_dep$x), "]\n")
## Dominio de X (año calendario equivalente): [ 1918 , 2023 ]

Paso 2 - Dominio de Y: la variable dependiente (año de inicio de producción) toma valores reales positivos dentro del rango observado en los datos depurados.

cat("Dominio de Y: [", round(min(pares_dep$y), 2), ",", round(max(pares_dep$y), 2), "]\n")
## Dominio de Y: [ 1921 , 2027.6 ]

Paso 3 - Ecuación del modelo: dado que \(y = a \cdot e^{b \cdot (x - X_0)}\) siempre produce valores positivos para cualquier valor real de X, no existe una restricción matemática que excluya valores del dominio. Aun así, esta sección se incluye de forma obligatoria como parte del procedimiento, aunque no existan restricciones que aplicar.

cat("Ecuación: y = a * exp(b * (x -", x0_referencia, "))\n")
## Ecuación: y = a * exp(b * (x - 1910 ))
cat("Valores de Y predichos siempre positivos: TRUE\n")
## Valores de Y predichos siempre positivos: TRUE
cat("Restricción matemática: No existe\n")
## Restricción matemática: No existe

12.Estimación del modelo

Las predicciones se solicitan y se reportan en la variable original (año calendario real); internamente el año se convierte a la escala transformada (antigüedad desde el año cero) antes de aplicar la ecuación.

anio_estimar        <- 2025
anio_estimar_transf <- anio_estimar - x0_referencia
y_estimado          <- a_coef * exp(b_coef * anio_estimar_transf)
cat("Estimación para el año", anio_estimar, ":\n")
## Estimación para el año 2025 :
cat("Año de inicio de producción estimado:", round(y_estimado, 2), "\n\n")
## Año de inicio de producción estimado: 2033.96
anio_estimar2        <- 2030
anio_estimar2_transf <- anio_estimar2 - x0_referencia
y_estimado2          <- a_coef * exp(b_coef * anio_estimar2_transf)
cat("Estimación para el año", anio_estimar2, ":\n")
## Estimación para el año 2030 :
cat("Año de inicio de producción estimado:", round(y_estimado2, 2), "\n")
## Año de inicio de producción estimado: 2038.62

13.Conclusión

Ecuacion <- paste0(
  "y = ", round(a_coef, 4),
  " * exp(", round(b_coef, 6), " * (x - ", x0_referencia, "))"
)

data.frame(
  `Variable Independiente` = "Año de Descubrimiento (antigüedad desde año cero)",
  `Variable Dependiente`   = "Año de Inicio de Producción",
  `Año Cero (X0)`          = x0_referencia,
  `Test Pearson`           = round(r, 2),
  `Ecuación del modelo`    = Ecuacion,
  `Rango válido de X`      = paste0("[", min(pares_dep$x), ", ", max(pares_dep$x), "]"),
  check.names = FALSE
) %>%
  gt() %>%
  tab_header(
    title    = md("**Tabla N°1**"),
    subtitle = md("**Resumen del modelo de regresión exponencial**")
  ) %>%
  tab_source_note(source_note = md("Autor: Grupo 5")) %>%
  cols_align(align = "center", columns = everything()) %>%
  tab_options(
    table.border.top.color            = "black",
    table.border.bottom.color         = "black",
    table.border.top.style            = "solid",
    table.border.bottom.style         = "solid",
    column_labels.font.weight         = "bold",
    column_labels.border.top.color    = "black",
    column_labels.border.bottom.color = "black",
    column_labels.border.bottom.width = px(2),
    heading.border.bottom.color       = "black",
    heading.border.bottom.width       = px(2),
    table_body.hlines.color           = "grey",
    table_body.border.bottom.color    = "black"
  )
Tabla N°1
Resumen del modelo de regresión exponencial
Variable Independiente Variable Dependiente Año Cero (X0) Test Pearson Ecuación del modelo Rango válido de X
Año de Descubrimiento (antigüedad desde año cero) Año de Inicio de Producción 1910 0.97 y = 1929.836 * exp(0.000457 * (x - 1910)) [1918, 2023]
Autor: Grupo 5

Entre el año de descubrimiento (X) y el año de inicio de producción (Y) existe una relación exponencial. Definiendo el año 1910 como año cero, la ecuación del modelo queda expresada como:

y = 1929.836 * exp(0.000457 * (x - 1910))

Siendo X el año calendario de descubrimiento del yacimiento (convertido internamente a antigüedad desde el año 1910) y Y el año estimado de inicio de producción. El modelo no presenta restricciones matemáticas ya que la función exponencial siempre produce valores positivos, aunque su uso queda limitado al rango de datos observado. Con una correlación de Pearson de 0.97, el modelo refleja que a medida que los años de descubrimiento avanzan, el inicio de producción también tiende a adelantarse, evidenciando la mejora sostenida en los tiempos de desarrollo de los yacimientos petroleros a nivel mundial.