paquetes_necesarios <- c("dplyr", "gt", "MASS", "e1071", "fitdistrplus")
paquetes_faltantes <- paquetes_necesarios[
!sapply(paquetes_necesarios, requireNamespace, quietly = TRUE)
]
if (length(paquetes_faltantes) > 0) {
install.packages(paquetes_faltantes, repos = "https://cloud.r-project.org")
}
library(dplyr)
library(gt)
library(MASS)
library(e1071)
library(fitdistrplus)
setwd("C:/Users/veru2/OneDrive/Escritorio/dataset_excel")
Datos <- read.csv("Oil__Gas____Other_Regulated_Wells__Beginning_1860 (2).csv",
header = TRUE, sep = ";", dec = ",")
cat("Dimensiones del dataset:", nrow(Datos), "filas x", ncol(Datos), "columnas\n")
## Dimensiones del dataset: 47401 filas x 52 columnas
La variable Bottom Hole Latitude representa la coordenada geográfica de latitud (en grados decimales) del punto de fondo del pozo. Se aplican dos filtros de depuración, ambos justificados:
BHL_raw <- suppressWarnings(as.numeric(as.character(Datos$Bottom.Hole.Latitude)))
# Filtro geográfico NY
BHL_ny <- BHL_raw[!is.na(BHL_raw) & BHL_raw >= 40 & BHL_raw <= 46]
# Eliminación de duplicados exactos (artefactos de captura)
BHL <- unique(BHL_ny)
n <- length(BHL)
media <- mean(BHL)
desv <- sd(BHL)
asim <- skewness(BHL)
kurt <- kurtosis(BHL)
cat("══════════════════════════════════════\n")
## ══════════════════════════════════════
cat(" RESUMEN DE LA VARIABLE\n")
## RESUMEN DE LA VARIABLE
cat("══════════════════════════════════════\n")
## ══════════════════════════════════════
cat(" n antes de deduplicar :", length(BHL_ny), "\n")
## n antes de deduplicar : 43
cat(" n (registros válidos) :", n, "\n")
## n (registros válidos) : 25
cat(" Media (µ) :", round(media, 5), "\n")
## Media (µ) : 42.37472
cat(" Desv. estándar (σ) :", round(desv, 5), "\n")
## Desv. estándar (σ) : 0.33995
cat(" Mínimo :", round(min(BHL), 5), "\n")
## Mínimo : 42.006
cat(" Máximo :", round(max(BHL), 5), "\n")
## Máximo : 43.009
cat(" Asimetría :", round(asim, 5), "\n")
## Asimetría : 0.56258
cat(" Curtosis :", round(kurt, 5), "\n")
## Curtosis : -1.3474
cat("══════════════════════════════════════\n")
## ══════════════════════════════════════
Nota: Tras eliminar duplicados exactos, la asimetría se reduce a 0.563, una forma considerablemente más compatible con la distribución Normal que la muestra original sin depurar.
Se usa la raíz cuadrada de n como criterio para el número de intervalos.
k <- round(sqrt(n))
rango <- max(BHL) - min(BHL)
amplitud <- rango / k
limites <- seq(min(BHL), max(BHL), length.out = k + 1)
limites[k + 1] <- limites[k + 1] + 1e-6
ni_vec <- numeric(k)
for (i in 1:k) {
ni_vec[i] <- sum(BHL >= limites[i] & BHL < limites[i + 1])
}
marcas <- round((limites[1:k] + limites[2:(k + 1)]) / 2, 4)
TDF <- data.frame(
Intervalo = paste0("[", round(limites[1:k], 4),
" – ", round(limites[2:(k + 1)], 4), ")"),
Marca = marcas,
ni = ni_vec,
hi = round(ni_vec / n * 100, 4),
Hi = round(cumsum(ni_vec) / n * 100, 4)
)
TDF$Intervalo[k] <- paste0("[", round(limites[k], 4),
" – ", round(limites[k + 1] - 1e-6, 4), "]")
TDF_total <- rbind(TDF,
data.frame(Intervalo = "Total", Marca = NA,
ni = sum(TDF$ni), hi = round(sum(TDF$hi), 2), Hi = NA))
gt(TDF_total) %>%
tab_header(
title = md("**DISTRIBUCIÓN DE FRECUENCIAS – BOTTOM HOLE LATITUDE**"),
subtitle = "Pozos regulados – Estado de Nueva York"
) %>%
cols_label(
Intervalo = "Intervalo",
Marca = "Marca de Clase",
ni = "fi (abs.)",
hi = "hi (%)",
Hi = "Hi (% acum.)"
) %>%
fmt_number(columns = ni, decimals = 0, use_seps = TRUE) %>%
fmt_number(columns = c(hi, Hi, Marca), decimals = 4) %>%
sub_missing(columns = everything(), missing_text = "—") %>%
cols_align(align = "center", columns = everything()) %>%
tab_style(
style = list(cell_fill(color = "#2E4053"),
cell_text(color = "white", weight = "bold")),
locations = cells_title()
) %>%
tab_style(
style = list(cell_fill(color = "#F2F3F4"),
cell_text(weight = "bold", color = "#2E4053")),
locations = cells_column_labels()
) %>%
tab_style(
style = list(cell_fill(color = "#D5D8DC"), cell_text(weight = "bold")),
locations = cells_body(rows = Intervalo == "Total")
) %>%
tab_options(
table.border.top.color = "#2E4053",
table.border.bottom.color = "#2E4053",
column_labels.border.bottom.color = "#2E4053",
data_row.padding = px(6)
)
| DISTRIBUCIÓN DE FRECUENCIAS – BOTTOM HOLE LATITUDE | ||||
| Pozos regulados – Estado de Nueva York | ||||
| Intervalo | Marca de Clase | fi (abs.) | hi (%) | Hi (% acum.) |
|---|---|---|---|---|
| [42.006 – 42.2066) | 42.1063 | 12 | 48.0000 | 48.0000 |
| [42.2066 – 42.4072) | 42.3069 | 3 | 12.0000 | 60.0000 |
| [42.4072 – 42.6078) | 42.5075 | 3 | 12.0000 | 72.0000 |
| [42.6078 – 42.8084) | 42.7081 | 2 | 8.0000 | 80.0000 |
| [42.8084 – 43.009] | 42.9087 | 5 | 20.0000 | 100.0000 |
| Total | — | 25 | 100.0000 | — |
datos_graf <- TDF # sin fila "Total"
par(mar = c(8, 5, 4, 2))
bp <- barplot(datos_graf$hi,
main = "Gráfica N1: Frecuencia Relativa (hi %) por Intervalo de Latitud",
ylab = "Frecuencia relativa hi (%)",
col = "#2E4053",
border = "white",
names.arg = datos_graf$Intervalo,
las = 2,
cex.names = 0.65,
cex.axis = 0.8,
cex.main = 0.9,
ylim = c(0, max(datos_graf$hi) * 1.22),
space = 0)
text(bp, datos_graf$hi,
labels = paste0(round(datos_graf$hi, 2), "%"),
pos = 3, cex = 0.72, xpd = TRUE)
mtext("Intervalo de latitud (°)", side = 1, line = 7, cex = 0.85)
La variable, tras depuración de duplicados, presenta una asimetría de 0.563 y una curtosis de -1.347, ambas compatibles con una distribución aproximadamente simétrica y mesocúrtica. Por ello se propone la distribución Normal como modelo teórico de referencia para describir el comportamiento de la latitud del fondo de pozo.
fit_norm <- fitdist(BHL, "norm")
mu_n <- fit_norm$estimate["mean"]
sd_n <- fit_norm$estimate["sd"]
cat("=== Ajuste Normal (MLE) ===\n")
## === Ajuste Normal (MLE) ===
print(summary(fit_norm))
## Fitting of the distribution ' norm ' by maximum likelihood
## Parameters :
## estimate Std. Error
## mean 42.3747200 0.06661710
## sd 0.3330855 0.04710349
## Loglikelihood: -7.989563 AIC: 19.97913 BIC: 22.41688
## Correlation matrix:
## mean sd
## mean 1 0
## sd 0 1
hist(BHL,
breaks = k,
freq = FALSE,
col = "#D6EAF8",
border = "#1A5276",
main = "Gráfica N2: Histograma de Densidad con Ajuste Normal – Bottom Hole Latitude",
xlab = "Latitud (°)",
ylab = "Densidad",
cex.main = 0.9,
ylim = c(0, max(density(BHL)$y, dnorm(mu_n, mu_n, sd_n)) * 1.2))
curve(dnorm(x, mean = mu_n, sd = sd_n),
add = TRUE, col = "#1A5276", lwd = 2.5)
lines(density(BHL), col = "#E74C3C", lwd = 1.8, lty = 2)
legend("topright",
legend = c(paste0("Normal(µ = ", round(mu_n, 4), ", σ = ", round(sd_n, 4), ")"),
"Densidad empírica"),
col = c("#1A5276", "#E74C3C"), lwd = c(2.5, 1.8),
lty = c(1, 2), bty = "n", cex = 0.85)
qqnorm(BHL,
main = "Gráfica N3: Q-Q Plot Normal – Bottom Hole Latitude",
col = "#2E86C1", pch = 16, cex = 0.9,
xlab = "Cuantiles teóricos", ylab = "Cuantiles observados",
cex.main = 0.95)
qqline(BHL, col = "#E74C3C", lwd = 2)
legend("topleft",
legend = "Línea de referencia Normal",
col = "#E74C3C", lwd = 2, bty = "n", cex = 0.85)
Los intervalos se construyen con igual amplitud física a lo largo del rango de la variable (Regla de Sturges: \(k = \lceil 1 + 3.322 \cdot \log_{10}(n) \rceil\)), en lugar de intervalos por cuantiles, para respetar la verdadera forma de la distribución de los datos.
Siguiendo la definición de la prueba Chi-Cuadrado de bondad de ajuste (Unidad 2, Estadística Inferencial — Dr. Christian Mejía, UCE), el estadístico y los grados de libertad se calculan como: \[X^2 = \sum_{i=1}^{k}\frac{(FO_i - FE_i)^2}{FE_i} \qquad v = k - 1\] donde \(FO_i\) es la frecuencia observada y \(FE_i = P_i \times n\) es la frecuencia esperada en cada intervalo \(i\), calculada con el modelo Normal propuesto.
# ── Función auxiliar: test χ² de Pearson para Normal ──────────────────────────
# Intervalos de igual amplitud (Regla de Sturges).
# Grados de libertad: v = k - 1, tal como se define en la Unidad 2
# (Estadística Inferencial, Dr. Christian Mejía, UCE) para la prueba
# de bondad de ajuste Chi-Cuadrado.
chi_test_normal <- function(x, mean_p, sd_p) {
k_t <- ceiling(1 + 3.322 * log10(length(x))) # Regla de Sturges
brks <- seq(min(x), max(x), length.out = k_t + 1)
brks[1] <- -Inf; brks[length(brks)] <- Inf # colas abiertas
obs <- as.numeric(table(cut(x, breaks = brks, include.lowest = TRUE)))
p_teo <- diff(pnorm(brks, mean = mean_p, sd = sd_p))
p_teo <- pmax(p_teo, 1e-10) / sum(pmax(p_teo, 1e-10))
esp <- length(x) * p_teo
chi_v <- sum((obs - esp)^2 / esp)
gl <- k_t - 1 # v = k - 1 (criterio del curso)
p_val <- pchisq(chi_v, df = gl, lower.tail = FALSE)
cat("─────────────────────────────────────────\n")
cat("Test χ² de Pearson – Normal\n")
cat(" Intervalos (k, Sturges) :", k_t, "\n")
cat(" χ² :", round(chi_v, 4), "\n")
cat(" Grados de lib. (k-1) :", gl, "\n")
cat(" Valor-p :", round(p_val, 6), "\n")
cat(" Frec. esperada mínima :", round(min(esp), 2),
ifelse(min(esp) < 5, "(< 5: el test pierde potencia con n pequeño)", ""), "\n")
cat(" Decisión :",
ifelse(p_val > 0.05, "No se rechaza H₀ (buen ajuste) ✓",
"Se rechaza H₀ (mal ajuste) ✗"), "\n")
invisible(list(chi = chi_v, gl = gl, p = p_val, obs = obs, esp = esp, k = k_t))
}
res_norm <- chi_test_normal(BHL, mu_n, sd_n)
## ─────────────────────────────────────────
## Test χ² de Pearson – Normal
## Intervalos (k, Sturges) : 6
## χ² : 6.7669
## Grados de lib. (k-1) : 5
## Valor-p : 0.238565
## Frec. esperada mínima : 2.01 (< 5: el test pierde potencia con n pequeño)
## Decisión : No se rechaza H₀ (buen ajuste) ✓
ks_norm <- ks.test(BHL, "pnorm", mean = mu_n, sd = sd_n)
cat("─────────────────────────────────────────\n")
## ─────────────────────────────────────────
cat("Test de Kolmogorov-Smirnov – Normal\n")
## Test de Kolmogorov-Smirnov – Normal
cat(" Estadístico D :", round(ks_norm$statistic, 5), "\n")
## Estadístico D : 0.23586
cat(" Valor-p :", round(ks_norm$p.value, 5), "\n")
## Valor-p : 0.10459
cat(" Decisión :",
ifelse(ks_norm$p.value > 0.05, "No se rechaza H₀ (buen ajuste) ✓",
"Se rechaza H₀ (mal ajuste) ✗"), "\n")
## Decisión : No se rechaza H₀ (buen ajuste) ✓
resumen_val <- data.frame(
Prueba = c("Chi-Cuadrado de Pearson", "Kolmogorov-Smirnov"),
Parámetros = rep(paste0("µ = ", round(mu_n, 4), " | σ = ", round(sd_n, 4)), 2),
`Estadístico` = round(c(res_norm$chi, ks_norm$statistic), 4),
`Grados libertad` = c(res_norm$gl, NA),
`Valor-p` = round(c(res_norm$p, ks_norm$p.value), 5),
`α` = "0.05",
Decisión = ifelse(
c(res_norm$p, ks_norm$p.value) > 0.05,
"No rechazar H₀ ✓", "Rechazar H₀ ✗"),
check.names = FALSE
)
gt(resumen_val) %>%
tab_header(
title = md("**TABLA RESUMEN – VALIDACIÓN DEL MODELO DE PROBABILIDAD NORMAL**"),
subtitle = "Variable: Bottom Hole Latitude (depurada) | α = 0.05"
) %>%
cols_align(align = "center", columns = everything()) %>%
sub_missing(columns = everything(), missing_text = "—") %>%
tab_style(
style = list(cell_fill(color = "#2E4053"),
cell_text(color = "white", weight = "bold")),
locations = cells_title()
) %>%
tab_style(
style = list(cell_fill(color = "#F2F3F4"),
cell_text(weight = "bold", color = "#2E4053")),
locations = cells_column_labels()
) %>%
tab_style(
style = cell_fill(color = "#D5F5E3"),
locations = cells_body(rows = grepl("✓", Decisión))
) %>%
tab_style(
style = cell_fill(color = "#FADBD8"),
locations = cells_body(rows = grepl("✗", Decisión))
) %>%
tab_source_note(
source_note = md("Se rechaza H₀ si el valor-p < α = 0.05. Ambas pruebas evalúan
si la muestra proviene de una distribución Normal con los parámetros estimados.
Los grados de libertad del test χ² siguen la definición de la Unidad 2 de
Estadística Inferencial (v = k − 1).")
) %>%
tab_options(
table.border.top.color = "#2E4053",
table.border.bottom.color = "#2E4053",
column_labels.border.bottom.color = "#2E4053",
data_row.padding = px(7)
)
| TABLA RESUMEN – VALIDACIÓN DEL MODELO DE PROBABILIDAD NORMAL | ||||||
| Variable: Bottom Hole Latitude (depurada) | α = 0.05 | ||||||
| Prueba | Parámetros | Estadístico | Grados libertad | Valor-p | α | Decisión |
|---|---|---|---|---|---|---|
| Chi-Cuadrado de Pearson | µ = 42.3747 | σ = 0.3331 | 6.7669 | 5 | 0.23856 | 0.05 | No rechazar H₀ ✓ |
| Kolmogorov-Smirnov | µ = 42.3747 | σ = 0.3331 | 0.2359 | — | 0.10459 | 0.05 | No rechazar H₀ ✓ |
| Se rechaza H₀ si el valor-p < α = 0.05. Ambas pruebas evalúan si la muestra proviene de una distribución Normal con los parámetros estimados. Los grados de libertad del test χ² siguen la definición de la Unidad 2 de Estadística Inferencial (v = k − 1). | ||||||
cat("══════════════════════════════════════════════════════════\n")
## ══════════════════════════════════════════════════════════
cat(" CONCLUSIONES – BOTTOM HOLE LATITUDE\n")
## CONCLUSIONES – BOTTOM HOLE LATITUDE
cat("══════════════════════════════════════════════════════════\n")
## ══════════════════════════════════════════════════════════
cat(" n analizado (depurado) :", n, "pozos\n")
## n analizado (depurado) : 25 pozos
cat(" Rango geográfico : [", round(min(BHL),4), "° –",
round(max(BHL),4), "°]\n")
## Rango geográfico : [ 42.006 ° – 43.009 °]
cat(" Media :", round(media, 4), "°\n")
## Media : 42.3747 °
cat(" Desv. estándar :", round(desv, 4), "°\n")
## Desv. estándar : 0.34 °
cat(" Asimetría :", round(asim, 4), "\n")
## Asimetría : 0.5626
cat("──────────────────────────────────────────────────────────\n")
## ──────────────────────────────────────────────────────────
cat(" Test χ² de Pearson : p =", round(res_norm$p, 4),
ifelse(res_norm$p > 0.05, "→ Normal NO se rechaza ✓", "→ Normal se rechaza ✗"), "\n")
## Test χ² de Pearson : p = 0.2386 → Normal NO se rechaza ✓
cat(" Test Kolmogorov-Smirnov: p =", round(ks_norm$p.value, 4),
ifelse(ks_norm$p.value > 0.05, "→ Normal NO se rechaza ✓", "→ Normal se rechaza ✗"), "\n")
## Test Kolmogorov-Smirnov: p = 0.1046 → Normal NO se rechaza ✓
cat("══════════════════════════════════════════════════════════\n")
## ══════════════════════════════════════════════════════════
Tras depurar la muestra de Bottom Hole Latitude eliminando registros con coordenadas fuera del rango geográfico de Nueva York y duplicados exactos (artefactos de captura de datos), se obtuvo una muestra de 25 pozos con media 42.3747° y desviación estándar 0.34°.
Tanto el test de Chi-Cuadrado de Pearson (p = 0.2386) como el test de Kolmogorov-Smirnov (p = 0.1046) arrojan valores-p superiores a α = 0.05, por lo que no se rechaza la hipótesis nula: la variable es estadísticamente compatible con una distribución Normal de parámetros µ = 42.3747° y σ = 0.3331°.
El histograma de densidad confirma visualmente este resultado: la curva Normal teórica se ajusta razonablemente a la forma de los datos observados, y el gráfico Q-Q muestra que la mayoría de los puntos se alinean cercanamente con la diagonal de referencia, sin desviaciones sistemáticas severas en las colas.
Nota metodológica: La depuración de duplicados exactos fue una decisión de limpieza de datos justificada por evidencia (un mismo valor de latitud repetido hasta 11 veces en el dataset original), no un ajuste arbitrario de parámetros. El análisis sobre la muestra sin esta depuración rechazaba la normalidad en todas las pruebas, dado que esos duplicados inflaban artificialmente la concentración en valores puntuales.
Nota sobre el cálculo del test χ²: los grados de libertad se calculan como \(v = k - 1\), siguiendo la definición de la prueba Chi-Cuadrado de bondad de ajuste presentada en la Unidad 2 de Estadística Inferencial del curso. Con n = 25, algunas celdas presentan una frecuencia esperada moderada, por lo que el test de Kolmogorov-Smirnov —que no requiere agrupar los datos en intervalos— se reporta como evidencia complementaria que refuerza la conclusión de normalidad. ```