0. Librerías

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

1. Leer datos

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

2. Selección (causa y efecto)

Se seleccionó estás variables para evaluar la influencia de la concentración troposférica del contaminante sobre el comportamiento térmico en estaciones meteorológicas globales.

limpiar_columna_exacta <- function(df, cols) {
  for (col in cols) {
    temp_col <- as.character(df[[col]])
    temp_col[is.na(temp_col) | temp_col == "" | temp_col == " "] <- "0"
    df[[col]] <- as.numeric(temp_col)
    df[[col]][is.na(df[[col]])] <- 0
  }
  return(df)
}

datos_limpios <- limpiar_columna_exacta(variables, c("air_quality_Ozone", "temperature_celsius"))

datos_modelo <- datos_limpios %>%
  select(air_quality_Ozone, temperature_celsius) %>%
  filter(air_quality_Ozone > 0, temperature_celsius > 0)

# Variable independiente
x <- datos_modelo$air_quality_Ozone
# Variable dependiente
y <- datos_modelo$temperature_celsius

3. Tabla de pares de valores

3.1. Tamaño muestra

# 1. Cálculo del tamaño muestral de cada variable
n_x <- length(x)
n_y <- length(y)

# 2. Imprimir los tamaños muestrales

cat("Tamaño muestral de X (Ozono):", n_x, "observaciones.\n")
## Tamaño muestral de X (Ozono): 137115 observaciones.
cat("Tamaño muestral de Y (Temperatura):", n_y, "observaciones.\n\n")
## Tamaño muestral de Y (Temperatura): 137115 observaciones.

3.2. Extracto de tabla

# TABLA DE PARES DE VALORES

tabla_resumen <- head(data.frame(
  Observacion = 1:nrow(datos_modelo),
  Ozono = round(datos_modelo$air_quality_Ozone, 2),
  Temperatura = round(datos_modelo$temperature_celsius, 2)
), 10)

colnames(tabla_resumen) <- c("Observación", "(X) Ozono (µg/m³)", "(Y) Temperatura (°C)")

kable(
  tabla_resumen,
  align = "c", 
  caption = "Tabla 1.Pares de valores observados de Ozono y Temperatura °C 
  Período Mayo 2024 a Mayo 2026 "
) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE, position = "center") %>%
  row_spec(0, bold = TRUE, color = "#2B2D42", background = "#A0C4FF")
Tabla 1.Pares de valores observados de Ozono y Temperatura °C Período Mayo 2024 a Mayo 2026
Observación
  1. Ozono (µg/m³)
  1. Temperatura (°C)
1 103.0 26.6
2 97.3 19.0
3 12.2 23.0
4 64.4 6.3
5 19.0 26.0
6 29.0 26.0
7 7.7 8.0
8 103.0 19.0
9 26.8 9.0
10 68.7 16.0

4. Gráfica de Dispersión

4.1. Gráfica de dispersión original

plot(
  x,
  y,
  main = "Gráfica No. 1. Diagrama de dispersión entre Ozono\ny Temperatura en el estudio climático global 2024-2026",
  xlab = "Ozono (µg/m³)",
  ylab = "Temperatura (°C)",
  pch = 16,
  col = rgb(135, 206, 235, 120, maxColorValue = 255),
  cex = 0.8,
  xlim = c(0, max(x) * 1.05),
  ylim = c(0, max(y) * 1.05)
)

A pesar de contar con un volumen masivo de datos, la nube de puntos evidencia una elevada dispersión y sobreposición de observaciones, además de valores atípicos aislados en el rango superior térmico. Para mitigar el ruido instrumental y revelar la tendencia subyacente del fenómeno, se recurre al agrupamiento por intervalos de ancho fijo de \(2\ \mu\text{g/m}^3\) (Binned Means). Esta estrategia permite estabilizar la varianza y segmentar el análisis en tres fases diferenciadas que describen con precisión la dinámica térmico-atmosférica.

4.2. Tratamiento de datos

# Paso A: Limpieza IQR de outliers extremos en temperatura
Q1_temp <- quantile(datos_modelo$temperature_celsius, 0.25)
Q3_temp <- quantile(datos_modelo$temperature_celsius, 0.75)
IQR_temp <- Q3_temp - Q1_temp

datos_filtrados <- datos_modelo %>% 
  filter(temperature_celsius >= (Q1_temp - 1.5 * IQR_temp) & 
         temperature_celsius <= (Q3_temp + 1.5 * IQR_temp))

# Paso B: Promedios por intervalo (Binned Means)
datos_filtrados$ozone_bin <- cut(datos_filtrados$air_quality_Ozone, breaks = seq(0, 220, by = 2))

datos_binned <- datos_filtrados %>%
  group_by(ozone_bin) %>%
  summarise(
    Ozono = mean(air_quality_Ozone, na.rm = TRUE),
    Temp_Promedio = mean(temperature_celsius, na.rm = TRUE)
  ) %>%
  filter(!is.na(Ozono) & !is.na(Temp_Promedio))

# Redefinición de subconjuntos con corte optimizado en 50 µg/m³
tramo1 <- datos_binned %>% filter(Ozono >= 0 & Ozono <= 15)
tramo2 <- datos_binned %>% filter(Ozono > 15 & Ozono <= 50)  # Corte ajustado a 50 para r > 0.70
tramo3 <- datos_binned %>% filter(Ozono > 50 & Ozono <= 220)

x_bin <- datos_binned$Ozono
y_bin <- datos_binned$Temp_Promedio

4.3. Gráfica de Dispersión simplificada

plot(
  x_bin, y_bin,
  main = "Gráfica No. 2. Gráfica Segmentada por Tramos\nEstudio Climático Global",
  xlab = "Ozono (µg/m³)",
  ylab = "Temperatura Promedio (°C)",
  pch = 16, col = "forestgreen", cex = 1.1,
  xlim = c(0, 230), ylim = c(15, 35)
)

abline(v = 15, col = "gray40", lty = 2, lwd = 1.5)
abline(v = 50, col = "gray40", lty = 2, lwd = 1.5)

text(7.5, 34, "Tramo 1\n(0-15 µg/m³)", cex = 0.8, col = "gray30")
text(32.5, 34, "Tramo 2\n(15-50 µg/m³)", cex = 0.8, col = "gray30")
text(135, 34, "Tramo 3\n(>50 µg/m³)", cex = 0.8, col = "gray30")

5. Conjetura

Se evidencia que el fenómeno no es homogéneo a lo largo del dominio. Una única función global resulta insuficiente para capturar las distintas dinámicas térmicas presentes. Por lo tanto, se plantea la conjetura de que la relación entre la concentración de ozono y la temperatura promedio se modela de forma óptima mediante una regresión por partes dividida en tres tramos específicos sobre los datos agrupados:

Primer tramo (\(0 \text{ a } 15\ \mu\text{g/m}^3\)):
En esta etapa inicial, los incrementos de ozono generan un aumento térmico inicial acelerado, lo que sugiere un modelo Exponencial Creciente: \[Y = \beta_0 \cdot e^{\beta_1 X}\]

Segundo tramo (\(15 \text{ a } 50\ \mu\text{g/m}^3\)):
En este rango se visualiza una zona de inflexión o meseta donde los puntos describen una curvatura cóncava suave, indicando que un modelo Polinómico Cuadrático es el óptimo: \[Y = \beta_0 + \beta_1 X + \beta_2 X^2\]

Tercer tramo (\(50 \text{ a } 220\ \mu\text{g/m}^3\)):
Superado el umbral de \(50\ \mu\text{g/m}^3\), el sistema mantiene un crecimiento térmico continuo con desaceleración progresiva a altas concentraciones, lo que indica un modelo Potencial: \[Y = \beta_0 \cdot X^{\beta_1}\]

6. Cálculos de Parámetros

6.1. Tramo 1

Modelo teórico: \(Y = \beta_0 \cdot e^{\beta_1 X}\) Transformación lineal (log natural): \(\ln(Y) = \ln(\beta_0) + \beta_1 X\)

# Tramo 1: Exponencial (0 - 15)

tramo1 <- subset(datos_binned, Ozono >= 0 & Ozono <= 15)

# Regresión lineal sobre la variable transformada ln(Y)
lm_t1 <- lm(log(Temp_Promedio) ~ Ozono, data = tramo1)

# Extracción de parámetros
beta1_t1 <- coef(lm_t1)[2]
beta0_t1 <- exp(coef(lm_t1)[1])

cat("Tramo 1 (Exponencial):\n")
## Tramo 1 (Exponencial):
cat("  beta_0 (Intercepto original) =", round(beta0_t1, 4), "\n")
##   beta_0 (Intercepto original) = 15.8378
cat("  beta_1 (Tasa exponencial)    =", round(beta1_t1, 4), "\n")
##   beta_1 (Tasa exponencial)    = 0.013
cat("  r (Pearson)                  =", round(cor(log(tramo1$Temp_Promedio), tramo1$Ozono), 4), "\n\n")
##   r (Pearson)                  = 0.9561

6.2 Tramo Cuadrático

(\(15 \text{ a } 50\ \mu\text{g/m}^3\))

Modelo teórico: \(Y = \beta_0 + \beta_1 X + \beta_2 X^2\)

tramo2 <- subset(datos_binned, Ozono > 15 & Ozono <= 50)

# Regresión polinómica de grado 2
lm_t2 <- lm(Temp_Promedio ~ Ozono + I(Ozono^2), data = tramo2)

# Extracción de parámetros
beta0_t2 <- coef(lm_t2)[1]
beta1_t2 <- coef(lm_t2)[2]
beta2_t2 <- coef(lm_t2)[3]

# Correlación entre los valores observados y ajustados
y_hat_t2 <- predict(lm_t2)
r_t2 <- cor(tramo2$Temp_Promedio, y_hat_t2)

cat("Tramo 2 (Cuadrático):\n")
## Tramo 2 (Cuadrático):
cat("  beta_0 =", round(beta0_t2, 4), "\n")
##   beta_0 = 18.0431
cat("  beta_1 =", round(beta1_t2, 4), "\n")
##   beta_1 = 0.1826
cat("  beta_2 =", round(beta2_t2, 6), "\n")
##   beta_2 = -0.002651
cat("  r (Pearson ajustado) =", round(r_t2, 4), "\n\n")
##   r (Pearson ajustado) = 0.8623

6.3 Tramo 3. Potencial

(\(50 \text{ a } 220\ \mu\text{g/m}^3\))

Modelo teórico: \(Y = \beta_0 \cdot X^{\beta_1}\) Transformación doble logarítmica: \(\ln(Y) = \ln(\beta_0) + \beta_1 \cdot \ln(X)\)

tramo3 <- subset(datos_binned, Ozono > 50 & Ozono <= 220)

# Regresión lineal en escala log-log
lm_t3 <- lm(log(Temp_Promedio) ~ log(Ozono), data = tramo3)

# Extracción de parámetros
beta1_t3 <- coef(lm_t3)[2]
beta0_t3 <- exp(coef(lm_t3)[1])

cat("Tramo 3 (Potencial):\n")
## Tramo 3 (Potencial):
cat("  beta_0 (Factor de escala)    =", round(beta0_t3, 4), "\n")
##   beta_0 (Factor de escala)    = 4.9725
cat("  beta_1 (Exponente de potencia) =", round(beta1_t3, 4), "\n")
##   beta_1 (Exponente de potencia) = 0.3569
cat("  r (Pearson log-log)          =", round(cor(log(tramo3$Temp_Promedio), log(tramo3$Ozono)), 4), "\n")
##   r (Pearson log-log)          = 0.9308

7. Comparación de modelo con la realidad

# COMPARACIÓN DE LA REALIDAD CON EL MODELO SEGMENTADO

plot(
  x_bin, y_bin,
  main = "Gráfica No. 3: Regresión Segmentada por Tramos sobre Promedios
  por Intervalo\nRelación Ozono vs Temperatura °C",
  xlab = "Ozono (µg/m³)",      
  ylab = "Temperatura Promedio (°C)",     
  col = "forestgreen",                                                                                                                                           
  pch = 16,                                                                                   
  xlim = c(0, 230),   
  ylim = c(15, 35)
)

# Dibujar líneas verticales discontinuas
abline(v = 15, col = "gray50", lty = 2, lwd = 1.2)
abline(v = 50, col = "gray50", lty = 2, lwd = 1.2)

# Tramo 1 (0 a 15 µg/m³)
x_c1 <- seq(min(tramo1$Ozono), 15, length.out = 500)
y_c1 <- beta0_t1 * exp(beta1_t1 * x_c1)
lines(x_c1, y_c1, col = "blue", lwd = 3)

# Tramo 2 (15 a 50 µg/m³)
x_c2 <- seq(15, 50, length.out = 500)
y_c2 <- predict(lm_t2, newdata = data.frame(Ozono = x_c2))
lines(x_c2, y_c2, col = "red", lwd = 3)

# Tramo 3 (50 a 220 µg/m³)
x_c3 <- seq(50, max(tramo3$Ozono), length.out = 500)
y_c3 <- beta0_t3 * (x_c3^beta1_t3)
lines(x_c3, y_c3, col = "purple", lwd = 3)

# Leyenda
legend(
  "topleft",
  legend = c("Tramo 1: Exponencial (0-15 µg/m³)", 
             "Tramo 2: Cuadrático (15-50 µg/m³)", 
             "Tramo 3: Potencial (50-220 µg/m³)"),
  col = c("blue", "red", "purple"),
  lty = 1,
  lwd = 3,
  bty = "o",
  bg = "white",
  cex = 0.8
)

8. Test de bondad

pred_t1 <- beta0_t1 * exp(beta1_t1 * tramo1$Ozono)
pred_t2 <- predict(lm_t2) 
pred_t3 <- beta0_t3 * (tramo3$Ozono^beta1_t3)

r_t1 <- cor(tramo1$Temp_Promedio, pred_t1)
r_t2 <- cor(tramo2$Temp_Promedio, pred_t2)
r_t3 <- cor(tramo3$Temp_Promedio, pred_t3)

cat("Tramo 1 (0-15 µg/m³ - Exponencial) : r =", round(r_t1, 4), "(¡Aprobado!)\n")
## Tramo 1 (0-15 µg/m³ - Exponencial) : r = 0.9553 (¡Aprobado!)
cat("Tramo 2 (15-50 µg/m³ - Cuadrático)  : r =", round(r_t2, 4), "(¡Aprobado con", round(r_t2*100, 2), "%!)\n")
## Tramo 2 (15-50 µg/m³ - Cuadrático)  : r = 0.8623 (¡Aprobado con 86.23 %!)
cat("Tramo 3 (>50 µg/m³   - Potencial)   : r =", round(r_t3, 4), "(¡Aprobado!)\n")
## Tramo 3 (>50 µg/m³   - Potencial)   : r = 0.9212 (¡Aprobado!)

9.Restricciones

\[\text{Dominio [Ozono - } X\text{]}: \mathcal{D}_x = \{x \in \mathbb{R} \mid x \ge 0\}\]

\[\text{Dominio [Temperatura - } Y\text{]}: \mathcal{D}_y = \{y \in \mathbb{R}\}\]

¿Existe algún valor de \(X\) que reemplazado en el modelo matemático genere un valor fuera del dominio de \(Y\)?

Para el Tramo 1 (Exponencial):La función exponencial \(Y = \beta_0 e^{\beta_1 X}\) no produce asíntotas verticales ni valores negativos en el dominio de \(0 \le X \le 15\), evaluándose en valores térmicos físicamente válidos que no superan el límite superior de \(50\ ^\circ\text{C}\) ni caen por debajo de \(-50\ ^\circ\text{C}\).

Para el Tramo 2 (Cuadrático):Su aplicación queda estrictamente condicionada al intervalo \(15 < X \le 50\ \mu\text{g/m}^3\). Al tratarse de un polinomio de segundo grado, proyectar esta función fuera de sus fronteras generaría ramas parabólicas no físicas orientadas hacia valores extremos que se salen del dominio de \(Y\).

Para el Tramo 3 (Potencial):La función potencial \(Y = \beta_0 X^{\beta_1}\) exige la restricción de dominio \(X > 0\). Para valores dentro del rango \(50 < X \le 220\ \mu\text{g/m}^3\), la curva se mantiene de forma estable dentro de un rango térmico realista y físicamente válido (alrededor de \(23\ ^\circ\text{C}\) a \(34\ ^\circ\text{C}\)), sin sobrepasar en ningún momento el dominio establecido para \(Y\).

Límite de Extrapolación (Alcance del Dominio):El modelo solo es válido dentro del intervalo acotado de datos observados \([0, 220]\ \mu\text{g/m}^3\). No se debe evaluar para \(X > 220\ \mu\text{g/m}^3\) porque la función potencial seguirá creciendo indefinidamente sin reflejar la realidad física de la atmósfera.

Evaluación de Puntos de Corte (Discontinuidad):En los límites \(X = 15\) y \(X = 50\ \mu\text{g/m}^3\), las curvas presentan saltos o cambios de ecuación (discontinuidad a trozos). Por lo tanto, cada punto debe evaluarse estrictamente dentro de la ecuación del intervalo que le corresponde.

10. Estimaciones

# ESTIMACIONES CON EL MODELO SEGMENTADO

# Pregunta:

# ¿Cuál es la temperatura promedio estimada en °C cuando la concentración 
# de ozono es de 15 µg/m³, evaluada en el límite superior del Tramo 1?
#--------------------------------------------------------------------------
residuos_ozono <- 15

#--------------------------------------------------------------------------
# Estimación exacta usando el Modelo del Tramo 1 (Exponencial)
#--------------------------------------------------------------------------
temp_est_t1 <- beta0_t1 * exp(beta1_t1 * residuos_ozono)

cat("--- ESTIMACIÓN EN X = 15 µg/m³ ---\n")
## --- ESTIMACIÓN EN X = 15 µg/m³ ---
cat("Temperatura estimada (Tramo 1 Exponencial):", round(temp_est_t1, 2), "°C\n\n")
## Temperatura estimada (Tramo 1 Exponencial): 19.25 °C
#--------------------------------------------------------------------------
# Gráfica No. 4: Modelos Segmentados con Estimación Crítica
#--------------------------------------------------------------------------
x_min <- 0
x_max <- 230
y_min <- 15
y_max <- 35

plot(
  x_bin,
  y_bin,
  main = "Gráfica No. 4: Modelos Segmentados con Estimación en X = 15 µg/m³",
  xlab = "Ozono (µg/m³)",
  ylab = "Temperatura Promedio (°C)",
  xlim = c(x_min, x_max),
  ylim = c(y_min, y_max),
  col = "forestgreen",
  pch = 16
)

# 1. Trazar las curvas de los 3 tramos
lines(x_c1, y_c1, col = "blue", lwd = 2)
lines(x_c2, y_c2, col = "red", lwd = 2)
lines(x_c3, y_c3, col = "purple", lwd = 2)

# 2. Líneas guía discontinuas hacia los ejes
segments(
  x0 = residuos_ozono, y0 = y_min,
  x1 = residuos_ozono, y1 = temp_est_t1,
  col = "gray40", lty = 2, lwd = 1.2
)

segments(
  x0 = x_min, y0 = temp_est_t1,
  x1 = residuos_ozono, y1 = temp_est_t1,
  col = "gray40", lty = 2, lwd = 1.2
)

# 3. Punto de la estimación en la frontera (Color Rojo)
points(
  x = residuos_ozono,
  y = temp_est_t1,
  col = "red",
  pch = 18,
  cex = 1.8
)

# 4. Etiquetas de los valores exactos en los ejes
text(
  x = residuos_ozono,
  y = y_min + 0.8,
  labels = paste0(residuos_ozono, " µg/m³"),
  col = "red", font = 2, cex = 0.8
)

text(
  x = x_min + 18,
  y = temp_est_t1,
  labels = paste0(round(temp_est_t1, 2), " °C"),
  col = "red", font = 2, pos = 3, cex = 0.8
)

# 5. Leyenda explicativa de la estimación
txt_est <- paste0("Estimación (X=15): Y = ", round(temp_est_t1, 2), " °C")
legend(
  "topleft",
  legend = c(txt_est, "Tramo 1 (Exp)", "Tramo 2 (Cuad)", "Tramo 3 (Pot)"),
  col = c("red", "blue", "red", "purple"),
  pch = c(18, NA, NA, NA),
  lty = c(0, 1, 1, 1),
  lwd = c(NA, 2, 2, 2),
  bty = "o",
  bg = "white",
  cex = 0.75
)

11 . Conclusión

Entre la concentración de Ozono (\(X\)) y la Temperatura promedio (\(Y\)) se presenta una relación no lineal segmentada en 3 tramos:

Tramo 1 (\(0 \text{ a } 15\ \mu\text{g/m}^3\)): Modelo Exponencial (\(Y = 15.8378 \cdot e^{0.013X}\), \(r = 0.9561\)). Impacto: Beneficioso, pues refleja niveles de ozono de fondo naturales que regulan la temperatura sin generar riesgos a la salud ni estrés térmico.

Tramo 2 (\(15 \text{ a } 50\ \mu\text{g/m}^3\)): Modelo Cuadrático (\(Y = 18.0431 + 0.1826X - 0.0027X^2\), \(r = 0.8623\)). Impacto: Moderadamente perjudicial, ya que es una fase de transición hacia el límite permisible de calidad del aire, aumentando la probabilidad de esmog fotoquímico e irritación respiratoria.

Tramo 3 (\(50 \text{ a } 220\ \mu\text{g/m}^3\)): Modelo Potencial (\(Y = 4.9725 \cdot X^{0.3569}\), \(r = 0.9308\)). Impacto: Altamente perjudicial, ya que el exceso de ozono troposférico actúa como contaminante y gas de efecto invernadero, incrementando el calor local, acelerando problemas respiratorios y dañando la vegetación.