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
library(MASS) # regresión lineal robusta (rlm)
cat("Librerías cargadas: readr, dplyr, ggplot2, gt, stringr, DT, MASS")Librerías cargadas: readr, dplyr, ggplot2, gt, stringr, DT, MASS
## DATASET ##
# Archivo con las variables AVG_PRODUCTION y CUMULATIVE_PRODUCTION.
nombre_archivo <- "oil_and_gas_leases_data.csv.csv"
# 1) Si el archivo existe tal cual en la carpeta de trabajo actual, se usa directamente.
if (file.exists(nombre_archivo)) {
ruta_archivo <- nombre_archivo
# 2) Si no, se busca cualquier .csv cuyo nombre contenga "oil_and_gas" en la
# carpeta de trabajo actual (por si el nombre real difiere un poco).
} else {
candidatos <- list.files(pattern = "oil_and_gas.*\\.csv$", ignore.case = TRUE, full.names = TRUE)
if (length(candidatos) >= 1) {
ruta_archivo <- candidatos[1]
# 3) Si sigue sin encontrarse y la sesión es interactiva (RStudio, no Knit),
# se abre una ventana para elegir el archivo manualmente.
} else if (interactive()) {
ruta_archivo <- file.choose()
# 4) Si nada de lo anterior funcionó (p. ej. al hacer Knit sin el archivo
# presente), se detiene con un mensaje claro en vez de un error críptico.
} else {
stop(
"No se encontro el archivo '", nombre_archivo, "' en la carpeta de trabajo actual (",
getwd(), "). Copia el CSV a esa carpeta, o edita 'nombre_archivo' en este chunk ",
"con la ruta completa (ej: 'C:/Users/Hp/Downloads/oil_and_gas_leases_data.csv.csv')."
)
}
}
cat("Archivo cargado:", ruta_archivo, "\n")Archivo cargado: ./oil_and_gas_leases_data (1).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$AVG_PRODUCTION))) {
datos$AVG_PRODUCTION[is.na(datos$AVG_PRODUCTION)] <- mean(datos$AVG_PRODUCTION, 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 si se necesitaran como respaldo)
datos <- datos %>%
filter(AVG_PRODUCTION > 0, CUMULATIVE_PRODUCTION > 0)
## Estructura de los datos
str(datos[, c("AVG_PRODUCTION", "CUMULATIVE_PRODUCTION")])tibble [47,757 × 2] (S3: tbl_df/tbl/data.frame)
$ AVG_PRODUCTION : num [1:47757] 859 5001 1758 377 24322 ...
$ CUMULATIVE_PRODUCTION: num [1:47757] 47225 275063 82624 7544 681006 ...
Según la Tabla de Variables del proyecto, se eligió el siguiente par por ser conceptualmente directamente proporcional: un pozo con mayor producción promedio (ritmo de extracción) tiende a acumular, en total, una mayor cantidad de hidrocarburos.
Se definió la Producción Promedio Anual
(AVG_PRODUCTION, cuantitativa continua, proporción, medida
en barriles/año) como variable independiente / causa
(x), ya que representa el ritmo al que un pozo extrae
hidrocarburos.
La Producción Acumulada
(CUMULATIVE_PRODUCTION, cuantitativa continua, proporción,
medida en barriles) actúa como variable dependiente / efecto
(y), pues refleja la cantidad total de hidrocarburos extraídos
a lo largo de la vida del pozo.
Ambas variables cumplen la condición buscada: al aumentar x, y también aumenta de forma sostenida, lo que se comprueba en las secciones siguientes.
El dataset depurado contiene 47757 pozos, con valores de Producción Promedio (x) muy dispersos y sin repetirse exactamente entre pozos. Trabajar con los 47757 pares crudos genera una nube saturada y ruidosa (se muestra completa en la siguiente sección). Por ello, para el ajuste del modelo se ordenan los pozos según su Producción Promedio y se agrupan en 25 percentiles (grupos de igual cantidad de pozos), calculando la media de ambas variables dentro de cada grupo. Esto entrega un único par (x̄, ȳ) por percentil, que resume el comportamiento típico de los pozos de cada tramo.
tabla_xy <- datos %>%
mutate(percentil = ntile(AVG_PRODUCTION, 25)) %>%
group_by(percentil) %>%
summarise(
n_pozos = n(),
x_media = mean(AVG_PRODUCTION),
y_media = mean(CUMULATIVE_PRODUCTION),
.groups = "drop"
) %>%
arrange(percentil)
cat("Total de pares (x̄, ȳ) obtenidos, uno por percentil:", nrow(tabla_xy), "\n")Total de pares (x̄, ȳ) obtenidos, uno por percentil: 25
datatable(
tabla_xy,
caption = htmltools::tags$caption(
style = "caption-side: top; text-align: left; font-size: 16px; font-weight: 700; color:#1F2A33;",
"Tabla N°1: Pares (x̄, ȳ) — Media de Producción Acumulada por Percentil de Producción Promedio"
),
rownames = FALSE,
class = "display compact stripe hover",
options = list(
pageLength = 10,
dom = "ltip",
columnDefs = list(list(className = "dt-center", targets = "_all"))
)
) %>%
formatRound(columns = c("x_media", "y_media"), digits = 2)max_x <- max(tabla_xy$x_media)
min_x <- min(tabla_xy$x_media)
max_y <- max(tabla_xy$y_media)
min_y <- min(tabla_xy$y_media)
resumen_general <- data.frame(
Variable = c("AVG_PRODUCTION (X)", "CUMULATIVE_PRODUCTION (Y)"),
Minimo = round(c(min_x, min_y), 2),
Maximo = round(c(max_x, max_y), 2),
Rango = round(c(max_x - min_x, max_y - min_y), 2),
Media = round(c(mean(tabla_xy$x_media), mean(tabla_xy$y_media)), 2)
)
resumen_general %>%
gt() %>%
tab_header(
title = md("**Tabla N°2: Resumen General de las Variables (pares x̄, ȳ)**")
) %>%
tab_source_note(source_note = "Autor: Valeska Araujo") %>%
cols_align(align = "center", everything())| Tabla N°2: Resumen General de las Variables (pares x̄, ȳ) | ||||
| Variable | Minimo | Maximo | Rango | Media |
|---|---|---|---|---|
| AVG_PRODUCTION (X) | 67.14 | 47125.88 | 47058.75 | 6540.58 |
| CUMULATIVE_PRODUCTION (Y) | 966.50 | 566564.83 | 565598.33 | 144091.74 |
| Autor: Valeska Araujo | ||||
Se grafican todos los pozos del dataset, sin agrupar ni resumir, para visualizar la nube completa de puntos.
par(mar = c(5, 5, 4, 2))
plot(datos$AVG_PRODUCTION, datos$CUMULATIVE_PRODUCTION,
pch = 16, cex = 0.35,
col = adjustcolor("#2E86AB", alpha.f = 0.15),
xlab = "X (Producción Promedio Anual)", ylab = "Y (Producción Acumulada)",
main = "Gráfica N°1: Nube de Puntos — Todos los Valores",
cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
box()Con los datos ya resumidos por percentil (media), la tendencia ascendente se aprecia con mayor claridad, sin el ruido de los pares crudos.
par(mar = c(5, 5, 4, 2))
plot(tabla_xy$x_media, tabla_xy$y_media, pch = 19, col = "#2E86AB",
xlab = "X̄ (Media Producción Promedio Anual)", ylab = "Ȳ (Media Producción Acumulada)",
main = "Gráfica N°2: Nube de Puntos — Pares (x̄, ȳ)",
cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
box()Al observar ambas nubes de puntos, se aprecia una tendencia ascendente y prácticamente recta: a medida que aumenta la Producción Promedio Anual, la Producción Acumulada crece de forma sostenida y proporcional en todo el rango de X, sin cambios bruscos de patrón que obliguen a segmentar el análisis por partes.
Por lo tanto, se conjetura un modelo lineal simple, directamente proporcional: \[y = a + bx\]
con pendiente \(b > 0\) (relación directamente proporcional / ascendente).
El modelo se ajusta sobre los pares (x̄, ȳ) por percentil (Tabla N°1).
Como estos promedios pueden verse afectados por pozos con producciones
extremas dentro de cada tramo, se usa regresión lineal
robusta (MASS::rlm, M-estimación de Huber) en
lugar de mínimos cuadrados ordinarios: esta técnica da menos peso a los
puntos que se alejan del patrón general, logrando que la recta siga de
cerca a la mayoría de los 25 pares, sin dejarse
arrastrar por unos pocos percentiles atípicos.
modelo <- rlm(y_media ~ x_media, data = tabla_xy)
a <- coef(modelo)[1]
b <- coef(modelo)[2]
cat("Modelo ajustado (robusto): y =", round(a, 2), "+", round(b, 2), "* x\n")Modelo ajustado (robusto): y = 7295.35 + 24.3 * x
Call: rlm(formula = y_media ~ x_media, data = tabla_xy)
Residuals:
Min 1Q Median 3Q Max
-596638.6 -8057.1 -127.3 6342.8 38409.6
Coefficients:
Value Std. Error t value
(Intercept) 7295.3520 2628.6887 2.7753
x_media 24.3015 0.2191 110.9061
Residual standard error: 11950 on 23 degrees of freedom
data.frame(
Parametro = c("a (Intercepto)", "b (Pendiente)"),
Valor = round(c(a, b), 4)
) %>%
gt() %>%
tab_header(
title = md("**Tabla N°3: Parámetros del Modelo**")
) %>%
tab_source_note(source_note = "Autor: Valeska Araujo") %>%
cols_align(align = "center", everything())| Tabla N°3: Parámetros del Modelo | |
| Parametro | Valor |
|---|---|
| a (Intercepto) | 7295.3520 |
| b (Pendiente) | 24.3015 |
| Autor: Valeska Araujo | |
par(mar = c(5, 5, 4, 2))
plot(tabla_xy$x_media, tabla_xy$y_media, col = "#2E86AB", pch = 16,
xlab = "X̄ (Media Producción Promedio Anual)", ylab = "Ȳ (Media Producción Acumulada)",
main = "Gráfica N°3: Sobreponer Modelo con la Realidad — Pares (x̄, ȳ)",
cex.main = 0.9, frame.plot = FALSE)
grid(nx = NULL, ny = NULL, col = "#D7DBDD", lty = "dotted")
abline(modelo, col = "#1F2A33", lwd = 3)
legend("topleft", legend = c("Datos (x̄, ȳ)", "Modelo Lineal"),
col = c("#2E86AB", "#1F2A33"), pch = c(16, NA), lwd = c(NA, 3), bty = "n")
box()La recta, ajustada con regresión robusta, sigue de cerca la dirección de la mayoría de los puntos —sin dejarse desviar por los pocos percentiles con valores más extremos— confirmando una relación lineal ascendente.
El coeficiente de correlación de Pearson, calculado mediante
cor(x, y) sobre los pares (x̄, ȳ) por percentil (Tabla N°1),
debe superar 0.7 (en valor absoluto) para aceptar el
modelo lineal, dado que toma valores entre -1 y 1.
r_medias <- cor.test(tabla_xy$x_media, tabla_xy$y_media)
tabla_pearson <- data.frame(
Conjunto = "Pares (x̄, ȳ) por percentil",
r = round(r_medias$estimate, 4),
R2 = round(r_medias$estimate^2, 4),
Supera_0.7 = abs(r_medias$estimate) > 0.7
)
tabla_pearson %>%
gt() %>%
tab_header(
title = md("**Tabla N°4: Test de Bondad de Ajuste**"),
subtitle = "Umbral |r| > 0.7"
) %>%
tab_source_note(source_note = "Autor: Valeska Araujo") %>%
cols_align(align = "center", everything())| Tabla N°4: Test de Bondad de Ajuste | |||
| Umbral |r| > 0.7 | |||
| Conjunto | r | R2 | Supera_0.7 |
|---|---|---|---|
| Pares (x̄, ȳ) por percentil | 0.9029 | 0.8153 | TRUE |
| Autor: Valeska Araujo | |||
Sobre los pares (x̄, ȳ), \(r =\) 0.903 (\(R^2 =\) 0.815), muy por encima del umbral de 0.7, por lo que el modelo lineal se acepta para describir la relación entre las variables.
data.frame(
Variable = c("Y (CUMULATIVE_PRODUCTION)", "X (AVG_PRODUCTION)"),
Dominio = c("R+ : Y \u2208 (0, +\u221e)", "R+ : X \u2208 (0, +\u221e)")
) %>%
gt() %>%
tab_header(
title = md("**Tabla N°6: Dominios de las Variables**")
) %>%
tab_source_note(source_note = "Autor: Valeska Araujo") %>%
cols_align(align = "left", everything())| Tabla N°6: Dominios de las Variables | |
| Variable | Dominio |
|---|---|
| Y (CUMULATIVE_PRODUCTION) | R+ : Y ∈ (0, +∞) |
| X (AVG_PRODUCTION) | R+ : X ∈ (0, +∞) |
| Autor: Valeska Araujo | |
¿Existen valores de X que, utilizando la ecuación del modelo, nos den valores fuera del dominio de Y?
El modelo no tiene restricciones, ya que al evaluar la ecuación en el valor mínimo observado de X (67 barriles/año), el resultado sigue estando dentro del dominio de Y (Y > 0):
\[Y = 7295.35 + 24.3 \times 67 = 8,926.9\]
Como este valor mínimo de X ya arroja un Y positivo, y la pendiente es positiva (\(b > 0\)), cualquier X dentro del rango observado (y mayor a este mínimo) también producirá valores de Y dentro de su dominio. Aun así, el modelo es válido únicamente dentro del rango de X observado, aproximadamente entre 67 y 47,126 barriles/año de Producción Promedio; no se recomienda extrapolar fuera de ese rango, ya que el comportamiento de pozos con ritmos de producción muy distintos a los observados no está garantizado por los datos.
Ingresa un valor de Producción Promedio (X) y la calculadora estima la Producción Acumulada (Y) usando la ecuación del modelo, respetando el dominio \(X \in (0, +\infty)\). Si el valor ingresado no es válido, o si está fuera del rango de X observado en los datos (67 – 47,126), se muestra un aviso.
Modelo: Y = 7295.35 + 24.30 × X
Entre la producción promedio (X) y la producción acumulada (Y) existe una relación lineal simple cuya ecuación matemática es:
\[Y = 7295.35 + 24.3\,X\]
Siendo X la producción promedio, medida en las unidades correspondientes de producción; y Y la producción acumulada. Con un coeficiente de correlación de 0.903, el modelo explica 81.5% de la variabilidad de Y (\(R^2\)); el resto (18.5%) es por otros factores, evidenciando una relación lineal positiva y de alta intensidad entre las variables.