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)

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

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

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

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