0. Librerias

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

# Frecuencia relativa
hi1 <- (ni1 / sum(ni1)) * 100

# Acumuladas
Ni_asc1 <- cumsum(ni1)
Hi_asc1 <- cumsum(hi1)

Ni_dsc1 <- rev(cumsum(rev(ni1)))
Hi_dsc1 <- rev(cumsum(rev(hi1)))

# Marca de clase
MC1 <- (Li1 + Ls1)/2

3.5. Amplitud ajustada para tabla simplificada

amplitud <- 15

# Intervalos
Li2 <- seq(
  floor(valor_min/15)*15,
  ceiling(valor_max/15)*15 - 15,
  by = 15
)

Ls2 <- Li2 + 15

#Frecuencias

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

# Frecuencia relativa
hi2 <- (ni2 / sum(ni2)) * 100

# Acumuladas
Ni_asc2 <- cumsum(ni2)
Hi_asc2 <- cumsum(hi2)

Ni_dsc2 <- rev(cumsum(rev(ni2)))
Hi_dsc2 <- rev(cumsum(rev(hi2)))

# Marca de clase
MC2 <- (Li2 + Ls2)/2

4. Tabla de Distribución de Frecuencias

4.1. Tabla según Sturges

# Convertir a caracteres para poder mezclar números y texto

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

4.2. Tabla simplificada

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

# Agregar fila

Tabla_Sturges2 <- rbind(Tabla_Sturges2, fila_total)


kable(
  Tabla_Sturges2,
  align = "c",
  caption = "Gráfico N°3. Distribución de frecuencias relativas de la 
  latitud de los registros meteorológicos mundiales agrupados en 
  intervalos de 20°, período 2024–2026"
) |>
  
  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")
  )
Gráfico N°3. Distribución de frecuencias relativas de la latitud de los registros meteorológicos mundiales agrupados en intervalos de 20°, período 2024–2026
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.
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)
  
)

# Convertir todo a texto
TDF_Celsius[] <- lapply(TDF_Celsius, as.character)

# Fila TOTAL
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
  
)

# Agregar fila total
TDF_Celsius <- rbind(TDF_Celsius, TDF_Total)

# Renombrar columnas
colnames(TDF_Celsius) <- c(
  
  "Lim. Inf.",
  
  "Lim. Sup.",
  
  "MC",
  
  "ni",
  
  "hi (%)",
  
  "Ni Asc",
  
  "Hi Asc",
  
  "Ni Dsc",
  
  "Hi Dsc"
  
)

# Mostrar tabla

kable(
  TDF_Celsius,
  align = "c",
  caption = "Tabla N°4: Distribución de frecuencia de Celsius del mundo"
) |>
  
  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°4: Distribución de frecuencia de Celsius del mundo
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,
  
  main = "Gráfico N°2. Histograma y polígono de frecuencias absolutas de la 
  Celsius de los registros meteorológicos mundiales, período 2024–2026",
  
  xlab = "Latitud",
  
  ylab = "Frecuencia",
  
  col = "lightgreen",
  
  border = "black"
)

grid()

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 Global de Latitud",
  
  xlab = "Celsius",
  
  ylab = "Frecuencia",
  
  col = "lightgreen",
  
  border = "black"
)

grid()

5.3. Histograma original (hi)

barplot(
  hi2,
  
  names.arg = paste(Li2, Ls2, sep = " a "),
  
  
  main = "Gráfico N°5: Histograma Local de Frecuencia Relativa",
  
  xlab = "Intervalos de Celsius",
  
  ylab = "Frecuencia Relativa (%)",
  
  col = "skyblue",
  
  las = 2,
  space = 0
)

5.4.Histograma con relacion a todo (hi)

barplot(
  hi2,
  
  names.arg = paste(Li2, Ls2, sep = " a "),
  
  ylim = c(0, 100),
  
  main = "Gráfico N°6: Histograma Global de Frecuencia Relativa",
  
  xlab = "Intervalos de Celsius",
  
  ylab = "Frecuencia Relativa (%)",
  
  col = "lightgreen",
  
  las = 2,
  
  space = 0
)

5.5.Polígono de frecuencias (ni)

bp <- barplot(
  hi2,
  names.arg = paste(Li2, Ls2, sep = "-"),
  col = "lightgreen",
  border = "black",
  ylim = c(0, max(hi2)*1.2),
  main = "Gráfico N°7. Histograma de frecuencias relativas",
  xlab = "Intervalos",
  ylab = "Frecuencia relativa (%)",
  space = 0
)

# Polígono cerrado desde el límite inferior hasta el límite superior

x_pol <- c(
  bp[1] - 1,   # inicio del primer intervalo (-45)
  bp,            # centros de barras
  bp[length(bp)] + 0.5  # final del último intervalo (75)
)

y_pol <- c(
  0,
  hi2,
  0
)

lines(
  x_pol,
  y_pol,
  type = "o",
  col = "red",
  lwd = 2,
  pch = 16
)

grid()

5.6. Ojiva ascendente y descendente (ni)

plot(
  Ls2,
  Ni_asc2,
  
  type = "o",
  
  pch = 16,
  
  lwd = 2,
  
  col = "blue",
  
  ylim = c(0, max(Ni_asc2)),
  
  main = "Gráfico N°9: Ojivas de Frecuencia Absoluta Acumulada",
  
  xlab = "Latitud",
  
  ylab = "Frecuencia Acumulada (Ni)"
)

lines(
  Li2,
  Ni_dsc2,
  
  type = "o",
  
  pch = 17,
  
  lwd = 2,
  
  col = "red"
)

legend(
  "right",
  
  legend = c(
    "Ojiva Ascendente",
    "Ojiva Descendente"
  ),
  
  col = c(
    "blue",
    "red"
  ),
  
  pch = c(
    16,
    17
  ),
  
  lwd = 2,
  
  bty = "n"
)

grid()

5.7.Ojiva ascentende y descendente (hi)

# Ascendente: inicia en 0
x_asc <- c(Li2[1], MC2)
y_asc <- c(0, Ni_asc2)

# Descendente: termina en 0
x_dsc <- c(MC2, Ls2[length(Ls2)])
y_dsc <- c(Ni_dsc2, 0)

# Crear gráfico vacío
plot(
  x_asc,
  y_asc,
  type = "o",
  pch = 16,
  col = "blue",
  lwd = 2,
  ylim = c(0, n_total),
  xlab = "Celsius",
  ylab = "Frecuencia Acumulada (Ni)",
  main = "Gráfico N°5: Ojivas Ascendente y Descendente"
)

# Agregar ojiva descendente
lines(
  x_dsc,
  y_dsc,
  type = "o",
  pch = 17,
  col = "red",
  lwd = 2
)

# Cuadrícula
grid()

# Leyenda
legend(
  "right",
  legend = c(
    "Ojiva Ascendente",
    "Ojiva Descendente"
  ),
  col = c("blue", "red"),
  pch = c(16, 17),
  lwd = 2,
  bty = "n"
)

5.8. Bloxplot

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
bp <- boxplot(
  Celsius,
  horizontal = TRUE,
  outline = FALSE,
  main = "Gráfico N°6: Boxplot de Celsius",
  xlab = "Celsius",
  col = "lightblue"
)

atipicos <- Celsius[
  Celsius < Lim_inf |
    Celsius > Lim_sup
]

points(
  atipicos,
  rep(1, length(atipicos)),
  col = "red",
  pch = 19,
  cex = 0.8
)

abline(v = Lim_inf, col = "blue", lwd = 2, lty = 2)
abline(v = Lim_sup, col = "blue", lwd = 2, lty = 2)

#Outliers

Lim_inf2 <- Q1 - 1.0 * RIC
Lim_sup2 <- Q3 + 1.0 * RIC

atipicos2 <- Celsius[
  Celsius < Lim_inf2 |
    Celsius > Lim_sup2
]

length(atipicos2)
## [1] 8649
legend(
  "topright",
  legend = c(
    "Atípicos",
    "Límites de Tukey"
  ),
  col = c("red", "blue"),
  pch = c(19, NA),
  lty = c(NA, 2),
  lwd = c(NA, 2),
  bty = "n"
)

grid()

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
tabla_indicadores

7. Conclusiones

El comportamiento de la variable Celsius sigue la siguiente manera: fluctúa entre -41.30° y 64.15° y sus valores giran en torno a la media aritmética de 19.22°, con una desviación estándar de 24.41°, siendo un conjunto de datos muy heterogéneo, ya que su coeficiente de variación es de 127.03%. El conjunto de valores presenta una distribución platicúrtica (Curtosis = -0.74), lo que indica una menor concentración de datos alrededor del centro en comparación con una distribución normal. Además, muestra una ligera asimetría negativa (Asimetría = -0.30), por lo que los valores tienden a concentrarse hacia la parte alta de la distribución, con una cola más extendida hacia los valores bajos. Finalmente, no se identificaron valores atípicos (N = 0), lo que evidencia que los datos se encuentran dentro de los límites esperados. Por todo lo anterior, la variable Latitud presenta una amplia dispersión geográfica de las ubicaciones registradas, reflejando la diversidad de regiones incluidas en el conjunto de datos meteorológicos analizado.