0. Librerías

library(knitr)
library(kableExtra)
library(e1071)

1. Leer Datos

variables <- read.csv("C:/Users/WAN/Downloads/GlobalWeatherRepository.csv")

2. Extracción y Depuración de la Variable

# Corrección ortográfica de la variable a lo largo de todo el documento
Longitud <- na.omit(variables$longitude)

# Total de datos
n_total <- length(Longitud)

3. Frecuencias

3.1 Max y Min

valor_min <- min(Longitud)
valor_max <- max(Longitud)
rango <- valor_max - valor_min

3.2. Regla de Sturges

K_sturges <- floor(1 + 3.322 * log10(n_total))
cat("Número de clases:", K_sturges, "\n")
## Número de clases: 18
A_sturges <- rango / K_sturges
cat("Amplitud Sturges:", A_sturges, "\n")
## Amplitud Sturges: 19.69

3.3. Intervalos

# Se garantiza que no exceda el dominio geográfico real de la longitud (-180 a 180)
Li1 <- seq(max(-180, valor_min), min(180, valor_max) - A_sturges, by = A_sturges)
Ls1 <- pmin(180, Li1 + A_sturges)

3.4. Bucle para las columnas de la tabla

ni1 <- numeric(length(Li1))

for(i in 1:length(Li1)){
  if(i == length(Li1)){
    ni1[i] <- sum(Longitud >= Li1[i] & Longitud <= Ls1[i])
  } else {
    ni1[i] <- sum(Longitud >= Li1[i] & Longitud < Ls1[i])
  }
}

# Frecuencias adicionales
hi1 <- (ni1 / sum(ni1)) * 100
Ni_asc1 <- cumsum(ni1)
Hi_asc1 <- cumsum(hi1)
Ni_dsc1 <- rev(cumsum(rev(ni1)))
Hi_dsc1 <- rev(cumsum(rev(hi1)))
MC1 <- (Li1 + Ls1)/2

3.5. Frecuencia para tabla con amplitud ajustada (Amplitud = 50)

amplitud <- 50

# Ajuste de intervalos simétricos respetando los límites de la cartografía terrestre
Li2 <- seq(floor(max(-180, valor_min)/50)*50, ceiling(min(180, valor_max)/50)*50 - 50, by = 50)
Ls2 <- Li2 + 50

ni2 <- numeric(length(Li2))

for(i in 1:length(Li2)){
  if(i == length(Li2)){
    ni2[i] <- sum(Longitud >= Li2[i] & Longitud <= Ls2[i])
  } else {
    ni2[i] <- sum(Longitud >= Li2[i] & Longitud < Ls2[i])
  }
}

hi2 <- (ni2 / sum(ni2)) * 100
Ni_asc2 <- cumsum(ni2)
Hi_asc2 <- cumsum(hi2)
Ni_dsc2 <- rev(cumsum(rev(ni2)))
Hi_dsc2 <- rev(cumsum(rev(hi2)))
MC2 <- (Li2 + Ls2)/2

4. Tabla de Distribución de Frecuencias

4.1. Tabla según Sturges

Tabla_Sturges <- data.frame(
  Lim_inf = round(Li1, 2),
  Lim_sup = round(Ls1, 2),
  MC = round(MC1, 2),
  ni = ni1,
  hi = round(hi1, 2),
  Ni_asc = Ni_asc1,
  Hi_asc = round(Hi_asc1, 2),
  Ni_dsc = Ni_dsc1,
  Hi_dsc = round(Hi_dsc1, 2)
)

Tabla_Sturges2 <- Tabla_Sturges
Tabla_Sturges2[] <- lapply(Tabla_Sturges2, as.character)

# Fila de totales
fila_total <- data.frame(
  Lim_inf = "TOTAL", Lim_sup = "", MC = "",
  ni = as.character(sum(ni1)),
  hi = as.character(round(sum(hi1), 2)),
  Ni_asc = "", Hi_asc = "", Ni_dsc = "", Hi_dsc = ""
)

Tabla_Sturges2 <- rbind(Tabla_Sturges2, fila_total)

kable(
  Tabla_Sturges2,
  align = "c",
  caption = "Tabla N°1: Distribución de frecuencias de Longitudes de los registros meteorológicos mundiales mediante la regla de Sturges"
) |>
  kableExtra::kable_styling(full_width = TRUE, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) |>
  kableExtra::row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") |>
  kableExtra::row_spec(nrow(Tabla_Sturges2), bold = TRUE, background = "#EAEDED") |>
  footnote(general = "Elaborado por Grupo 2. Fuente: Global Weather Repository.", general_title = "Nota: ", footnote_as_chunk = TRUE, title_format = c("italic","bold"))
Tabla N°1: Distribución de frecuencias de Longitudes de los registros meteorológicos mundiales mediante la regla de Sturges
Lim_inf Lim_sup MC ni hi Ni_asc Hi_asc Ni_dsc Hi_dsc
-175.2 -155.51 -165.35 1448 1.02 1448 1.02 141564 100
-155.51 -135.82 -145.66 0 0 1448 1.02 140116 98.98
-135.82 -116.13 -125.97 719 0.51 2167 1.53 140116 98.98
-116.13 -96.44 -106.28 725 0.51 2892 2.04 139397 98.47
-96.44 -76.75 -86.6 8640 6.1 11532 8.15 138672 97.96
-76.75 -57.06 -66.9 13812 9.76 25344 17.9 130032 91.85
-57.06 -37.37 -47.22 1453 1.03 26797 18.93 116220 82.1
-37.37 -17.68 -27.53 1454 1.03 28251 19.96 114767 81.07
-17.68 2.01 -7.84 12169 8.6 40420 28.55 113313 80.04
2.01 21.7 11.85 30318 21.42 70738 49.97 101144 71.45
21.7 41.39 31.54 26403 18.65 97141 68.62 70826 50.03
41.39 61.08 51.23 14972 10.58 112113 79.2 44423 31.38
61.08 80.77 70.92 6545 4.62 118658 83.82 29451 20.8
80.77 100.46 90.61 2950 2.08 121608 85.9 22906 16.18
100.46 120.15 110.3 8456 5.97 130064 91.88 19956 14.1
120.15 139.84 129.99 4362 3.08 134426 94.96 11500 8.12
139.84 159.53 149.68 2182 1.54 136608 96.5 7138 5.04
159.53 179.22 169.37 4956 3.5 141564 100 4956 3.5
TOTAL 141564 100
Nota: Elaborado por Grupo 2. Fuente: Global Weather Repository.

4.2. Tabla agrupada y ajustada (Amplitud de 50)

TDF_Longitud <- data.frame(
  Lim_inf = round(Li2, 2),
  Lim_sup = round(Ls2, 2),
  MC = round(MC2, 2),
  ni = ni2,
  hi = round(hi2, 2),
  Ni_asc = Ni_asc2,
  Hi_asc = round(Hi_asc2, 2),
  Ni_dsc = Ni_dsc2,
  Hi_dsc = round(Hi_dsc2, 2)
)

TDF_Longitud[] <- lapply(TDF_Longitud, as.character)

TDF_Total <- data.frame(
  Lim_inf = "TOTAL", Lim_sup = "", MC = "",
  ni = as.character(sum(ni2)),
  hi = as.character(round(sum(hi2), 2)),
  Ni_asc = "", Hi_asc = "", Ni_dsc = "", Hi_dsc = "",
  stringsAsFactors = FALSE
)

TDF_Longitud <- rbind(TDF_Longitud, TDF_Total)
colnames(TDF_Longitud) <- c("Lim. Inf.", "Lim. Sup.", "MC", "ni", "hi (%)", "Ni Asc", "Hi Asc (%)", "Ni Dsc", "Hi Dsc (%)")

kable(
  TDF_Longitud,
  align = "c",
  caption = "Tabla N°2: Distribución de frecuencias de Longitudes con amplitud de clase de 50°"
) |>
  kable_styling(full_width = TRUE, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) |>
  row_spec(0, bold = TRUE, color = "white", background = "#2C3E50") |>
  row_spec(nrow(TDF_Longitud), bold = TRUE, background = "#EAEDED") |>
  footnote(general = "Elaborado por Grupo 2. Fuente: Global Weather Repository.", general_title = "Nota: ", footnote_as_chunk = TRUE, title_format = c("italic","bold"))
Tabla N°2: Distribución de frecuencias de Longitudes con amplitud de clase de 50°
Lim. Inf. Lim. Sup. MC ni hi (%) Ni Asc Hi Asc (%) Ni Dsc Hi Dsc (%)
-200 -150 -175 1448 1.02 1448 1.02 141703 100
-150 -100 -125 721 0.51 2169 1.53 140255 98.98
-100 -50 -75 24627 17.38 26796 18.91 139534 98.47
-50 0 -25 12627 8.91 39423 27.82 114907 81.09
0 50 25 66873 47.19 106296 75.01 102280 72.18
50 100 75 15312 10.81 121608 85.82 35407 24.99
100 150 125 14274 10.07 135882 95.89 20095 14.18
150 200 175 5821 4.11 141703 100 5821 4.11
TOTAL 141703 100
Nota: Elaborado por Grupo 2. Fuente: Global Weather Repository.

5. Gráficos de Distribución de Frecuencias

5.1. Histograma Original (ni)

hist(
  Longitud,
  breaks = c(Li2, max(Ls2)),
  main = "Gráfico N°1: Distribución de frecuencias absolutas de Longitudes\nRegistros meteorológicos mundiales, período 2024–2026",
  xlab = "Longitud (°)",
  ylab = "Frecuencia absoluta (ni)",
  col = "lightgreen",
  border = "black",
  xaxt = "n"
)
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.2. Histograma con relación al todo (ni)

hist(
  Longitud,
  breaks = c(Li2, max(Ls2)),
  ylim = c(0, n_total),
  main = "Gráfico N°2: Histograma de Longitud en Relación al Todo\n(Escala completa de la muestra)",
  xlab = "Longitud (°)",
  ylab = "Frecuencia absoluta (ni)",
  col = "lightgreen",
  border = "black",
  xaxt = "n"
)
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.3. Histograma original (hi)

bp <- barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = " a "),
  ylim = c(0, max(hi2) * 1.1),
  main = "Gráfico N°3: Distribución de frecuencias relativas de Longitudes",
  xlab = "Intervalos de Longitud (°)",
  ylab = "Frecuencia Relativa (%)",
  col = "skyblue",
  border = "black",
  las = 2,
  cex.names = 0.8,
  space = 0
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.4. Histograma con relación a todo (hi)

barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = " a "),
  ylim = c(0, 100),
  main = "Gráfico N°4: Histograma Global de Frecuencia Relativa\n(Escala completa de 0 a 100%)",
  xlab = "Intervalos de Longitud (°)",
  ylab = "Frecuencia Relativa (%)",
  col = "lightgreen",
  border = "black",
  las = 2,
  cex.names = 0.8,
  space = 0
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.5. Polígono de frecuencias (hi)

bp <- barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = "-"),
  col = "lightgreen",
  border = "black",
  ylim = c(0, max(hi2)*1.2),
  main = "Gráfico N°5: Polígono de frecuencias relativas de Longitudes",
  xlab = "Intervalos de Longitud (°)",
  ylab = "Frecuencia relativa (%)",
  las = 2,
  cex.names = 0.8,
  space = 0
)

# Sincronización exacta en los centros de las columnas del barplot
lines(
  bp,
  hi2,
  type = "o",
  col = "red",
  lwd = 2,
  pch = 16
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.6. Ojiva ascendente y descendente (ni)

# Reparación completa del bloque que venía cortado de origen
eje_x_asc <- c(Li2, max(Ls2))
ni_asc_graf <- c(0, Ni_asc2)

eje_x_dsc <- c(Li2, max(Ls2))
ni_dsc_graf <- c(Ni_dsc2, 0)

plot(
  eje_x_asc,
  ni_asc_graf,
  type = "o",
  pch = 16,
  lwd = 2,
  col = "blue",
  ylim = c(0, n_total),
  main = "Gráfico N°6: Ojivas de Frecuencia Absoluta Acumulada de Longitud",
  xlab = "Longitud (°)",
  ylab = "Frecuencia Acumulada (Ni)",
  xaxt = "n"
)

lines(
  eje_x_dsc,
  ni_dsc_graf,
  type = "o",
  pch = 17,
  lwd = 2,
  col = "red"
)

axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

legend(
  "right",
  legend = c("Ojiva Ascendente (<= Ls)", "Ojiva Descendente (>= Li)"),
  col = c("blue", "red"),
  pch = c(16, 17),
  lwd = 2,
  bty = "n",
  cex = 0.9
)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.7. Ojiva ascendente y descendente (ni)

# 1. Preparar datos para que las curvas inicien y terminen en cero sobre los límites de clase exactos
eje_x_asc <- c(Li2, max(Ls2))
ni_asc_graf <- c(0, Ni_asc2)

eje_x_dsc <- c(Li2, max(Ls2))
ni_dsc_graf <- c(Ni_dsc2, 0)

# 2. Graficar Ojiva Absoluta Ascendente
plot(eje_x_asc, ni_asc_graf, 
     type = "o", pch = 16, lwd = 2, col = "blue",
     ylim = c(0, n_total),
     main = "Gráfico N°6: Ojivas de Frecuencia Absoluta Acumulada de la Longitud",
     xlab = "Longitud (°)", 
     ylab = "Frecuencia Acumulada (Ni)",
     xaxt = "n")

# 3. Superponer Ojiva Descendente
lines(eje_x_dsc, ni_dsc_graf, type = "o", pch = 17, lwd = 2, col = "red")

# 4. Forzar el eje X de 50 en 50 rotado verticalmente
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

# 5. Leyenda y cuadrícula
legend("right", 
       legend = c("Ojiva Ascendente (<= Ls)", "Ojiva Descendente (>= Li)"), 
       col = c("blue", "red"), pch = c(16, 17), lwd = 2, bty = "n", cex = 0.9)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.8. Ojiva ascendente y descendente (hi)

#  Preparar datos porcentuales acumulados
hi_asc_graf <- c(0, Hi_asc2)
hi_dsc_graf <- c(Hi_dsc2, 0)

#  Graficar Ojiva Relativa Ascendente
plot(eje_x_asc, hi_asc_graf, 
     type = "o", pch = 16, lwd = 2, col = "blue",
     ylim = c(0, 100),
     main = "Gráfico N°7: Ojivas de Frecuencia Relativa Acumulada de la Longitud",
     xlab = "Longitud (°)", 
     ylab = "Frecuencia Acumulada (%)",
     xaxt = "n")

#  Superponer Ojiva Descendente
lines(eje_x_dsc, hi_dsc_graf, type = "o", pch = 17, lwd = 2, col = "red")

#  Eje X simétrico
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

#  Leyenda y cuadrícula
legend("right", 
       legend = c("Ascendente (<= Ls)", "Descendente (>= Li)"), 
       col = c("blue", "red"), pch = c(16, 17), lwd = 2, bty = "n", cex = 0.9)
grid(nx = NA, ny = NULL, lty = 2, col = "gray")

5.9. Boxplot

#  Calcular Cuartiles y Rango Intercuartílico (RIC)
Q1 <- quantile(Longitud, 0.25)
Q3 <- quantile(Longitud, 0.75)
RIC <- Q3 - Q1

#  Límites de Tukey estándar acotados al dominio cartográfico terrestre
Lim_inf_Tukey <- max(-180, Q1 - 1.5 * RIC)
Lim_sup_Tukey <- min(180, Q3 + 1.5 * RIC)

#  Dibujar Boxplot acotado a la misma escala de los intervalos
boxplot(Longitud,
        horizontal = TRUE,
        outline = FALSE,
        ylim = c(min(Li2), max(Ls2)), 
        main = "Gráfico N°8: Diagrama de Caja y Bigotes de la Longitud con Límites de Tukey",
        xlab = "Longitud (°)",
        col = "lightblue",
        xaxt = "n")

#  Forzar el eje X idéntico de 50 en 50
axis(1, at = c(Li2, max(Ls2)), labels = c(Li2, max(Ls2)), las = 2, cex.axis = 0.8)

#  Dibujar atípicos reales
atipicos <- Longitud[Longitud < (Q1 - 1.5 * RIC) | Longitud > (Q3 + 1.5 * RIC)]
if(length(atipicos) > 0) {
  points(atipicos, rep(1, length(atipicos)), col = "red", pch = 19, cex = 0.8)
}

#  Dibujar líneas de límites
abline(v = Lim_inf_Tukey, col = "blue", lwd = 2, lty = 2)
abline(v = Lim_sup_Tukey, col = "blue", lwd = 2, lty = 2)

#  Leyenda
legend("topright",
       legend = c(paste("Atípicos (N =", length(atipicos), ")"), "Límites de Tukey"),
       col = c("red", "blue"), pch = c(19, NA), lty = c(NA, 2), lwd = c(NA, 2), bty = "n", cex = 0.9)
grid(nx = NULL, ny = NA, lty = 2, col = "gray")

6. Indicadores Estadísticos

6.1. Tendencia central

media <- mean(Longitud)
mediana <- median(Longitud)

# Moda calculada de forma robusta
frecuencias_valores <- table(Longitud)
moda <- as.numeric(names(frecuencias_valores)[frecuencias_valores == max(frecuencias_valores)])
if(length(moda) > 1) { moda <- moda[1] }

6.2. Dispersión

varianza <- var(Longitud)
desv_est <- sd(Longitud)
cv <- (desv_est / media) * 100

6.3. Forma

asimetria <- skewness(Longitud)
curtosis <- kurtosis(Longitud)

6.4. Valores atípicos

rango_texto <- paste0("[", round(min(Longitud), 2), "°; ", round(max(Longitud), 2), "°]")
intervalo_atipicos <- paste0("[", round(Lim_inf_Tukey, 2), "°; ", round(Lim_sup_Tukey, 2), "°]")
n_atipicos <- length(atipicos)

6.5. Tabla de indicadores

tabla_indicadores <- data.frame(
  Variable = "Longitud",
  Rango = rango_texto,
  Media = round(media, 2),
  Mediana = round(mediana, 2),
  Moda = round(moda, 2),
  Varianza = round(varianza, 2),
  Desv_Est = round(desv_est, 2),
  CV = paste0(round(cv, 2), "%"),
  Asimetria = round(asimetria, 2),
  Curtosis = round(curtosis, 2),
  Umbral = intervalo_atipicos,
  N_Atipicos = n_atipicos
)

colnames(tabla_indicadores) <- c("Variable", "Rango Real", "Media", "Mediana", "Moda", "Varianza", "Desv. Est.", "CV (%)", "Asimetría", "Curtosis", "Umbral de Tukey", "N° Atípicos")

# Mostrar tabla estilizada final
kable(
  tabla_indicadores,
  align = "c",
  caption = "Tabla N°3: Indicadores estadísticos descriptivos de la variable Longitud"
) |>
  kable_styling(
    full_width = TRUE,
    position = "center",
    bootstrap_options = c("striped", "hover", "condensed", "responsive")
  ) |>
  row_spec(0, bold = TRUE, color = "white", background = "#2C3E50")
Tabla N°3: Indicadores estadísticos descriptivos de la variable Longitud
Variable Rango Real Media Mediana Moda Varianza Desv. Est. CV (%) Asimetría Curtosis Umbral de Tukey N° Atípicos
Longitud [-175.2°; 179.22°] 21.94 23.24 -58.17 4327.43 65.78 299.87% 0.01 0.34 [-91.91°; 134.96°] 10898

7. Conclusiones

La distribución geográfica de la variable Longitud con promedio de 21.94° y una mediana de 23.24°, con el valor 25.0° repitiéndose como la moda, acompañados de una asimetría casi nula de 0.01 y una curtosis de 0.34. Las ubicaciones presentan una dispersión alta debido a una desviación estándar de 65.78°, una varianza de 4,327.43 y un coeficiente de variación del 299.87% con la presencia de 10,898 valores atípicos según el criterio de Tukey. Esta amplia dispersión de la longitud es muy beneficiosa para el ambiente y las personas porque confirma una cobertura de monitoreo casi global, sin embargo, la presencia masiva de datos atípicos distribuidos en los extremos geográficos es perjudicial, ya que la desigual concentración de estaciones deja grandes vacíos de información meteorológica en vastas zonas oceánicas despobladas.