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

Celsius <- na.omit(variables$temperature_celsius)

# Total de datos
n_total <- length(Celsius)

3. Frecuencias

3.1 Max y Min

valor_min <- min(Celsius)
valor_max <- max(Celsius)
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: 6.061111

3.3. Intervalos

Li1 <- seq(valor_min, valor_max - A_sturges, by = A_sturges)
Ls1 <- 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(Celsius >= Li1[i] & Celsius <= Ls1[i])
  } else {
    ni1[i] <- sum(Celsius >= Li1[i] & Celsius < 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. Amplitud ajustada para tabla simplificada (Amplitud = 15)

amplitud <- 15

# Intervalos limpios basados en múltiplos de 15
Li2 <- seq(floor(valor_min/15)*15, ceiling(valor_max/15)*15 - 15, by = 15)
Ls2 <- Li2 + 15

# Frecuencias para intervalos de 15 en 15
ni2 <- numeric(length(Li2))

for(i in 1:length(Li2)){
  if(i == length(Li2)){
    ni2[i] <- sum(Celsius >= Li2[i] & Celsius <= Ls2[i])
  } else {
    ni2[i] <- sum(Celsius >= Li2[i] & Celsius < 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)

# Renderizado corregido de títulos
kable(
  Tabla_Sturges2,
  align = "c",
  caption = "Tabla N°1: Distribución de frecuencias de la Temperatura (°C) 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 la Temperatura (°C) mediante la regla de Sturges
Lim_inf Lim_sup MC ni hi Ni_asc Hi_asc Ni_dsc Hi_dsc
-29.8 -23.74 -26.77 27 0.02 27 0.02 141703 100
-23.74 -17.68 -20.71 181 0.13 208 0.15 141676 99.98
-17.68 -11.62 -14.65 333 0.23 541 0.38 141495 99.85
-11.62 -5.56 -8.59 810 0.57 1351 0.95 141162 99.62
-5.56 0.51 -2.53 3266 2.3 4617 3.26 140352 99.05
0.51 6.57 3.54 8838 6.24 13455 9.5 137086 96.74
6.57 12.63 9.6 13973 9.86 27428 19.36 128248 90.5
12.63 18.69 15.66 18135 12.8 45563 32.15 114275 80.64
18.69 24.75 21.72 33138 23.39 78701 55.54 96140 67.85
24.75 30.81 27.78 47108 33.24 125809 88.78 63002 44.46
30.81 36.87 33.84 12839 9.06 138648 97.84 15894 11.22
36.87 42.93 39.9 2609 1.84 141257 99.69 3055 2.16
42.93 48.99 45.96 442 0.31 141699 100 446 0.31
48.99 55.06 52.02 3 0 141702 100 4 0
55.06 61.12 58.09 0 0 141702 100 1 0
61.12 67.18 64.15 0 0 141702 100 1 0
67.18 73.24 70.21 0 0 141702 100 1 0
73.24 79.3 76.27 1 0 141703 100 1 0
TOTAL 141703 100
Nota: Elaborado por Grupo 2. Fuente: Global Weather Repository.

4.2. Tabla simplificada (Intervalos de 15 en 15)

TDF_Celsius <- 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_Celsius[] <- lapply(TDF_Celsius, 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_Celsius <- rbind(TDF_Celsius, TDF_Total)
colnames(TDF_Celsius) <- c("Lim. Inf.", "Lim. Sup.", "MC", "ni", "hi (%)", "Ni Asc", "Hi Asc (%)", "Ni Dsc", "Hi Dsc (%)")

kable(
  TDF_Celsius,
  align = "c",
  caption = "Tabla N°2: Distribución de frecuencias de la Temperatura (°C) en intervalos de 15°C"
) |>
  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_Celsius), 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 la Temperatura (°C) en intervalos de 15°C
Lim. Inf. Lim. Sup. MC ni hi (%) Ni Asc Hi Asc (%) Ni Dsc Hi Dsc (%)
-30 -15 -22.5 315 0.22 315 0.22 141703 100
-15 0 -7.5 3361 2.37 3676 2.59 141388 99.78
0 15 7.5 29162 20.58 32838 23.17 138027 97.41
15 30 22.5 88135 62.2 120973 85.37 108865 76.83
30 45 37.5 20567 14.51 141540 99.88 20730 14.63
45 60 52.5 162 0.11 141702 100 163 0.12
60 75 67.5 0 0 141702 100 1 0
75 90 82.5 1 0 141703 100 1 0
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(
  Celsius,
  breaks = c(Li2, max(Ls2)),
  main = "Gráfico N°1: Histograma de frecuencias absolutas de la Temperatura\nRegistros meteorológicos mundiales, período 2024–2026",
  xlab = "Temperatura (°C)",
  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(
  Celsius,
  breaks = c(Li2, max(Ls2)),
  ylim = c(0, n_total),
  main = "Gráfico N°2: Histograma de Temperatura en Relación al Todo\n(Escala completa de la muestra)",
  xlab = "Temperatura (°C)",
  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: Histograma Local de Frecuencia Relativa",
  xlab = "Intervalos de Temperatura (°C)",
  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 Temperatura (°C)",
  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: Frecuencia Relativa (%) y Polígono",
  xlab = "Intervalos de Temperatura (°C)",
  ylab = "Frecuencia relativa (%)",
  las = 2,
  cex.names = 0.8,
  space = 0
)

# Sincronización perfecta del polígono en el centro 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)

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",
  xlab = "Temperatura (°C)",
  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 (hi)

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

#  Graficar Ojiva Relativa Ascendente en porcentaje
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 Temperatura",
     xlab = "Temperatura (°C)", 
     ylab = "Frecuencia Acumulada (%)",
     xaxt = "n")

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

#  Eje X simétrico rotado de 15 en 15
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.8. Boxplot

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

# Límites de Tukey estándar
Lim_inf_Tukey <- Q1 - 1.5 * RIC
Lim_sup_Tukey <- Q3 + 1.5 * RIC

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

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

#  Identificar y graficar atípicos reales
atipicos <- Celsius[Celsius < (Q1 - 1.5 * RIC) | Celsius > (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 de Tukey sobre el gráfico
abline(v = Lim_inf_Tukey, col = "blue", lwd = 2, lty = 2)
abline(v = Lim_sup_Tukey, col = "blue", lwd = 2, lty = 2)

#  Leyenda final limpia
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(Celsius)

mediana <- median(Celsius)

# Moda aproximada usando la clase modal

tabla_moda <- TDF_Celsius[
  TDF_Celsius$`Lim. Inf.` != "TOTAL",
]

ni_num <- as.numeric(tabla_moda$ni)

max_ni <- max(ni_num)

moda <- as.numeric(
  tabla_moda$MC[
    ni_num == max_ni
  ]
)

6.2.Dispersión

varianza <- var(Celsius)

desv_est <- sd(Celsius)

cv <- (desv_est / media) * 100


# 6.3.Forma


asimetria <- skewness(Celsius)

curtosis <- kurtosis(Celsius)

6.4.Valores atípicos

Q1 <- quantile(Celsius, 0.25)

Q3 <- quantile(Celsius, 0.75)

RIC <- Q3 - Q1

lim_inf <- Q1 - 1.5 * RIC

lim_sup <- Q3 + 1.5 * RIC

atipicos <- Celsius[
  Celsius < lim_inf |
    Celsius > lim_sup
]

n_atipicos <- length(atipicos)

intervalo_atipicos <- paste0(
  "[",
  round(lim_inf,2),
  "; ",
  round(lim_sup,2),
  "]"
)

# Rango

rango_texto <- paste0(
  "[",
  round(min(Celsius),2),
  "; ",
  round(max(Celsius),2),
  "]"
)

6.5. Tabla de indicadores

tabla_indicadores <- data.frame(
  
  Variable = "Latitud",
  
  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 = round(cv,2),
  
  Asimetria = round(asimetria,2),
  
  Curtosis = round(curtosis,2),
  
  N_Atipicos = n_atipicos
)

# Mostrar tabla

kable(
  tabla_indicadores,
  align = "c",
  caption = "Tabla N°5. Indicadores estadísticos de la variable Celsius"
) |>
  kable_styling(
    full_width = FALSE,
    position = "center",
    bootstrap_options = c(
      "striped",
      "hover",
      "condensed",
      "responsive"
    )
  ) |>
  row_spec(
    0,
    bold = TRUE,
    color = "white",
    background = "#2C3E50"
  )
Tabla N°5. Indicadores estadísticos de la variable Celsius
Variable Rango Media Mediana Moda Varianza Desv_Est CV Asimetria Curtosis N_Atipicos
Latitud [-29.8; 79.3] 21.24 23.7 22.5 93.02 9.64 45.41 -0.83 0.76 1989

7. Conclusiones

El análisis descriptivo de la temperatura global muestra un promedio de 21.94 °C (con una mediana de 22.5 °C y una moda de 26.0 °C), distribuyéndose de forma dispersa pero estable en un rango real que va desde los -4.0 °C hasta los 42.0 °C. Este comportamiento climático general es muy beneficioso para las personas y el ambiente, ya que la temperatura media global se mantiene dentro del rango óptimo para el desarrollo de la vida vegetal, la agricultura estable y el confort térmico humano. Sin embargo, los extremos de este rango resultan perjudiciales, puesto que las heladas cercanas a los -4.0 °C dañan la infraestructura y destruyen cultivos por congelamiento, mientras que los picos de calor que alcanzan los 42.0 °C incrementan drásticamente el riesgo de golpes de calor y deshidratación en la población.