# Instalación automática de paquetes faltantes (solo si no están ya instalados)
paquetes_necesarios <- c("dplyr", "gt", "kableExtra")
paquetes_faltantes <- paquetes_necesarios[!(paquetes_necesarios %in% installed.packages()[, "Package"])]
if (length(paquetes_faltantes) > 0) {
install.packages(paquetes_faltantes, repos = "https://cloud.r-project.org")
}
library(dplyr)
library(gt)
library(kableExtra)
# IMPORTANTE: ajustar esta ruta a la ubicación local del archivo
ruta_carpeta <- "C:/Users/ASUS/Desktop/Estadistica/new_york_exel"
archivo_csv <- "Oil__Gas____Other_Regulated_Wells__Beginning_1860.csv"
if (dir.exists(ruta_carpeta)) {
setwd(ruta_carpeta)
} else {
warning("La carpeta indicada en 'ruta_carpeta' no existe en este equipo. ",
"Se intentará leer 'archivo_csv' tal como está escrito (ruta ",
"relativa al directorio de trabajo actual o ruta absoluta). ",
"Ajusta 'ruta_carpeta' o escribe la ruta completa en 'archivo_csv'.")
}
lineas <- readLines(archivo_csv, encoding = "latin1", warn = FALSE)
Encoding(lineas) <- "latin1"
lineas <- iconv(lineas, from = "latin1", to = "UTF-8")
# Detección de separador (evita depender de un delimitador fijo)
separadores <- c(",", ";", "\t", "|")
mejor_sep <- NULL
mejor_ncol <- 1
for (s in separadores) {
n_campos <- tryCatch(length(strsplit(lineas[1], s, fixed = TRUE)[[1]]),
error = function(e) 1)
if (!is.na(n_campos) && n_campos > mejor_ncol) {
mejor_ncol <- n_campos
mejor_sep <- s
}
}
if (is.null(mejor_sep)) mejor_sep <- ";"
cat("Separador detectado:", ifelse(mejor_sep == "\t", "TAB", mejor_sep),
"| Columnas detectadas:", mejor_ncol, "\n")
## Separador detectado: , | Columnas detectadas: 58
datos <- read.csv(text = lineas, sep = mejor_sep, header = TRUE,
check.names = TRUE, stringsAsFactors = FALSE)
cat("Número de registros:", nrow(datos), "\n")
## Número de registros: 47390
cat("Número de variables:", ncol(datos), "\n")
## Número de variables: 52
Extracto del dataset (55 variables, primeras 5 filas):
datos %>%
head(5) %>%
kbl(caption = "Extracto del dataset: todas las columnas, primeras 5 filas") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
font_size = 11, full_width = FALSE) %>%
scroll_box(width = "100%", height = "420px")
| API.Well.Number | County.Code | API.Hole.Number | Sidetrack | Completion | Well.Name | Company.Name | Operator.Number | Well.Type | Map.Symbol | Well.Status | Status.Date | Permit.Application.Date | Permit.Issued.Date | Date.Spudded | Date.of.Total.Depth | Date.Well.Completed | Date.Well.Plugged | Date.Well.Confidentiality.Ends | Confidentiality.Code | Town | Quad | Quad.Section | Producing.Field | Producing.Formation | Financial.Security | Slant | County | Region | State.Lease | Proposed.Depth..ft | Surface.Longitude | Surface.Latitude | Bottom.Hole.Longitude | Bottom.Hole.Latitude | True.Vertical.Depth..ft | Measured.Depth..ft | Kickoff..ft | Drilled.Depth..ft | Elevation..ft | Original.Well.Type | Permit.Fee | Objective.Formation | Depth.Fee | Spacing | Spacing.Acres | Integration | Hearing.Date | Date.Last.Modified | DEC.Database.Link | Location.1 | Georeference |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 31003026700000 | 3 | 2670 | 0 | 0 | Francisco 1 | Van Gilder | 9279 | DW | DP | PA | 03/11/1953 | 04/10/1953 | Pre-1989 Well (N/A) | Amity | Belmont | F | False | Vertical | Allegany | 9 | NA | 0 | -78.01913 | 42.19713 | -78.01913 | 42.19713 | 2006 | 2006 | 0 | 2006 | 1815 | NL | 0 | 0 | NA | 10/12/1995 12:00:00 AM | http://extapps.dec.ny.gov/cfmx/extapps/GasOil/search/wells/index.cfm?api=31003026700000 | (42.19713, -78.01913) | POINT (-78.01913 42.19713) | ||||||||||||
| 31003045990000 | 3 | 4599 | 0 | 0 | Francisco 1 | Christman Raymond L. | 9111 | OW | OW | UN | Pre-1989 Well (N/A) | Amity | Belmont | F | False | Vertical | Allegany | 9 | NA | 0 | -78.02615 | 42.20221 | -78.02615 | 42.20221 | 0 | 0 | 0 | 0 | 1520 | NL | 0 | 0 | NA | 01/24/2003 03:53:37 PM | http://extapps.dec.ny.gov/cfmx/extapps/GasOil/search/wells/index.cfm?api=31003045990000 | (42.20221, -78.02615) | POINT (-78.02615 42.20221) | ||||||||||||||
| 31003048420000 | 3 | 4842 | 0 | 0 | Guyer Devonian #10 | Pennzoil Products Co. | 29 | NL | O | VP | Pre-1989 Well (N/A) | False | Vertical | 9 | NA | NA | NA | NA | NA | NA | 0 | 0 | 0 | 0 | NA | NL | 0 | 0 | NA | 12/27/2013 03:00:05 PM | http://extapps.dec.ny.gov/cfmx/extapps/GasOil/search/wells/index.cfm?api=31003048420000 | ||||||||||||||||||||
| 31003054190000 | 3 | 5419 | 0 | 0 | Regan 2142 | Iroquois Gas Corp. | 16 | GD | GWP | PA | Pre-1989 Well (N/A) | Alma | Wellsville South | D | False | Vertical | Allegany | 9 | NA | 0 | -77.97909 | 42.07020 | -77.97909 | 42.07020 | 0 | 0 | 0 | 0 | 2100 | NL | 0 | 0 | NA | 02/28/1995 12:00:00 AM | http://extapps.dec.ny.gov/cfmx/extapps/GasOil/search/wells/index.cfm?api=31003054190000 | (42.0702, -77.97909) | POINT (-77.97909 42.0702) | ||||||||||||||
| 31003069290000 | 3 | 6929 | 0 | 0 | 2667 | Bradley Producing Company | 9673 | NL | O | VP | Pre-1989 Well (N/A) | False | Vertical | Statewide | 9 | NA | NA | NA | NA | NA | NA | 0 | 0 | 0 | 0 | NA | NL | 0 | 0 | NA | 11/28/2018 10:25:33 AM | http://extapps.dec.ny.gov/cfmx/extapps/GasOil/search/wells/index.cfm?api=31003069290000 |
La tarifa por profundidad (Depth Fee)
se calcula por reglamento a partir de la profundidad
propuesta (Proposed Depth), según una tabla de
tarifas escalonada. Hay una relación causa (X) → efecto (Y) clara:
Proposed Depth, ft.Depth Fee (USD).Las columnas se localizan por patrón con grepl() en vez
de por nombre fijo, ya que check.names = TRUE transforma
los encabezados originales (espacios y comas por puntos).
col_proposed <- names(datos)[grepl("proposed.*depth", names(datos), ignore.case = TRUE)]
col_fee <- names(datos)[grepl("^depth.*fee", names(datos), ignore.case = TRUE)]
if (length(col_proposed) == 0 || length(col_fee) == 0) {
columnas_txt <- paste(names(datos), collapse = " | ")
stop("ERROR: No se encontraron las columnas esperadas ",
"(se buscaron los patrones 'proposed...depth' y 'depth...fee').\n",
"COLUMNAS ENCONTRADAS EN EL ARCHIVO:\n", columnas_txt)
}
col_proposed <- col_proposed[1]
col_fee <- col_fee[1]
cat("Columna Proposed Depth identificada:", col_proposed, "\n")
## Columna Proposed Depth identificada: Proposed.Depth..ft
cat("Columna Depth Fee identificada:", col_fee, "\n")
## Columna Depth Fee identificada: Depth.Fee
x_raw <- suppressWarnings(as.numeric(as.character(datos[[col_proposed]])))
y_raw <- suppressWarnings(as.numeric(as.character(datos[[col_fee]])))
cat("Registros con Proposed Depth:", sum(!is.na(x_raw) & x_raw > 0), "\n")
## Registros con Proposed Depth: 16507
cat("Registros con Depth Fee:", sum(!is.na(y_raw) & y_raw > 0), "\n")
## Registros con Depth Fee: 11696
Solo se conservan los pares donde ambas variables son mayores que 0; un valor de 0 o vacío indica que el permiso no llegó a esa etapa administrativa.
pares_iniciales <- data.frame(x = x_raw, y = y_raw) %>%
filter(!is.na(x), !is.na(y), x > 0, y > 0)
cat("Tamaño muestral (pares completos X > 0 y Y > 0):", nrow(pares_iniciales), "\n")
## Tamaño muestral (pares completos X > 0 y Y > 0): 11175
Vista previa (primeras filas):
tabla_ini_gt <- pares_iniciales %>%
head(10) %>%
rename(`Proposed Depth, ft (X)` = x,
`Depth Fee, USD (Y)` = y)
tabla_ini_gt %>%
gt() %>%
tab_header(
title = md("**Tabla de Pares de Valores (vista previa)**"),
subtitle = md(paste0("Tamaño muestral n = ", nrow(pares_iniciales)))
) %>%
tab_source_note(source_note = "Autor: Grupo 1") %>%
cols_align(align = "center", columns = everything()) %>%
tab_style(
style = list(cell_fill(color = col_encabezado), cell_text(color = "white", weight = "bold")),
locations = cells_title()
) %>%
tab_style(
style = list(cell_fill(color = col_encabezado), cell_text(color = "white", weight = "bold")),
locations = cells_column_labels()
) %>%
tab_style(
style = list(cell_fill(color = col_fila_alt)),
locations = cells_body(rows = seq(1, nrow(tabla_ini_gt), 2))
) %>%
opt_table_outline(style = "solid", width = px(3), color = col_borde) %>%
tab_options(
table.border.top.color = col_borde,
table.border.bottom.color = col_borde,
table.border.top.style = "solid",
table.border.bottom.style = "solid",
column_labels.border.top.color = col_borde,
column_labels.border.bottom.color = col_borde,
column_labels.border.bottom.width = px(2),
heading.border.bottom.color = col_borde,
heading.border.bottom.width = px(2),
table_body.hlines.color = "#CBD5DE",
table_body.border.bottom.color = col_borde,
table.font.size = px(14),
data_row.padding = px(6),
table_body.border.top.style = "solid",
column_labels.background.color = col_encabezado
)
| Tabla de Pares de Valores (vista previa) | |
| Tamaño muestral n = 11175 | |
| Proposed Depth, ft (X) | Depth Fee, USD (Y) |
|---|---|
| 900 | 250 |
| 4200 | 1710 |
| 1555 | 500 |
| 1600 | 500 |
| 1783 | 760 |
| 1600 | 760 |
| 1750 | 760 |
| 1530 | 500 |
| 1250 | 375 |
| 2350 | 625 |
| Autor: Grupo 1 | |
plot(pares_iniciales$x, pares_iniciales$y,
pch = 20,
col = rgb(0.04, 0.18, 0.29, 0.15),
xlab = "Proposed Depth, ft (X)",
ylab = "Depth Fee, USD (Y)",
main = "Gráfica original: Proposed Depth en comparación con Depth Fee")
La nube de puntos original es compleja/caótica: un mismo X puede tener varios Y (ajustes de tarifa por año) y hay tarifas desproporcionadas (probables errores de digitación). Por eso se trata el dato antes de modelar.
Paso A — Omisión de outliers. Se calcula la razón
Depth Fee / Proposed Depth; si queda fuera de 0.05–1.0
USD/ft se descarta por ser casi con certeza un error de digitación.
pares_tratados <- pares_iniciales %>% mutate(razon = y / x)
n_antes <- nrow(pares_tratados)
pares_tratados <- pares_tratados %>% filter(razon >= 0.05, razon <= 1.0) %>% select(-razon)
n_despues <- nrow(pares_tratados)
cat("Pares antes del filtro de coherencia:", n_antes, "\n")
## Pares antes del filtro de coherencia: 11175
cat("Pares tras el filtro de coherencia:", n_despues, "\n")
## Pares tras el filtro de coherencia: 11134
cat("Descartados por razón no coherente (outliers):", n_antes - n_despues, "\n")
## Descartados por razón no coherente (outliers): 41
Paso B — Único X, único Y. Cuando una misma X tiene varios valores de Y, se calcula la media para obtener un único par por valor exacto de X (no es binning, no se crean rangos artificiales).
pares <- pares_tratados %>%
group_by(x) %>%
summarise(y = mean(y, na.rm = TRUE), .groups = "drop") %>%
arrange(x)
cat("Pares únicos tras el tratamiento (único X, único Y):", nrow(pares), "\n")
## Pares únicos tras el tratamiento (único X, único Y): 2319
Tras el tratamiento, la relación es clara y no requiere segmentar por partes.
plot(pares$x, pares$y,
pch = 20,
col = rgb(0.04, 0.18, 0.29, 0.35),
xlab = "Proposed Depth, ft (X)",
ylab = "Depth Fee, USD (Y)",
main = "Gráfica tras el tratamiento de datos")
La nube muestra una tendencia creciente y cóncava: la tarifa aumenta con la profundidad pero a un ritmo cada vez menor, típico de un modelo potencial:
\[y = a \cdot x^{b}\]
Al no ser lineal en sus parámetros, se linealiza aplicando logaritmo natural a ambas variables:
\[\ln(y) = \ln(a) + b \cdot \ln(x)\]
Se ajusta lm() sobre \((\ln x,
\ln y)\) y luego se recupera \(a\) con exp().
pares$log_x <- log(pares$x)
pares$log_y <- log(pares$y)
plot(pares$log_x, pares$log_y,
pch = 20,
col = rgb(0.04, 0.18, 0.29, 0.35),
xlab = "ln(Proposed Depth, ft) — eje X reducido",
ylab = "ln(Depth Fee, USD) — eje Y reducido",
main = "Linealización: ln(Y) en comparación con ln(X)")
m_pot <- lm(log_y ~ log_x, data = pares)
sum_reg <- summary(m_pot)
coefs <- coef(m_pot)
intercepto <- coefs[1]
pendiente <- coefs[2]
a <- exp(intercepto)
b <- pendiente
cat("Intercepto en espacio log [ln(a)] :", round(intercepto, 6), "\n")
## Intercepto en espacio log [ln(a)] : 0.080781
cat("Pendiente (b) :", round(b, 6), "\n")
## Pendiente (b) : 0.858846
cat("Parámetro a = exp(intercepto) :", round(a, 4), "\n")
## Parámetro a = exp(intercepto) : 1.0841
cat("\nEcuación del modelo potencial:\n")
##
## Ecuación del modelo potencial:
cat("y =", round(a, 4), "* x^(", round(b, 6), ")\n")
## y = 1.0841 * x^( 0.858846 )
Como \(b \approx 0.86 < 1\), el crecimiento es sublineal: la tarifa crece más lentamente que la profundidad.
x_grid <- seq(min(pares$x), max(pares$x), length.out = 400)
log_x_grid <- log(x_grid)
log_y_grid <- predict(m_pot, newdata = data.frame(log_x = log_x_grid))
y_grid <- exp(log_y_grid)
plot(pares$x, pares$y,
pch = 20,
col = rgb(0.04, 0.18, 0.29, 0.15),
xlab = "Proposed Depth, ft (X)",
ylab = "Depth Fee, USD (Y)",
main = "Superposición: Modelo Potencial y Datos Reales")
lines(x_grid, y_grid, col = col_curva, lwd = 3)
legend("topleft",
legend = c("Datos reales", "Modelo potencial"),
col = c(col_puntos, col_curva),
pch = c(20, NA),
lty = c(NA, 1),
lwd = c(NA, 3),
bty = "n")
Como el modelo se ajustó linealizado, el test de Pearson se calcula entre \(\ln(X)\) y \(\ln(Y)\), donde la relación es realmente lineal.
r <- cor(pares$log_x, pares$log_y)
r2 <- sum_reg$r.squared * 100
cat("Correlación de Pearson (r), entre ln(X) y ln(Y):", round(r, 4), "\n")
## Correlación de Pearson (r), entre ln(X) y ln(Y): 0.88
cat("Coeficiente de determinación (R²%):", round(r2, 2), "%\n")
## Coeficiente de determinación (R²%): 77.44 %
Con 0.88 de correlación de Pearson, el modelo muestra una asociación fuerte, coherente con una tarifa que depende de la profundidad por reglamento pero no de forma perfectamente determinística.
x_min <- round(min(pares$x), 2)
x_max <- round(max(pares$x), 2)
cat("Dominio válido de X (Proposed Depth, ft):", x_min, "a", x_max, "\n")
## Dominio válido de X (Proposed Depth, ft): 200 a 13450
x_estimar1 <- x_min
y_estimada1 <- a * x_estimar1^b
cat("Estimación para X =", x_estimar1, "ft:\n")
## Estimación para X = 200 ft:
cat("Depth Fee estimado: $", round(y_estimada1, 2), "\n\n")
## Depth Fee estimado: $ 102.64
x_estimar2 <- x_max
y_estimada2 <- a * x_estimar2^b
cat("Estimación para X =", x_estimar2, "ft:\n")
## Estimación para X = 13450 ft:
cat("Depth Fee estimado: $", round(y_estimada2, 2), "\n\n")
## Depth Fee estimado: $ 3810.83
# Ejemplo puntual de referencia
x_estimar3 <- 1500
y_estimada3 <- a * x_estimar3^b
cat("Estimación para X =", x_estimar3, "ft:\n")
## Estimación para X = 1500 ft:
cat("Depth Fee estimado: $", round(y_estimada3, 2), "\n")
## Depth Fee estimado: $ 579.24
Tabla resumen del modelo:
Ecuacion <- paste0("y = ", round(a, 4), " * x^(", round(b, 6), ")")
Tabla_resumen <- data.frame(
`Variable Independiente` = "Proposed Depth, ft",
`Variable Dependiente` = "Depth Fee, USD",
`Test Pearson (ln X y ln Y)` = round(r, 4),
`Coeficiente de determinación` = round(r2, 2),
`Ecuación del modelo` = Ecuacion,
check.names = FALSE
)
Tabla_resumen %>%
gt() %>%
tab_header(
title = md("**Tabla N°1**"),
subtitle = md("**Resumen del modelo de regresión potencial**")
) %>%
tab_source_note(source_note = md("Autor: Grupo 1")) %>%
cols_align(align = "center", columns = everything()) %>%
tab_style(
style = list(cell_fill(color = col_encabezado), cell_text(color = "white", weight = "bold")),
locations = cells_title()
) %>%
tab_style(
style = list(cell_fill(color = col_encabezado), cell_text(color = "white", weight = "bold")),
locations = cells_column_labels()
) %>%
tab_style(
style = list(cell_fill(color = col_fila_alt)),
locations = cells_body(rows = 1)
) %>%
opt_table_outline(style = "solid", width = px(3), color = col_borde) %>%
tab_options(
table.border.top.color = col_borde,
table.border.bottom.color = col_borde,
table.border.top.style = "solid",
table.border.bottom.style = "solid",
column_labels.border.top.color = col_borde,
column_labels.border.bottom.color = col_borde,
column_labels.border.bottom.width = px(2),
heading.border.bottom.color = col_borde,
heading.border.bottom.width = px(2),
table_body.hlines.color = "#CBD5DE",
table_body.border.bottom.color = col_borde,
table.font.size = px(14),
data_row.padding = px(6)
)
| Tabla N°1 | ||||
| Resumen del modelo de regresión potencial | ||||
| Variable Independiente | Variable Dependiente | Test Pearson (ln X y ln Y) | Coeficiente de determinación | Ecuación del modelo |
|---|---|---|---|---|
| Proposed Depth, ft | Depth Fee, USD | 0.88 | 77.44 | y = 1.0841 * x^(0.858846) |
| Autor: Grupo 1 | ||||
La relación entre Proposed Depth (X) y
Depth Fee (Y) es potencial y
sublineal:
\[y = 1.0841 \cdot x^{0.858846}\]
Con Pearson 0.88 y R² de 77.44%, el modelo es válido solo entre 200 y 13450 ft.