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


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: 23.3207
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 (%)
−152.129 −128.808 −140.469 36 0.480
−128.808 −105.488 −117.148 1,255 16.650
−105.488 −82.167 −93.828 2,897 38.440
−82.167 −58.846 −70.506 857 11.370
−58.846 −35.525 −47.185 133 1.760
−35.525 −12.205 −23.865 10 0.130
−12.205 11.116 −0.545 929 12.330
11.116 34.437 22.776 422 5.600
34.437 57.757 46.097 427 5.670
57.757 81.078 69.418 181 2.400
81.078 104.399 92.738 126 1.670
104.399 127.720 116.060 221 2.930
127.720 151.040 139.380 33 0.440
151.040 174.361 162.700 9 0.120
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 longitud (por ejemplo, límites como -128.808° o -105.488°). 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: 12

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

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 = "Longitud (°)", 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 = "Longitud (°)", 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 = "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)
  )

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


7.Indicadores Estadísticos

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
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 = "Longitud (°)",
  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 Longitud**")) %>%
  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 Longitud
Variable Rango Media (X̄) Mediana (Me) Moda (Mo) Varianza (S²) Desv. Est. (S) C.V. (%) Asimetria (As) Curtosis (K) Outliers [Intervalo]
Longitud (°) [-152.13; 174.36] -54.65 -93.4 -115 4610.38 67.9 124.24 1.0723 -0.0522 7 [173.31; 174.36]
Autor: Grupo 5

8.Conclusiones

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: entre 173.31° y 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.