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 Latitude (Latitud
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$Latitude)
Variable <- na.omit(as.numeric(valores_limpios))
N <- length(Variable)
Minimo_real <- min(Variable)
Maximo_real <- max(Variable)
cat("Variable analizada: Latitude\n")
## Variable analizada: Latitude
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: -53.9713 grados
cat("Máximo real observado en el dataset:", round(Maximo_real, 4), "grados\n")
## Máximo real observado en el dataset: 73.4344 grados
Naturaleza y escala de la variable: la Latitud 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° (línea del Ecuador) 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 latitudes (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 -53.97° hasta 73.43°.
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.
R_sturges <- Maximo_real - Minimo_real
k_sturges <- ceiling(1 + 3.322 * log10(N))
Amplitud_sturges <- R_sturges / k_sturges
Li_sturges <- seq(from = Minimo_real, to = Maximo_real - Amplitud_sturges, by = Amplitud_sturges)
Ls_sturges <- c(seq(from = Minimo_real + Amplitud_sturges, to = Maximo_real - Amplitud_sturges, by = Amplitud_sturges), Maximo_real)
Li_sturges <- round(Li_sturges, 3)
Ls_sturges <- round(Ls_sturges, 3)
MC_sturges <- (Li_sturges + Ls_sturges) / 2
ni_sturges <- numeric(length(Li_sturges))
for (i in 1:length(Li_sturges)) {
if (i < length(Li_sturges)) {
ni_sturges[i] <- sum(Variable >= Li_sturges[i] & Variable < Ls_sturges[i])
} else {
ni_sturges[i] <- sum(Variable >= Li_sturges[i] & Variable <= Ls_sturges[i])
}
}
hi_sturges <- (ni_sturges / N) * 100
TDF_Sturges <- data.frame(
Li = Li_sturges,
Ls = Ls_sturges,
MC = round(MC_sturges, 3),
ni = ni_sturges,
hi_perc = round(hi_sturges, 2)
)
cat("Número de intervalos (k):", k_sturges, "\n")
## Número de intervalos (k): 14
cat("Amplitud exacta de clase:", round(Amplitud_sturges, 4), "\n")
## Amplitud exacta de clase: 9.1004
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 = 3) %>%
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 (%) |
|---|---|---|---|---|
| −53.971 | −44.871 | −49.421 | 78 | 1.030 |
| −44.871 | −35.770 | −40.321 | 121 | 1.610 |
| −35.770 | −26.670 | −31.220 | 27 | 0.360 |
| −26.670 | −17.570 | −22.120 | 124 | 1.650 |
| −17.570 | −8.469 | −13.020 | 65 | 0.860 |
| −8.469 | 0.631 | −3.919 | 245 | 3.250 |
| 0.631 | 9.732 | 5.181 | 662 | 8.780 |
| 9.732 | 18.832 | 14.282 | 211 | 2.800 |
| 18.832 | 27.932 | 23.382 | 480 | 6.370 |
| 27.932 | 37.033 | 32.483 | 2,389 | 31.700 |
| 37.033 | 46.133 | 41.583 | 559 | 7.420 |
| 46.133 | 55.234 | 50.684 | 1,717 | 22.780 |
| 55.234 | 64.334 | 59.784 | 727 | 9.650 |
| 64.334 | 73.434 | 68.884 | 130 | 1.720 |
| n = 7,537 | k (Sturges) = 14 | ||||
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 latitud (por ejemplo, límites como -44.871° o -35.77°). 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: 14
Esta tabla simplificada se deriva y coincide lógicamente con los 14 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 latitud 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 latitud geográfica (Límites enteros) | |||||||||||
| Lim. Inf (°) | Lim. Sup (°) | Marca clase | ni | hi (Unit) | hi (%) | Ni (↑) | Ni (↓) | Hi (Unit ↑) | Hi (Unit ↓) | Hi (% ↑) | Hi (% ↓) |
|---|---|---|---|---|---|---|---|---|---|---|---|
| −60.00 | −50.00 | −55.00 | 21 | 0.0028 | 0.28 | 21 | 7,537 | 0.0028 | 1.0000 | 0.28 | 100.00 |
| −50.00 | −40.00 | −45.00 | 58 | 0.0077 | 0.77 | 79 | 7,516 | 0.0105 | 0.9972 | 1.05 | 99.72 |
| −40.00 | −30.00 | −35.00 | 136 | 0.0180 | 1.80 | 215 | 7,458 | 0.0285 | 0.9895 | 2.85 | 98.95 |
| −30.00 | −20.00 | −25.00 | 112 | 0.0149 | 1.49 | 327 | 7,322 | 0.0434 | 0.9715 | 4.34 | 97.15 |
| −20.00 | −10.00 | −15.00 | 68 | 0.0090 | 0.90 | 395 | 7,210 | 0.0524 | 0.9566 | 5.24 | 95.66 |
| −10.00 | 0.00 | −5.00 | 241 | 0.0320 | 3.20 | 636 | 7,142 | 0.0844 | 0.9476 | 8.44 | 94.76 |
| 0.00 | 10.00 | 5.00 | 713 | 0.0946 | 9.46 | 1,349 | 6,901 | 0.1790 | 0.9156 | 17.90 | 91.56 |
| 10.00 | 20.00 | 15.00 | 243 | 0.0322 | 3.22 | 1,592 | 6,188 | 0.2112 | 0.8210 | 21.12 | 82.10 |
| 20.00 | 30.00 | 25.00 | 985 | 0.1307 | 13.07 | 2,577 | 5,945 | 0.3419 | 0.7888 | 34.19 | 78.88 |
| 30.00 | 40.00 | 35.00 | 1,986 | 0.2635 | 26.35 | 4,563 | 4,960 | 0.6054 | 0.6581 | 60.54 | 65.81 |
| 40.00 | 50.00 | 45.00 | 1,056 | 0.1401 | 14.01 | 5,619 | 2,974 | 0.7455 | 0.3946 | 74.55 | 39.46 |
| 50.00 | 60.00 | 55.00 | 1,610 | 0.2136 | 21.36 | 7,229 | 1,918 | 0.9591 | 0.2545 | 95.91 | 25.45 |
| 60.00 | 70.00 | 65.00 | 265 | 0.0352 | 3.52 | 7,494 | 308 | 0.9943 | 0.0409 | 99.43 | 4.09 |
| 70.00 | 80.00 | 75.00 | 43 | 0.0057 | 0.57 | 7,537 | 43 | 1.0000 | 0.0057 | 100.00 | 0.57 |
| 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 = "Latitud (°)", 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 = "Latitud (°)", 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 = "Latitud (°)", 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 = "Latitud (°)", 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 Latitud 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
outliers_data <- Variable[Variable < (Q1 - 1.5 * IQR_val) | Variable > (Q3 + 1.5 * IQR_val)]
num_out <- length(outliers_data)
out_txt <- if (num_out > 0) {
paste0(num_out, " [", round(min(outliers_data), 2), "; ", round(max(outliers_data), 2), "]")
} else {
"0 [Sin outliers]"
}
data.frame(
Variable = "Latitud (°)",
Rango = paste0("[", round(min(Variable), 2), "; ", round(max(Variable), 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),
Asimetria = round(asim, 4),
Curtosis = round(kurt, 4),
Outliers = out_txt,
check.names = FALSE
) %>%
gt() %>%
tab_header(title = md("**Tabla N°2: Indicadores Estadisticos de Latitud**")) %>%
cols_label(
Variable = "Variable",
Rango = "Rango",
Media = md("Media (X\u0304)"),
Mediana = "Mediana (Me)",
Moda = "Moda (Mo)",
Varianza = md("Varianza (S\u00B2)"),
Desv_Est = "Desv. Est. (S)",
CV = "C.V. (%)",
Asimetria = "Asimetria (As)",
Curtosis = "Curtosis (K)",
Outliers = "Outliers [Intervalo]"
) %>%
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 Estadisticos de Latitud | ||||||||||
| Variable | Rango | Media (X̄) | Mediana (Me) | Moda (Mo) | Varianza (S²) | Desv. Est. (S) | C.V. (%) | Asimetria (As) | Curtosis (K) | Outliers [Intervalo] |
|---|---|---|---|---|---|---|---|---|---|---|
| Latitud (°) | [-53.97; 73.43] | 32.25 | 32.53 | 35 | 521.16 | 22.83 | 70.78 | -1.2222 | 1.6975 | 430 [-53.97; -7.66] |
| Autor: Grupo 5 | ||||||||||
Los valores de la latitud geográfica de los yacimientos fluctúan entre -53.97° y 73.43° y giran en torno a 32.53° (mediana), con una desviación estándar de 22.83, con valores atípicos (430 en total: entre -53.97° y -7.66°), siendo un conjunto de datos heterogéneo (C.V. = 70.78%), cuyos valores se agrupan medianamente en la parte media-alta (boreal, hemisferio norte) de la latitud. Por lo anterior, el comportamiento es beneficioso, ya que refleja una concentración moderada de los yacimientos en las latitudes medias del hemisferio norte, zona con mayor infraestructura y accesibilidad de explotación, sin dejar de captar presencia en otras franjas geográficas.