0. Librerias

library(readr)
library(dplyr)
library(knitr)
library(kableExtra)
library(ggplot2)
library(e1071)

1. Leer Datos

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

2. Depuración y Selección de Variables

O_3 <- na.omit(variables$air_quality_Ozone)
n_total <- length(O_3)

3. Frecuencias

3.1 Max y Min

valor_min <- min(O_3, na.rm = TRUE)
valor_max <- max(O_3, na.rm = TRUE)
rango <- valor_max - valor_min

3.2 Regla de Sturges

K_sturges <- floor(1 + 3.322 * log10(n_total))
A_sturges <- rango / K_sturges

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(O_3 >= Li1[i] & O_3 <= Ls1[i], na.rm = TRUE)
  } else {
    ni1[i] <- sum(O_3 >= Li1[i] & O_3 < Ls1[i], na.rm = TRUE)
  }
}
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 para tabla ajustada

K_ajustado <- 9
amplitud <- ceiling(rango / K_ajustado) 

Li2 <- seq(floor(valor_min), by = amplitud, length.out = K_ajustado)
Ls2 <- Li2 + amplitud

ni2 <- numeric(length(Li2))
for(i in 1:length(Li2)){
  if(i == length(Li2)){
    ni2[i] <- sum(O_3 >= Li2[i] & O_3 <= Ls2[i], na.rm = TRUE)
  } else {
    ni2[i] <- sum(O_3 >= Li2[i] & O_3 < Ls2[i], na.rm = TRUE)
  }
}
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. Tablas de Distribución de Frecuencias

4.1. Tabla 1

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_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 del Ozono Atmosférico (O_3) del 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 = "black", background = "#FADBD8") |> # Rosa Pastel
  kableExtra::row_spec(nrow(Tabla_Sturges2), bold = TRUE, background = "#EAEDED") |>   
  footnote(general = "Elaborado por Grupo 2.\nFuente: Global Weather Repository.", general_title = "Nota:", footnote_as_chunk = TRUE, title_format = c("italic","bold"))
Tabla N°1: Distribución de frecuencias del Ozono Atmosférico (O_3) del período 2024–2026
Lim_inf Lim_sup MC ni hi Ni_asc Hi_asc Ni_dsc Hi_dsc
0 26.71 13.35 18347 12.95 18347 12.95 141703 100
26.71 53.41 40.06 49740 35.1 68087 48.05 123356 87.05
53.41 80.12 66.76 47384 33.44 115471 81.49 73616 51.95
80.12 106.82 93.47 18003 12.7 133474 94.19 26232 18.51
106.82 133.53 120.17 5436 3.84 138910 98.03 8229 5.81
133.53 160.23 146.88 1801 1.27 140711 99.3 2793 1.97
160.23 186.94 173.59 573 0.4 141284 99.7 992 0.7
186.94 213.64 200.29 215 0.15 141499 99.86 419 0.3
213.64 240.35 227 103 0.07 141602 99.93 204 0.14
240.35 267.06 253.7 35 0.02 141637 99.95 101 0.07
267.06 293.76 280.41 25 0.02 141662 99.97 66 0.05
293.76 320.47 307.11 16 0.01 141678 99.98 41 0.03
320.47 347.17 333.82 11 0.01 141689 99.99 25 0.02
347.17 373.88 360.52 4 0 141693 99.99 14 0.01
373.88 400.58 387.23 3 0 141696 100 10 0.01
400.58 427.29 413.94 2 0 141698 100 7 0
427.29 453.99 440.64 3 0 141701 100 5 0
453.99 480.7 467.35 2 0 141703 100 2 0
TOTAL 141703 100
Nota: Elaborado por Grupo 2.
Fuente: Global Weather Repository.

4.2 Tabla ajustada

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

kable(TDF_O_3_Final, align = "c",
      caption = "Tabla N°2: Distribución de frecuencias para Cantidad de Ozono (O3) de los registros meteorológicos,
      período 2024–2026") |>  
  kable_styling(full_width = TRUE, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) |>  
  row_spec(0, bold = TRUE, color = "black", background = "#E8DAEF") |> # Lila Pastel
  row_spec(nrow(TDF_O_3_Final), bold = TRUE, background = "#EAEDED") |>  
  footnote(general = "Elaborado por Grupo 2.\nFuente: Global Weather Repository.", general_title = "Nota: ", footnote_as_chunk = TRUE, title_format = c("italic","bold"))
Tabla N°2: Distribución de frecuencias para Cantidad de Ozono (O3) de los registros meteorológicos, período 2024–2026
Lim. Inf. Lim. Sup. MC ni hi (%) Ni Asc Hi Asc Ni Dsc Hi Dsc
0 54 27 68199 48.13 68199 48.13 141703 100
54 108 81 65651 46.33 133850 94.46 73504 51.87
108 162 135 6902 4.87 140752 99.33 7853 5.54
162 216 189 755 0.53 141507 99.86 951 0.67
216 270 243 131 0.09 141638 99.95 196 0.14
270 324 297 41 0.03 141679 99.98 65 0.05
324 378 351 14 0.01 141693 99.99 24 0.02
378 432 405 6 0 141699 100 10 0.01
432 486 459 4 0 141703 100 4 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(
  O_3,      
  main = "Gráfico Nro 1:Histograma y polígono de frecuencias absolutas de\n cantidad deOzono (O_3) mundial,
  período 2024 hasta 2026",     
  xlab = "Concentración de Ozono (O_3)", ylab = "Frecuencia",      
  col = "#FADBD8", border = "white"
)     
grid()

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

hist(
  O_3,      
  breaks = c(Li2, max(Ls2)), ylim = c(0, n_total),
  main = "Gráfico Nro 2: Histograma global de \nconcentración de Ozono (O_3), 
  período 2024–2026",      
  xlab = "Concentración de Ozono (O_3)", ylab = "Frecuencia",      
  col = "#E8DAEF", border = "white"
)
grid()

5.3. Histograma original (hi)

barplot(
  hi2, names.arg = paste(Li2, Ls2, sep = " a "),
  main = "Gráfico Nro 3: Histograma local de frecuencia relativa de Ozono (O_3)\nperíodo 2024-2026",
  xlab = "Intervalos de Ozono (O_3)", ylab = "Frecuencia Relativa (%)",         
  col = "#FCF3CF", border = "white", las = 2, cex.names = 0.7
)

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 Nro 4: Histograma global de frecuencia relativa de Ozono (O_3)\nperíodo 2024-2026",         
  xlab = "Intervalos de Ozono (O_3)", ylab = "Frecuencia Relativa (%)",         
  col = "#D5F5E3", border = "white", las = 2, cex.names = 0.7
)

## 5.5. Polígonos de frecuencias (hi)

bp_ni <- barplot(
  ni2, names.arg = paste(Li2, Ls2, sep = "-"),
  col = "#E8DAEF", border = "white", ylim = c(0, max(ni2)*1.2), 
  main = "Gráfico Nro 5A: Polígono de frecuencias de la\nconcentración de Ozono (O_3), período 2024–2026",
  xlab = "Intervalos", ylab = "Frecuencia absoluta (ni)", las = 2, cex.names = 0.7
)
x_pol_ni <- c(bp_ni[1] - (bp_ni[2]-bp_ni[1]), bp_ni, bp_ni[length(bp_ni)] + (bp_ni[2]-bp_ni[1]))
y_pol_ni <- c(0, ni2, 0)
lines(x_pol_ni, y_pol_ni, type = "o", col = "#9B59B6", lwd = 2, pch = 16)
grid()

5.6. Ojiva ascendente y descendente (ni)

plot(
  Ls2, Ni_asc2, type = "o", pch = 16, lwd = 2, col = "#5499C7", ylim = c(0, max(Ni_asc2)),
  main = "Gráfico Nro 6: Ojivas de Frecuencia Absoluta de la concentración de Ozono (O_3)\nperíodo 2024-2026",      
  xlab = "Ozono (O_3)", ylab = "Frecuencia Acumulada (Ni)"
)
lines(Li2, Ni_dsc2, type = "o", pch = 17, lwd = 2, col = "#EC7063")
legend("right", legend = c("Ojiva Ascendente", "Ojiva Descendente"), col = c("#5499C7", "#EC7063"), pch = c(16, 17), lwd = 2, bty = "n")
grid()

5.7. Ojiva ascendente y descendente (hi)

plot(
  Ls2, Hi_asc2, type = "o", pch = 16, col = "#5499C7", lwd = 2, ylim = c(0, 100),
  xlab = "Ozono (O_3)", ylab = "Frecuencia Relativa Acumulada (%)",      
  main = "Gráfico Nro 7: Ojivas de frecuencias relativas acumuladas\ndel Ozono (O_3), período 2024–2026"
)
lines(Li2, Hi_dsc2, type = "o", pch = 17, col = "#EC7063", lwd = 2)
legend("right", legend = c("Ojiva Ascendente", "Ojiva Descendente"), col = c("#5499C7", "#EC7063"), pch = c(16, 17), lwd = 2, bty = "n")
grid()

5.8. Boxplot

Q1 <- quantile(O_3, 0.25)
Q3 <- quantile(O_3, 0.75)
RIC <- Q3 - Q1
Lim_inf <- Q1 - 1.5 * RIC
Lim_sup <- Q3 + 1.5 * RIC
boxplot(
  O_3, horizontal = TRUE, outline = FALSE,         
  main = "Gráfico Nro 8: Boxplot de la concentración de Ozono (O_3)\ndel año 2024 hasta 2026",         
  xlab = "Ozono (O_3)", col = "#E5E7E9"
)
atipicos <- O_3[O_3 < Lim_inf | O_3 > Lim_sup]
if(length(atipicos) > 0) {
  set.seed(123)
  muestra_atipicos <- sample(atipicos, min(length(atipicos), 1000))
  points(muestra_atipicos, rep(1, length(muestra_atipicos)), col = "#E74C3C", pch = 19, cex = 0.5)
}
abline(v = Lim_inf, col = "#2980B9", lwd = 2, lty = 2)
abline(v = Lim_sup, col = "#2980B9", lwd = 2, lty = 2)
legend("topright", legend = c("Atípicos (Muestra)", "Límites de Tukey"), col = c("#E74C3C", "#2980B9"), 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(O_3)
mediana <- median(O_3)
max_ni <- max(TDF_O_3$ni)
moda <- TDF_O_3$MC[TDF_O_3$ni == max_ni]

6.2. Dispersión

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

6.3. Forma

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

6.4. Valores atípicos

n_atipicos <- length(atipicos)
rango_texto <- paste0("[", round(min(O_3),2), "; ", round(max(O_3),2), "]")

6.5. Tabla de indicadores

tabla_indicadores <- data.frame(
  Variable = "Ozono (O_3)", Rango = rango_texto, Media = round(media,2), Mediana = round(mediana,2), Moda = round(moda[1],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
)

kable(tabla_indicadores, align = "c", caption = "Tabla N°3: Indicadores estadísticos descriptivos de la variable Ozono (O_3), período 2024–2026") |>
  kable_styling(full_width = TRUE, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) |>
  row_spec(0, bold = TRUE, color = "black", background = "#FCF3CF") # Amarillo Pastel para variar
Tabla N°3: Indicadores estadísticos descriptivos de la variable Ozono (O_3), período 2024–2026
Variable Rango Media Mediana Moda Varianza Desv_Est CV Asimetria Curtosis N_Atipicos
Ozono (O_3) [0; 480.7] 57.68 55 27 928.9 30.48 52.84 1.24 5.49 3858

7. Conclusiones

“EL analisis de Cantidad de Ozono (O3), se observa que la calidad del aire suele mantenerse en rangos bastante estables y moderados la mayor parte del tiempo, situando su valor central (Mediana= 55),Sin embargo está aparente calma se ve alterada por un fuerte sesgo hacia valores altos con Asimetría = 1.24 y ,(CV = 52.84%). Esta variabilidad se explica por la aparición de 3,858, registros atípicos, los cuales reflejan momentos específicos donde el ozono se disparó debido a factores ambientales o picos de contaminación aislados durante el período 2024–2026.” )