Preparación del entorno de trabajo cargando los paquetes necesarios para el manejo de datos, formato de tablas y visualizacion de gráficas.
library(readxl)
library(dplyr)
library(gt)
library(ggplot2)
library(scales)
library(e1071)
# Selección del archivo Excel (se abre ventana emergente)
ruta_archivo <- file.choose()
Datos <- read_excel(ruta_archivo)
cat("Dataset cargado exitosamente. Total de variables importadas:", ncol(Datos), "\n")
## Dataset cargado exitosamente. Total de variables importadas: 32
Se extrae y limpia la variable Longitude (Longitud
Geográfica). Se aseguran los formatos numéricos (reemplazando comas por
puntos) y se omiten los valores nulos para garantizar la exactitud de
los cálculos.
valores_limpios <- gsub(",", ".", Datos$Longitude)
Variable <- na.omit(as.numeric(valores_limpios))
N <- length(Variable)
Minimo_real <- min(Variable)
Maximo_real <- max(Variable)
cat("Variable analizada: Longitude\n")
## Variable analizada: Longitude
cat("Unidad de medida: grados decimales (°)\n")
## Unidad de medida: grados decimales (°)
cat("Total de observaciones útiles (n):", N, "\n")
## Total de observaciones útiles (n): 7537
cat("Mínimo real observado en el dataset:", round(Minimo_real, 4), "grados\n")
## Mínimo real observado en el dataset: -152.129 grados
cat("Máximo real observado en el dataset:", round(Maximo_real, 4), "grados\n")
## Máximo real observado en el dataset: 174.361 grados
Naturaleza y escala de la variable: la Longitud es cuantitativa continua. En cuanto a su escala de medición corresponde a una escala de intervalo y no a una escala de razón: el valor 0° (meridiano de Greenwich) es un punto de referencia convencional y no representa una ausencia real de la magnitud, por lo que no es válido interpretar razones entre longitudes (no tiene sentido decir que 60° es “el doble” de 30°). Todo depende de la interpretación del cero: como aquí es arbitrario y no absoluto, la escala es de intervalo.
El rango real, tomado directamente del dataset (sin límites teóricos como ±90°), va desde -152.13° hasta 174.36°.
Se realiza el cálculo de intervalos y el conteo de frecuencias usando la regla de Sturges. Siguiendo el procedimiento indicado en clase, primero se muestra la tabla con los límites exactos de Sturges, luego se justifica el ajuste y finalmente se presenta la tabla simplificada.
k_sturges <- floor(1 + 3.322 * log10(N))
Amplitud_sturges <- (Maximo_real - Minimo_real) / k_sturges
cortes_sturges <- Minimo_real + (0:k_sturges) * Amplitud_sturges
cortes_sturges[length(cortes_sturges)] <- Maximo_real # aseguramos que el máximo real quede incluido
inter_sturges <- cut(Variable, breaks = cortes_sturges, include.lowest = TRUE, right = FALSE)
ni_sturges <- as.vector(table(inter_sturges))
TDF_Sturges <- data.frame(
Li = cortes_sturges[1:k_sturges],
Ls = cortes_sturges[2:(k_sturges + 1)],
MC = (cortes_sturges[1:k_sturges] + cortes_sturges[2:(k_sturges + 1)]) / 2,
ni = ni_sturges,
hi_perc = round((ni_sturges / N) * 100, 2)
)
cat("Número de clases según Sturges (k):", k_sturges, "\n")
## Número de clases según Sturges (k): 13
cat("Amplitud exacta de clase:", round(Amplitud_sturges, 4), "\n")
## Amplitud exacta de clase: 25.1146
TDF_Sturges %>%
gt() %>%
tab_header(
title = md("**Tabla N°1a**"),
subtitle = "Distribución de frecuencias — Regla de Sturges (límites exactos, sin redondear)"
) %>%
cols_label(
Li = "Lim. Inf (°)", Ls = "Lim. Sup (°)",
MC = "Marca clase", ni = md("n~i~"), hi_perc = md("h~i~ (%)")
) %>%
cols_align(align = "center", columns = everything()) %>%
fmt_number(columns = c(Li, Ls, MC, hi_perc), decimals = 4) %>%
fmt_number(columns = ni, decimals = 0, use_seps = TRUE) %>%
tab_source_note(paste("n =", format(N, big.mark = ","), "| k (Sturges) =", k_sturges)) %>%
tab_options(heading.background.color = "#EBF5FB", column_labels.font.weight = "bold")
| Tabla N°1a | ||||
| Distribución de frecuencias — Regla de Sturges (límites exactos, sin redondear) | ||||
| Lim. Inf (°) | Lim. Sup (°) | Marca clase | ni | hi (%) |
|---|---|---|---|---|
| −152.1290 | −127.0144 | −139.5717 | 37 | 0.4900 |
| −127.0144 | −101.8998 | −114.4571 | 2,327 | 30.8700 |
| −101.8998 | −76.7852 | −89.3425 | 1,991 | 26.4200 |
| −76.7852 | −51.6706 | −64.2279 | 719 | 9.5400 |
| −51.6706 | −26.5559 | −39.1133 | 105 | 1.3900 |
| −26.5559 | −1.4413 | −13.9986 | 66 | 0.8800 |
| −1.4413 | 23.6733 | 11.1160 | 1,157 | 15.3500 |
| 23.6733 | 48.7879 | 36.2306 | 306 | 4.0600 |
| 48.7879 | 73.9025 | 61.3452 | 354 | 4.7000 |
| 73.9025 | 99.0171 | 86.4598 | 148 | 1.9600 |
| 99.0171 | 124.1318 | 111.5745 | 275 | 3.6500 |
| 124.1318 | 149.2464 | 136.6891 | 41 | 0.5400 |
| 149.2464 | 174.3610 | 161.8037 | 11 | 0.1500 |
| n = 7,537 | k (Sturges) = 13 | ||||
Los límites de clase obtenidos con la fórmula de Sturges (Tabla N°1a) presentan valores decimales con varias cifras que resultan poco prácticos e intuitivos para la interpretación geográfica de la longitud (por ejemplo, límites como -127.0144° o -101.8998°). Debido a que estos números son complicados de interpretar, es más conveniente redondear los límites a múltiplos de 10, manteniendo el mismo número de clases (k) obtenido matemáticamente, de modo que la tabla sea más legible sin perder representatividad estadística.
BASE <- 10
min_int <- floor(Minimo_real / BASE) * BASE
max_int <- ceiling(Maximo_real / BASE) * BASE
k_int_sug <- k_sturges # se mantiene el mismo k obtenido en el paso 1 (Sturges)
Rango_int <- max_int - min_int
Amplitud_int <- ceiling((Rango_int / k_int_sug) / 10) * 10
if (Amplitud_int == 0) Amplitud_int <- 10
cortes_int <- seq(from = min_int, by = Amplitud_int, length.out = k_int_sug + 1)
if (max(cortes_int) < Maximo_real) cortes_int <- c(cortes_int, max(cortes_int) + Amplitud_int)
while (length(cortes_int) > 2 && cortes_int[length(cortes_int) - 1] >= Maximo_real) {
cortes_int <- cortes_int[-length(cortes_int)]
}
K_real <- length(cortes_int) - 1
inter_int <- cut(Variable, breaks = cortes_int, include.lowest = TRUE, right = FALSE)
ni_int <- as.vector(table(inter_int))
# Cálculos unitarios y porcentuales
hi_int_unit <- ni_int / N
hi_int_perc <- hi_int_unit * 100
TDF_Enteros <- data.frame(
Li = cortes_int[1:K_real],
Ls = cortes_int[2:(K_real + 1)],
MC = (cortes_int[1:K_real] + cortes_int[2:(K_real + 1)]) / 2,
ni = ni_int,
hi_unit = hi_int_unit,
hi_perc = hi_int_perc,
Ni_asc = cumsum(ni_int),
Ni_desc = rev(cumsum(rev(ni_int))),
Hi_asc_unit = cumsum(hi_int_unit),
Hi_desc_unit = rev(cumsum(rev(hi_int_unit))),
Hi_asc_perc = cumsum(hi_int_perc),
Hi_desc_perc = rev(cumsum(rev(hi_int_perc)))
)
cat("Clases ajustadas (k), consistentes con Sturges:", K_real, "\n")
## Clases ajustadas (k), consistentes con Sturges: 12
Esta tabla simplificada se deriva y coincide lógicamente con los 13 intervalos calculados en el Paso 1 (Tabla N°1a), solo que con límites redondeados a múltiplos de 10.
fuente_nota <- paste("n =", format(N, big.mark = ","), "| Fuente: Global Energy Monitor - GOGET 2023")
TDF_Int_gt <- TDF_Enteros
fila_total_int <- data.frame(
Li = NA, Ls = NA, MC = NA,
ni = sum(TDF_Int_gt$ni),
hi_unit = sum(TDF_Int_gt$hi_unit),
hi_perc = sum(TDF_Int_gt$hi_perc),
Ni_asc = NA, Ni_desc = NA,
Hi_asc_unit = NA, Hi_desc_unit = NA,
Hi_asc_perc = NA, Hi_desc_perc = NA
)
bind_rows(TDF_Int_gt, fila_total_int) %>%
gt() %>%
tab_header(
title = md("**Tabla N°1**"),
subtitle = "Distribución de frecuencias de longitud geográfica (Límites enteros)"
) %>%
cols_label(
Li = "Lim. Inf (°)", Ls = "Lim. Sup (°)",
MC = "Marca clase", ni = md("n~i~"),
hi_unit = md("h~i~ (Unit)"), hi_perc = md("h~i~ (%)"),
Ni_asc = md("N~i~ (\u2191)"), Ni_desc = md("N~i~ (\u2193)"),
Hi_asc_unit = md("H~i~ (Unit \u2191)"), Hi_desc_unit = md("H~i~ (Unit \u2193)"),
Hi_asc_perc = md("H~i~ (% \u2191)"), Hi_desc_perc = md("H~i~ (% \u2193)")
) %>%
cols_align(align = "center", columns = everything()) %>%
fmt_number(columns = c(Li, Ls, MC, hi_perc, Hi_asc_perc, Hi_desc_perc), decimals = 2) %>%
fmt_number(columns = c(hi_unit, Hi_asc_unit, Hi_desc_unit), decimals = 4) %>%
fmt_number(columns = c(ni, Ni_asc, Ni_desc), decimals = 0, use_seps = TRUE) %>%
tab_source_note(fuente_nota) %>%
tab_style(
style = cell_fill(color = "#F5F5F5"),
locations = cells_body(rows = nrow(TDF_Int_gt) + 1)
) %>%
tab_style(
style = cell_text(weight = "bold"),
locations = cells_body(rows = nrow(TDF_Int_gt) + 1)
) %>%
sub_missing(columns = everything(), missing_text = "") %>%
tab_options(heading.background.color = "#EBF5FB", column_labels.font.weight = "bold")
| Tabla N°1 | |||||||||||
| Distribución de frecuencias de longitud geográfica (Límites enteros) | |||||||||||
| Lim. Inf (°) | Lim. Sup (°) | Marca clase | ni | hi (Unit) | hi (%) | Ni (↑) | Ni (↓) | Hi (Unit ↑) | Hi (Unit ↓) | Hi (% ↑) | Hi (% ↓) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| −160.00 | −130.00 | −145.00 | 37 | 0.0049 | 0.49 | 37 | 7,537 | 0.0049 | 1.0000 | 0.49 | 100.00 |
| −130.00 | −100.00 | −115.00 | 2,689 | 0.3568 | 35.68 | 2,726 | 7,500 | 0.3617 | 0.9951 | 36.17 | 99.51 |
| −100.00 | −70.00 | −85.00 | 2,018 | 0.2677 | 26.77 | 4,744 | 4,811 | 0.6294 | 0.6383 | 62.94 | 63.83 |
| −70.00 | −40.00 | −55.00 | 403 | 0.0535 | 5.35 | 5,147 | 2,793 | 0.6829 | 0.3706 | 68.29 | 37.06 |
| −40.00 | −10.00 | −25.00 | 51 | 0.0068 | 0.68 | 5,198 | 2,390 | 0.6897 | 0.3171 | 68.97 | 31.71 |
| −10.00 | 20.00 | 5.00 | 1,130 | 0.1499 | 14.99 | 6,328 | 2,339 | 0.8396 | 0.3103 | 83.96 | 31.03 |
| 20.00 | 50.00 | 35.00 | 431 | 0.0572 | 5.72 | 6,759 | 1,209 | 0.8968 | 0.1604 | 89.68 | 16.04 |
| 50.00 | 80.00 | 65.00 | 384 | 0.0509 | 5.09 | 7,143 | 778 | 0.9477 | 0.1032 | 94.77 | 10.32 |
| 80.00 | 110.00 | 95.00 | 183 | 0.0243 | 2.43 | 7,326 | 394 | 0.9720 | 0.0523 | 97.20 | 5.23 |
| 110.00 | 140.00 | 125.00 | 178 | 0.0236 | 2.36 | 7,504 | 211 | 0.9956 | 0.0280 | 99.56 | 2.80 |
| 140.00 | 170.00 | 155.00 | 26 | 0.0034 | 0.34 | 7,530 | 33 | 0.9991 | 0.0044 | 99.91 | 0.44 |
| 170.00 | 200.00 | 185.00 | 7 | 0.0009 | 0.09 | 7,537 | 7 | 1.0000 | 0.0009 | 100.00 | 0.09 |
| 7,537 | 1.0000 | 100.00 | |||||||||
| n = 7,537 | Fuente: Global Energy Monitor - GOGET 2023 | |||||||||||
color_barras <- "#2E86C1"
color_ojiva1 <- "#2E86C1"
color_ojiva2 <- "#C03928"
color_outlier <- "#C03928"
tema_base <- theme_minimal(base_size = 12) +
theme(
legend.position = "none",
plot.title = element_text(face = "bold", size = 13),
plot.caption = element_text(color = "#888888", size = 9, hjust = 0),
axis.title = element_text(face = "bold", size = 11),
axis.text.x = element_text(angle = 25, hjust = 1, size = 10),
panel.grid.major.x = element_blank(),
panel.grid.major.y = element_line(color = "#EEEEEE"),
panel.grid.minor = element_blank(),
plot.background = element_rect(fill = "white", color = NA)
)
df_graf <- TDF_Enteros %>%
mutate(intervalo = factor(paste(Li, "-", Ls), levels = paste(Li, "-", Ls)))
# --- Identificación de valores atípicos (regla del rango intercuartílico) ---
Q1_graf <- quantile(Variable, 0.25)
Q3_graf <- quantile(Variable, 0.75)
IQR_graf <- Q3_graf - Q1_graf
Lim_inf_out <- Q1_graf - 1.5 * IQR_graf
Lim_sup_out <- Q3_graf + 1.5 * IQR_graf
# Marcamos qué intervalos de la tabla contienen valores atípicos
df_graf <- df_graf %>%
mutate(tiene_outlier = Ls <= Lim_inf_out | Li >= Lim_sup_out)
ggplot(df_graf, aes(x = intervalo, y = ni, fill = tiene_outlier)) +
geom_col(color = "white", width = 0.95) +
geom_text(aes(label = format(ni, big.mark = ",")), vjust = -0.4, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c(`FALSE` = color_barras, `TRUE` = color_outlier)) +
scale_y_continuous(labels = label_comma(), expand = expansion(mult = c(0, 0.12))) +
labs(title = "Histograma de Frecuencia Absoluta",
subtitle = "En rojo: intervalos que contienen valores atípicos (outliers)",
x = "Longitud (°)", y = "Frecuencia Absoluta (ni)",
caption = fuente_nota) +
tema_base
ggplot(df_graf, aes(x = intervalo, y = hi_perc, fill = tiene_outlier)) +
geom_col(color = "white", width = 0.95) +
geom_text(aes(label = paste0(round(hi_perc, 2), "%")), vjust = -0.4, size = 3.5, fontface = "bold") +
scale_fill_manual(values = c(`FALSE` = color_barras, `TRUE` = color_outlier)) +
scale_y_continuous(labels = function(x) paste0(x, "%"), expand = expansion(mult = c(0, 0.12))) +
labs(title = "Histograma de Frecuencia Relativa Porcentual",
subtitle = "En rojo: intervalos que contienen valores atípicos (outliers)",
x = "Longitud (°)", y = "Frecuencia Relativa (%)",
caption = fuente_nota) +
tema_base
df_box <- data.frame(x = Variable) %>%
mutate(es_outlier = x < Lim_inf_out | x > Lim_sup_out)
df_outliers_extremos <- df_box %>%
filter(es_outlier) %>%
arrange(x) %>%
slice(c(1:min(3, n()), max(1, n() - 2):n())) %>%
distinct()
ggplot(df_box, aes(x = x, y = 0)) +
geom_boxplot(fill = color_barras, color = "#1A5276", outlier.color = color_outlier, outlier.size = 1.8) +
geom_text(
data = df_outliers_extremos,
aes(x = x, y = 0.35, label = round(x, 1)),
size = 3, color = color_outlier, angle = 90, hjust = 0
) +
labs(title = "Diagrama de Cajas (Boxplot)",
subtitle = "Puntos rojos = valores atípicos; etiquetas = outliers extremos",
x = "Longitud (°)", y = "",
caption = fuente_nota) +
theme_minimal(base_size = 12) +
theme(
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
plot.title = element_text(face = "bold", size = 13),
plot.caption = element_text(color = "#888888", size = 9, hjust = 0),
plot.background = element_rect(fill = "white", color = NA)
)
ojiva_df <- data.frame(
x = c(TDF_Enteros$Ls, TDF_Enteros$Li),
y = c(TDF_Enteros$Hi_asc_perc, TDF_Enteros$Hi_desc_perc),
tipo = rep(c("Ascendente", "Descendente"), each = K_real)
)
ggplot(ojiva_df, aes(x = x, y = y, color = tipo, group = tipo)) +
geom_line(linewidth = 1.2) +
geom_point(size = 2.5) +
scale_color_manual(values = c("Ascendente" = color_ojiva1, "Descendente" = color_ojiva2)) +
scale_y_continuous(labels = function(x) paste0(x, "%"), limits = c(0, 105), expand = expansion(mult = c(0, 0))) +
labs(title = "Ojivas Ascendente y Descendente",
x = "Longitud (°)", y = "Frecuencia Acumulada (%)",
color = NULL, caption = fuente_nota) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(face = "bold", size = 13),
plot.caption = element_text(color = "#888888", size = 9, hjust = 0),
legend.position = "bottom",
plot.background = element_rect(fill = "white", color = NA)
)
La variable Longitud es cuantitativa continua de escala de intervalo (ver justificación en la sección 3), por lo que se calculan todos los indicadores: tendencia central, dispersión, forma y valores atípicos.
media <- mean(Variable)
mediana <- median(Variable)
moda_val <- TDF_Enteros$MC[which.max(TDF_Enteros$ni)]
varianza <- var(Variable)
sd_val <- sd(Variable)
CV <- (sd_val / abs(media)) * 100
asim <- skewness(Variable, type = 2)
kurt <- kurtosis(Variable)
Q1 <- quantile(Variable, 0.25)
Q3 <- quantile(Variable, 0.75)
IQR_val <- Q3 - Q1
Lim_inf_out2 <- Q1 - 1.5 * IQR_val
Lim_sup_out2 <- Q3 + 1.5 * IQR_val
outliers_data <- sort(Variable[Variable < Lim_inf_out2 | Variable > Lim_sup_out2])
num_out <- length(outliers_data)
# Si hay pocos outliers se listan todos ("cuáles"); si hay muchos, se listan los extremos
# y se remite a la representación gráfica (sección 6, histogramas y boxplot en rojo).
if (num_out == 0) {
out_txt <- "0 (sin valores atípicos)"
} else if (num_out <= 10) {
out_txt <- paste0(num_out, " [", paste(round(outliers_data, 2), collapse = ", "), "]")
} else {
out_txt <- paste0(
num_out, " [ej.: ", paste(round(head(outliers_data, 3), 2), collapse = ", "),
" ... ", paste(round(tail(outliers_data, 3), 2), collapse = ", "),
"] — ver detalle resaltado en las gráficas de la sección 6"
)
}
data.frame(
Variable = "Longitud (°)",
Min = round(Minimo_real, 2),
Max = round(Maximo_real, 2),
Rango = round(Maximo_real - Minimo_real, 2),
Media = round(media, 2),
Mediana = round(mediana, 2),
Moda = round(moda_val, 2),
Varianza = round(varianza, 2),
Desv_Est = round(sd_val, 2),
CV = round(CV, 2),
Asimetría = round(asim, 4),
Curtosis = round(kurt, 4),
Outliers = out_txt,
check.names = FALSE
) %>%
gt() %>%
tab_header(title = md("**Tabla N°2: Indicadores Estadísticos de Longitud**")) %>%
cols_label(
Variable = "Variable",
Min = "M\u00ednimo",
Max = "M\u00e1ximo",
Rango = "Rango (R)",
Media = md("Media (X\u0304)"),
Mediana = "Mediana (Me)",
Moda = "Moda (Mo)",
Varianza = md("Varianza (S\u00B2)"),
Desv_Est = "Desv. Est. (S)",
CV = "C.V. (%)",
Asimetría = "Asimetría (As)",
Curtosis = "Curtosis (K)",
Outliers = "Outliers [Cuáles]"
) %>%
tab_source_note("Autor: Grupo 5") %>%
tab_options(
table.width = pct(100),
table.font.size = px(12),
table.font.names = "Arial",
heading.align = "center",
heading.title.font.size = px(13),
heading.background.color = "#AAAAAA",
heading.border.bottom.color = "#AAAAAA",
column_labels.border.bottom.color = "#CCCCCC",
column_labels.border.bottom.width = px(1),
table_body.border.bottom.color = "#CCCCCC",
table_body.border.bottom.width = px(1),
table.border.top.color = "#AAAAAA",
table.border.top.width = px(1),
table.border.bottom.color = "#AAAAAA",
table.border.bottom.width = px(1),
source_notes.font.size = px(11),
source_notes.border.lr.color = "transparent",
data_row.padding = px(5)
) %>%
tab_style(
style = cell_text(color = "white", weight = "bold"),
locations = cells_title(groups = "title")
) %>%
tab_style(
style = cell_text(color = "#333333", align = "center"),
locations = cells_column_labels()
) %>%
tab_style(
style = cell_text(color = "#333333", align = "center"),
locations = cells_body(columns = everything())
)
| Tabla N°2: Indicadores Estadísticos de Longitud | ||||||||||||
| Variable | Mínimo | Máximo | Rango (R) | Media (X̄) | Mediana (Me) | Moda (Mo) | Varianza (S²) | Desv. Est. (S) | C.V. (%) | Asimetría (As) | Curtosis (K) | Outliers [Cuáles] |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Longitud (°) | -152.13 | 174.36 | 326.49 | -54.65 | -93.4 | -115 | 4610.38 | 67.9 | 124.24 | 1.0723 | -0.0522 | 7 [173.31, 173.42, 174.11, 174.19, 174.29, 174.31, 174.36] |
| Autor: Grupo 5 | ||||||||||||
Los valores de la longitud geográfica de los yacimientos fluctúan entre -152.13° y 174.36° y giran en torno a -93.4° (mediana), con una desviación estándar de 67.9, con valores atípicos (7 en total: 173.31°, 173.42°, 174.11°, 174.19°, 174.29°, 174.31°, 174.36), siendo un conjunto de datos heterogéneo (C.V. = 124.24%), cuyos valores se agrupan débilmente en la parte baja (occidental) de la longitud. Por lo anterior, el comportamiento es beneficioso, ya que refleja una dispersión geográfica amplia de los yacimientos a nivel mundial, reduciendo la concentración de riesgo operativo y geopolítico en una sola región.