1.Carga de Librerías

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)

2.Leer Datos

# 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

3.Selección de la Variable

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°.


4.Conteo (Frecuencias)

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.

4.1 Paso 1: Tabla de Sturges (límites exactos, sin redondear)

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

4.2 Paso 2: Justificación del ajuste

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.

4.3 Paso 3: Tabla ajustada (límites simplificados, derivada de Sturges)

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.


5.Tabla de Distribución de Frecuencias

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

6.Gráficos de Distribución de Frecuencias

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)

6.1 Histograma - Frecuencia Absoluta

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

6.2 Histograma - Frecuencia Relativa Porcentual

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

6.3 Diagrama de Cajas (Boxplot)

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)
  )

6.4 Ojivas Ascendente y Descendente

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)
  )


7.Indicadores Estadísticos

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

8.Conclusiones

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.