0.- Librerias

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

1.- leer datos

setwd("C:/Users/CESAR/Downloads/proyecto x")
variables <- read_csv("GlobalweatherRepository.csv", show_col_types = TRUE)

2.- Depuración y selección de la variable

Depuración

variables <- na.omit(variables)

Selección

O_3<- variables$air_quality_Ozone

3.- TDF simplificada de la variable concentracion de Ozono

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

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

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

4.- Histograma de la variable

# Creamos el vector completo de cortes (incluyendo el último límite superior)
todos_los_cortes <- c(Li2, Ls2[length(Ls2)])

# Generamos el histograma usando esos cortes exactos
hist(O_3, 
     breaks = todos_los_cortes, 
     main = "Gráfico Nro 1: Histograma de Cantidad de Ozono (O3) para el 
     análisis meteorológico en capitales del mundo
  Periodo: Mayo 2024 a Mayo 2026", 
     xlab = "Concentración de Ozono (O3)", 
     ylab = "Frecuencia absoluta (ni)", 
     col = "#FADBD8", 
     border = "white")

# Añadimos la rejilla para facilitar la lectura
grid()

5.- Tratamiento de datos

para poder entender escojer un modelo que se acople de mejor manera al comportamiento de los datos procedemos a tratar datos

Estragia: Modificamos la amplitud de la tabla simplificada para asi optener un histograma con barras muy caracteristicas del comportamiento de esta variable

Tabla modificada

# 1. Ajuste manual de la amplitud
amplitud_deseada <- 20  # Aquí defines el ancho de cada barra

# 2. Recalcular los límites usando esta nueva amplitud
# floor(valor_min) es el inicio, y creamos cortes hasta cubrir el valor_max
cortes_personalizados <- seq(floor(valor_min), ceiling(valor_max) + amplitud_deseada, by = amplitud_deseada)

# 3. Ajustar los vectores Li2 y Ls2 a esta nueva estructura
Li2 <- cortes_personalizados[-length(cortes_personalizados)]
Ls2 <- cortes_personalizados[-1]

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

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 20 10 11325 7.99 11325 7.99 141703 100
20 40 30 27353 19.3 38678 27.3 130378 92.01
40 60 50 42285 29.84 80963 57.14 103025 72.7
60 80 70 33397 23.57 114360 80.7 60740 42.86
80 100 90 16052 11.33 130412 92.03 27343 19.3
100 120 110 6393 4.51 136805 96.54 11291 7.97
120 140 130 2705 1.91 139510 98.45 4898 3.46
140 160 150 1160 0.82 140670 99.27 2193 1.55
160 180 170 513 0.36 141183 99.63 1033 0.73
180 200 190 217 0.15 141400 99.79 520 0.37
200 220 210 117 0.08 141517 99.87 303 0.21
220 240 230 75 0.05 141592 99.92 186 0.13
240 260 250 34 0.02 141626 99.95 111 0.08
260 280 270 20 0.01 141646 99.96 77 0.05
280 300 290 22 0.02 141668 99.98 57 0.04
300 320 310 8 0.01 141676 99.98 35 0.02
320 340 330 9 0.01 141685 99.99 27 0.02
340 360 350 7 0 141692 99.99 18 0.01
360 380 370 1 0 141693 99.99 11 0.01
380 400 390 3 0 141696 100 10 0.01
400 420 410 1 0 141697 100 7 0
420 440 430 2 0 141699 100 6 0
440 460 450 3 0 141702 100 4 0
460 480 470 0 0 141702 100 1 0
480 500 490 1 0 141703 100 1 0
TOTAL 141703 100
Nota: Elaborado por Grupo 2.
Fuente: Global Weather Repository.

Histograma modificada la amplitud

# Creamos el vector completo de cortes (incluyendo el último límite superior)
todos_los_cortes <- c(Li2, Ls2[length(Ls2)])

# Generamos el histograma usando esos cortes exactos
hist(O_3, 
     breaks = todos_los_cortes, 
     main = "Gráfico Nro 1: Histograma de Cantidad de Ozono (O3)\nPeríodo 2024-2026", 
     xlab = "Concentración de Ozono (O3)", 
     ylab = "Frecuencia absoluta (ni)", 
     col = "#FADBD8", 
     border = "white")

# Añadimos la rejilla para facilitar la lectura
grid()

6.- Conjetura

Se evidencia un comportamiento a un modelo log normal muy caracteristico de esta variable ya que la mayor concentración de los datos se observa en el lado izquierdo y conforme avanza va decreciendo asi que cálculamos sus parametros

7.- Párametos

mu_log <- mean(log(O_3[O_3 > 0]), na.rm = TRUE)
mu_log
## [1] 3.886009
sigma_log <- sd(log(O_3[O_3 > 0]), na.rm = TRUE)
sigma_log
## [1] 0.7121347

8.- Sobreponer la ralidad con el modelo

hist(O_3, 
     breaks = 20, # Ajusta según necesites para ver mejor la forma
     prob = TRUE, 
     main = "Gráfico Nro 2: Histograma y Modelo Log-normal (O3) para el análisis
  meteorológico en capitales del mundo
  Periodo: Mayo 2024 a Mayo 2026", 
     xlab = "Concentración de Ozono (O3)", 
     ylab = "Densidad de probabilidades", 
     col = "#FADBD8", 
     border = "white")

# Añadir la curva teórica log-normal
curve(dlnorm(x, meanlog = mu_log, sdlog = sigma_log), 
      add = TRUE, 
      col = "blue", 
      lwd = 2)

grid()

9.- Test de bondad

test de pearson

# 1. Frecuencias observadas
Fo <- ni2

# 2. Probabilidades teóricas por intervalo (Modelo Log-normal)
h <- length(Fo)
P <- numeric(h)
for(i in 1:h){
    # Usamos plnorm para ajustar al modelo log-normal de ozono
    P[i] <- plnorm(Ls2[i], mu_log, sigma_log) - 
            plnorm(Li2[i], mu_log, sigma_log)
}

# 3. Frecuencias esperadas
Fe <- P * sum(ni2)

# 4. Correlación de Pearson entre frecuencias
# Compara qué tan bien sigue la forma observada a la teórica
Correlacion <- cor(Fo, Fe) * 100

# Resultado
cat("Correlación de Pearson:", round(Correlacion, 2), "%", "\n")
## Correlación de Pearson: 92.08 %

Test de chi cuadrado

# 1. Definir Frecuencias Relativas (Porcentaje)
Fo_pct <- hi2
Fe_pct <- P * 100

# 2. Calcular estadístico Chi-cuadrado
# Sumamos las diferencias al cuadrado divididas por lo esperado
X2_pct <- sum((Fo_pct - Fe_pct)^2 / Fe_pct)

# 3. Grados de libertad y Valor crítico
gl <- length(Fo_pct) - 1
X2_critico <- qchisq(0.95, gl)

# 4. Resultados
cat("--- RESULTADOS DEL TEST CHI-CUADRADO (Porcentajes) ---\n")
## --- RESULTADOS DEL TEST CHI-CUADRADO (Porcentajes) ---
cat("Chi-cuadrado Calculado:", round(X2_pct, 4), "\n")
## Chi-cuadrado Calculado: 18.0595
cat("Chi-cuadrado Crítico:", round(X2_critico, 4), "\n")
## Chi-cuadrado Crítico: 36.415
# 5. Decisión
if (X2_pct < X2_critico) {
  cat("Resultado: EL MODELO LOG-NORMAL ES ADECUADO.\n")
} else {
  cat("Resultado: SE RECHAZA EL MODELO LOG-NORMAL.\n")
}
## Resultado: EL MODELO LOG-NORMAL ES ADECUADO.

10.- Cálculo de probabilidades

Cúal es la probabilidad de que la concentración de ozono esté entre 50 y 100 µg/m³

p_rango <- (plnorm(100, mu_log, sigma_log) - plnorm(50, mu_log, sigma_log)) * 100

p_rango
## [1] 32.91505

¿Cuál es la probabilidad de que la concentración de ozono no supere los 25 µg/m³ en un día?

prob <- plnorm(25, mu_log, sigma_log)
prob_porcentaje <- prob * 100
prob_porcentaje
## [1] 17.44288

¿Cúal es la probabilidad de que en un mes tengan la concentracion de Ozono no supere los 25 µg/m³?

Dias_esperados_mes<- ((prob_porcentaje/ 100) * 30)

Dias_esperados_mes <- round(Dias_esperados_mes, 0)
Dias_esperados_mes
## [1] 5

Gráfica de probabilidades

# 1. Ajuste de la gráfica base (mantenemos tu configuración)
curve(
  dlnorm(x, meanlog = mu_log, sdlog = sigma_log),
  from = 0, to = 500,               
  col = "darkblue", lwd = 3,       
  main = "Gráfico Nro 3 :Modelo Log-normal: Concentración de Ozono para el análisis
  meteorológico en capitales del mundo
  Periodo: Mayo 2024 a Mayo 2026",
  xlab = "Ozono (µg/m³)", 
  ylab = "Densidad de probabilidad",
  cex.main = 0.9,
  ylim = c(0, 0.015)                
)

# 2. Sombreado para el área de 0 a 25 (Nueva estimación)
lim_25 <- 25
x_sombra_25 <- seq(0, lim_25, length = 100) 
y_sombra_25 <- dlnorm(x_sombra_25, meanlog = mu_log, sdlog = sigma_log)

polygon(
  c(0, x_sombra_25, lim_25), 
  c(0, y_sombra_25, 0), 
  col = rgb(0.2, 0.6, 0.8, 0.4), # Azul claro para diferenciar
  border = NA
)
abline(v = lim_25, col = "blue", lwd = 1.5, lty = 2)

# 3. Sombreado para el área de 50 a 100 (Tu estimación anterior)
lim_inf <- 50
lim_sup <- 100
x_sombra <- seq(lim_inf, lim_sup, length = 200) 
y_sombra <- dlnorm(x_sombra, meanlog = mu_log, sdlog = sigma_log)

polygon(
  c(lim_inf, x_sombra, lim_sup), 
  c(0, y_sombra, 0), 
  col = rgb(1, 0.5, 0, 0.4),       
  border = NA
)
abline(v = lim_inf, col = "darkorange", lwd = 1.5, lty = 2)
abline(v = lim_sup, col = "darkorange", lwd = 1.5, lty = 2)

# 4. Leyenda actualizada con ambas áreas
legend(
  "topright", 
  legend = c("Modelo Log-normal", "< 25 µg/m³", "50 - 100 µg/m³"),
  col = c("darkblue", rgb(0.2, 0.6, 0.8, 0.4), rgb(1, 0.5, 0, 0.4)), 
  lwd = c(3, NA, NA),                   
  fill = c(NA, rgb(0.2, 0.6, 0.8, 0.4), rgb(1, 0.5, 0, 0.4)), 
  border = c(NA, "blue", "darkorange"),    
  bty = "o", bg = "white", cex = 0.8
)

11.- Intervalos de confianza

  # 1. Parámetros del modelo log-normal (usando los datos de Ozono)
  # Filtramos O_3 > 0 para evitar errores logarítmicos
  datos_log <- log(O_3[O_3 > 0])
  media_log <- mean(datos_log)
  sigma_log <- sd(datos_log)
  n_ozono <- length(datos_log)
  
  # 2. Error estándar y límites en escala logarítmica (95% confianza)
  error_log <- 1.96 * (sigma_log / sqrt(n_ozono))
  limite_inf_log <- media_log - error_log
  limite_sup_log <- media_log + error_log
  
  # 3. Revertir a la escala original (µg/m³) usando exponencial
  limite_inf_ozono <- round(exp(limite_inf_log), 2)
  limite_sup_ozono <- round(exp(limite_sup_log), 2)
  
  # 4. Crear tabla del intervalo
  IC_Ozono <- data.frame(
    "Nivel de confianza" = "95%",
    "Intervalo de confianza de µ (µg/m³)" = paste0(
      limite_inf_ozono, " < µ < ", limite_sup_ozono
    ),
    "Desviación estándar (µg/m³)" = round(sd(O_3, na.rm = TRUE), 2)
  )
  
  # 5. Mostrar tabla con el estilo solicitado
  kable(
    IC_Ozono,
    align = "c",
    caption = "Tabla N°3: Intervalo de confianza de la concentración de Ozono (O3) de los registros meteorológicos mundiales con un nivel de confianza del 95%, período 2024–2026"
  ) |>
    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(1, bold = TRUE, background = "#EAEDED") |>
    footnote(
      general = "Elaborado por Grupo 2. Fuente: Global Weather Repository.",
      general_title = "Nota: ",
      footnote_as_chunk = TRUE
    )
Tabla N°3: Intervalo de confianza de la concentración de Ozono (O3) de los registros meteorológicos mundiales con un nivel de confianza del 95%, período 2024–2026
Nivel.de.confianza Intervalo.de.confianza.de.µ..µg.m.. Desviación.estándar..µg.m..
95% 48.54 < µ < 48.9 30.48
Nota: Elaborado por Grupo 2. Fuente: Global Weather Repository.

12.- Conclusiones

La variable concentración de ozono (\(O_3\)) en unidades de \(\mu g/m^3\) se explica de forma adecuada mediante un modelo de distribución log-normal, se puede afirmar con un 95% de confianza que la media verdadera de la concentración de ozono se encuentra entre 48.54 \(\mu g/m^3\) y 48.90 \(\mu g/m^3\), presentando una desviación estándar muestral de 30.48 \(\mu g/m^3\), lo que evidencia la variabilidad y el sesgo positivo característicos de los contaminantes atmosféricos registrados a nivel mundial.