0.- Librerías

library(readr)
library(dplyr)
library(knitr)
library(kableExtra)
library(ggplot2)
library(e1071)
library(tidyverse)
library(lubridate)
library(plotly)
library(scatterplot3d)

1.- leer datos

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

2.- Selección de variables (Causa y Efecto)

Causa y Efecto:

Cuando un motor quema combustible, no solo libera una cosa como el CO, libera una “mezcla” de estos contaminantes al mismo tiempo. Es decir, donde hay mucho tráfico, verás que los niveles de los tres suben simultáneamente.

y  <- as.numeric(variables$air_quality_Carbon_Monoxide)
x1 <- as.numeric(variables$air_quality_PM2.5)
x2 <- as.numeric(variables$air_quality_Nitrogen_dioxide)
TTP <- data.frame(y, x1, x2)
n <- nrow(TTP)
cat("Tamaño muestral del modelo = ", n)
## Tamaño muestral del modelo =  141703

3.- Tabla de tripleta de valores

Antes de realizar la tabla tripelta de valores, debemos comprobar que estas variables tengan el mismo tamaño muestral para no tener algún error a la hora de hacer la tabla y el gráfico

Depuración de datos

limpiar_columna <- function(df, cols) {
  for (col in cols) {
    df[[col]] <- as.numeric(as.character(df[[col]]))
  }
  return(df)
}

# Aplicar la función a tus variables climáticas
datos <- limpiar_columna(variables, c("air_quality_Carbon_Monoxide", "air_quality_PM2.5", "air_quality_Nitrogen_dioxide"))

# Filtrar filas completas (eliminar los NA)
datos_limpios <- datos %>% filter(!is.na(air_quality_Carbon_Monoxide) & !is.na(air_quality_PM2.5) & !is.na(air_quality_Nitrogen_dioxide))

# Asignar variables para el análisis
y  <- datos_limpios$air_quality_Carbon_Monoxide
x1 <- datos_limpios$air_quality_PM2.5
x2 <- datos_limpios$air_quality_Nitrogen_dioxide

TTP <- data.frame(y, x1, x2)
n <- nrow(TTP)
cat("Tamaño muestral del modelo = ", n)
## Tamaño muestral del modelo =  141703
# 1. Preparar los datos redondeados a 8 dígitos
# Creamos el data frame con tus variables limpias
tabla_tripleta <- head(data.frame(
  CO = datos_limpios$air_quality_Carbon_Monoxide,
  `PM 2.5` = datos_limpios$air_quality_PM2.5,
  NO2 = round(x2, 8),
  check.names = FALSE
), 300)

# 2. Generar la tabla con tu estilo personalizado
kable(
  tabla_tripleta,                   # Asegura que muestre los decimales
  align = "c",                
  booktabs = TRUE,
  caption = "Tabla N.1: Tripleta de valores: CO, PM 2.5 y NO2 para el análisis
  meteorológico en capitales del mundo
  Periodo: Mayo 2024 a Mayo 2026"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"), 
    full_width = FALSE,                                     
    position = "center"                                     
  ) %>%
  row_spec(
    0, 
    bold = TRUE, 
    color = "white",       
    background = "#1F4E79" 
  ) %>%
  footnote(
    general = "Fuente: Global Weather Repository.",
    general_title = "Nota: ", 
    footnote_as_chunk = TRUE
  ) %>%
  scroll_box(height = "400px", width = "100%")
Tabla N.1: Tripleta de valores: CO, PM 2.5 y NO2 para el análisis meteorológico en capitales del mundo Periodo: Mayo 2024 a Mayo 2026
CO PM 2.5 NO2
277.0 8.4 1.1
193.6 1.1 0.9
540.7 10.4 65.1
170.2 0.7 1.6
2964.0 183.4 72.7
220.3 1.2 0.2
270.4 4.0 8.3
186.9 0.8 1.0
277.0 3.7 15.1
220.3 3.7 5.1
227.0 1.9 7.1
330.5 12.2 1.2
397.2 39.0 18.5
520.7 49.7 8.7
257.0 1.9 1.0
201.9 1.6 1.4
277.0 14.7 22.3
307.1 7.7 0.3
587.5 8.2 4.5
280.4 9.3 0.2
212.0 4.6 2.1
178.6 2.7 1.1
433.9 16.4 7.3
168.6 0.5 0.2
323.8 2.2 1.8
233.7 3.5 4.5
474.0 5.4 1.7
687.6 33.3 2.1
212.0 0.5 0.0
181.9 6.1 0.6
1295.1 17.1 14.9
727.7 25.6 4.9
193.6 5.0 1.8
217.0 0.5 0.1
480.7 13.6 1.0
2723.7 211.1 41.8
1335.1 84.9 101.5
212.0 3.0 1.2
257.0 1.5 0.2
694.3 17.3 11.8
1161.6 21.7 10.9
227.0 6.6 8.8
317.1 4.7 5.9
195.3 7.7 0.4
205.3 3.4 2.9
694.3 17.3 11.8
230.3 5.8 4.8
267.0 9.5 2.0
203.6 0.9 0.5
387.2 7.0 14.4
2403.3 26.3 72.7
217.0 11.7 6.9
460.6 20.4 7.7
320.4 2.1 0.6
250.3 13.9 0.1
217.0 7.3 4.6
297.1 22.0 1.1
250.3 12.0 0.1
213.6 2.6 2.1
208.6 5.5 2.1
397.2 9.8 20.7
1094.8 41.0 15.6
195.3 12.9 0.9
208.6 2.0 1.7
223.6 6.6 4.0
407.2 19.2 4.5
230.3 8.7 12.5
243.7 2.4 0.8
2243.0 132.0 35.0
774.4 16.7 8.1
176.9 15.8 0.4
210.3 1.1 2.9
674.3 10.7 9.9
574.1 41.8 26.1
377.2 19.0 3.7
212.0 5.4 3.4
200.3 0.5 0.6
307.1 25.4 3.0
3471.4 196.1 52.1
1041.4 34.4 145.3
403.9 13.5 25.0
243.7 8.4 14.1
186.9 5.0 1.6
574.1 41.8 26.1
707.6 10.1 12.2
317.1 15.8 31.2
180.2 4.5 1.9
213.6 1.9 3.3
1682.3 58.0 4.4
217.0 2.5 0.0
547.4 31.9 76.1
203.6 3.1 0.5
397.2 10.0 2.2
260.4 6.7 4.7
198.6 5.8 0.9
487.3 29.8 4.5
300.4 1.7 0.7
487.3 2.8 0.7
203.6 4.9 10.0
208.6 1.6 1.4
188.6 2.1 17.8
327.1 2.6 0.8
243.7 1.1 0.4
2456.7 40.5 133.0
233.7 1.0 0.0
387.2 2.8 1.1
247.0 29.8 5.1
180.2 1.3 0.0
207.0 33.3 0.5
223.6 2.0 1.2
974.7 35.1 48.7
180.2 0.6 0.0
176.9 4.8 0.3
213.6 3.7 4.0
347.1 12.6 14.9
185.3 1.3 0.2
190.3 1.8 0.5
260.4 3.6 0.5
230.3 1.7 0.1
212.0 2.1 0.1
210.3 8.4 0.9
327.1 22.3 0.6
280.4 15.3 33.2
227.0 0.5 0.9
500.7 11.7 6.5
383.9 10.3 0.9
540.7 12.6 1.2
230.3 6.5 1.2
208.6 7.6 5.1
270.4 11.5 32.9
303.8 46.6 2.1
303.8 15.5 3.4
176.9 0.9 0.1
185.3 1.8 0.1
397.2 2.9 8.7
227.0 2.8 4.1
217.0 3.6 0.2
303.8 9.6 12.2
734.3 32.2 10.5
217.0 3.0 1.4
208.6 1.8 5.4
213.6 27.2 15.1
223.6 3.9 1.6
243.7 4.4 18.5
287.1 4.2 0.5
208.6 1.0 0.3
210.3 1.4 0.1
237.0 1.9 0.7
213.6 1.1 0.5
240.3 14.6 3.3
247.0 13.4 12.0
283.7 49.1 23.7
195.3 21.4 1.0
207.0 2.7 1.5
240.3 2.0 0.1
410.6 8.2 1.9
654.2 8.9 52.8
205.3 4.7 2.8
212.0 2.8 4.2
220.3 0.5 1.0
257.0 2.7 0.1
1134.9 78.1 49.4
360.5 18.7 32.6
170.2 38.7 0.2
186.9 0.5 2.4
667.6 4.0 20.1
263.7 28.0 1.6
188.6 1.1 0.1
253.7 4.2 10.9
213.6 2.4 4.9
183.6 5.1 4.0
233.7 5.5 0.4
233.7 1.9 0.2
3898.6 115.2 85.0
230.3 0.8 0.0
220.3 3.2 1.2
217.0 2.4 0.1
198.6 2.2 0.6
200.3 6.7 1.9
233.7 11.2 6.8
240.3 9.0 6.9
207.0 0.6 0.0
320.4 4.1 0.3
213.6 2.0 0.9
203.6 34.1 1.2
383.9 16.9 31.9
198.6 6.3 2.5
337.1 6.5 8.6
240.3 2.9 11.7
220.3 3.1 0.2
317.1 6.2 13.0
560.8 72.5 14.7
183.6 32.5 2.4
1108.2 10.7 2.7
747.7 67.2 8.3
1495.4 9.4 19.4
195.3 2.3 3.6
198.6 4.3 1.8
156.9 0.7 0.7
494.0 31.7 9.0
233.7 1.0 0.7
634.2 4.8 15.3
195.3 1.0 6.3
307.1 9.9 20.7
203.6 1.5 3.4
253.7 2.0 17.0
350.5 17.5 0.6
230.3 40.1 1.6
934.6 64.2 19.9
253.7 2.4 1.2
213.6 2.3 3.5
212.0 1.9 6.9
290.4 7.0 0.4
387.2 2.5 1.7
337.1 12.1 1.0
230.3 5.3 2.6
185.3 2.0 1.3
303.8 14.8 3.1
201.9 0.5 0.1
567.4 9.5 7.4
227.0 3.0 3.5
387.2 6.1 1.1
373.8 6.4 0.7
212.0 0.5 0.1
193.6 4.3 0.1
1762.4 64.0 43.2
360.5 3.8 0.4
195.3 0.5 0.6
303.8 0.5 0.0
320.4 34.5 0.5
9719.9 714.1 181.0
2697.0 228.2 128.9
208.6 2.8 0.7
247.0 1.4 0.3
627.5 15.0 2.2
1308.4 20.8 14.6
200.3 2.1 3.2
353.8 4.3 5.5
191.9 4.1 0.4
196.9 1.7 3.1
627.5 15.0 2.2
210.3 4.2 2.8
260.4 8.0 1.1
240.3 0.9 0.8
440.6 15.2 19.2
3845.2 59.3 98.7
195.3 13.6 3.6
567.4 29.2 6.7
287.1 1.3 0.0
183.6 14.6 0.2
208.6 8.0 2.4
283.7 17.9 1.3
1335.1 24.0 1.5
205.3 1.7 1.6
203.6 4.9 3.0
213.6 1.6 5.2
540.7 19.4 5.6
217.0 21.8 0.2
186.9 0.7 1.1
198.6 4.5 2.7
317.1 3.4 2.0
203.6 7.7 5.0
240.3 2.4 1.1
1976.0 152.7 21.4
280.4 0.5 0.2
205.3 13.1 0.1
220.3 1.1 4.6
827.8 19.0 5.4
227.0 63.0 2.8
554.1 19.0 6.9
195.3 2.1 2.4
205.3 0.5 0.2
741.0 44.0 22.6
19653.3 492.5 224.8
433.9 5.1 59.0
250.3 14.5 8.0
198.6 3.1 8.7
190.3 6.5 4.4
227.0 63.0 2.8
467.3 2.9 3.8
277.0 9.5 25.0
181.9 6.7 3.9
223.6 5.0 6.5
881.2 18.2 7.1
217.0 1.3 0.0
440.6 19.6 63.1
223.6 1.3 5.4
988.0 22.7 14.6
270.4 13.2 8.2
205.3 8.3 0.9
240.3 9.8 1.1
320.4 0.8 0.2
527.4 8.0 5.7
185.3 1.4 1.5
201.9 2.3 1.8
195.3 1.7 7.2
377.2 3.5 1.6
227.0 0.7 0.3
8972.2 131.5 131.6
212.0 1.2 0.0
Nota: Fuente: Global Weather Repository.

4.- Gráfica de dispersión

# 3. Gráfico original en Plotly
plot_ly(
  data = datos_limpios, 
  x = ~air_quality_PM2.5, 
  y = ~air_quality_Nitrogen_dioxide, 
  z = ~air_quality_Carbon_Monoxide, 
  type = "scatter3d", 
  mode = "markers",
  marker = list(
    size = 2,                    
    color = ~air_quality_Carbon_Monoxide,    
    colorscale = "Viridis", 
    opacity = 0.5                  
  )
) %>%
  layout(
        title = "Gráfico N°1: Nube de puntos, Relación de PM2.5, NO2 y CO 
para el análisis meteorológico en capitales del mundo
  Periodo: Mayo 2024 a Mayo 2026",
    scene = list(
      xaxis = list(title = "PM 2.5 (µg/m³)"),
      yaxis = list(title = "NO2 (µg/m³)"),
      zaxis = list(title = "CO (µg/m³)")
    )
  )

5.- Conjetura

dado que se observa una distribucion muy heterogenea procedemos a tratar los datos

Estrategia 1: apliqué una escala logarítmica para evitar que los datos se amontonen en una mancha borrosa. Esta escala actúa como una lupa: ‘estira’ los valores bajos donde se concentra la mayor información y ‘comprime’ los picos extremos, permitiéndonos ver claramente las tendencias que en una escala lineal quedaban ocultas.”

scene = list(
  xaxis = list(title = "PM 2.5 (µg/m³)", type = "log"), # type = "log"
  yaxis = list(title = "NO2 (µg/m³)", type = "log"),
  zaxis = list(title = "CO (µg/m³)", type = "log")
)
plot_ly(
  data = datos_limpios, 
  x = ~air_quality_PM2.5, 
  y = ~air_quality_Nitrogen_dioxide, 
  z = ~air_quality_Carbon_Monoxide, 
  type = "scatter3d", 
  mode = "markers",
  marker = list(
    size = 1.5, 
    color = ~air_quality_Carbon_Monoxide, 
    colorscale = "Viridis", 
    opacity = 0.6
  )
) %>%
  layout(
    title = "Gráfico N°1: Nube de puntos (Escala Logarítmica)", 
    scene = list(
      xaxis = list(
        title = "PM 2.5 (log)", 
        type = "log",           # Aplicamos escala logarítmica
        autorange = TRUE
      ), 
      yaxis = list(
        title = "NO2 (log)", 
        type = "log", 
        autorange = TRUE
      ),
      zaxis = list(
        title = "CO (log)", 
        type = "log", 
        autorange = TRUE
      )
    )
  )

Como se observa, a medida que las variables independientes crecen la variable dependiente tambien ya con esto nos da una idea en un modelo de regresion multiple:

Modelo Multiple:

\[Y = a + bX_1 + cX_2\]

\(Y\): Variable dependiente (el fenómeno que quieres predecir).

\(a\):Intersección (valor de \(Y\) cuando todas las variables son cero).

\(b, c\): Coeficientes (indican cuánto cambia \(Y\) por cada unidad que aumenta la variable correspondiente)

\(X_1, X_2\): Variables independientes (factores que influyen en la predicción).

6.- Cálculo de parámetros

# 1. Ajustar el modelo log-log
df_temp <- na.omit(data.frame(
  y  = log10(datos_limpios$air_quality_Carbon_Monoxide + 1),
  x1 = log10(datos_limpios$air_quality_PM2.5 + 1),
  x2 = log10(datos_limpios$air_quality_Nitrogen_dioxide + 1)
))

# 2. Ajustar el modelo usando este dataframe limpio
modelo <- lm(y ~ x1 + x2, data = df_temp)
a <- round(modelo$coefficients[1], 2)
a
## (Intercept) 
##        1.98
b <- round(modelo
           $coefficients[2], 2)
b
##   x1 
## 0.28
c <- round(modelo$coefficients[3], 2)
c
##   x2 
## 0.23
ecuacion_texto <- paste0("Y = ", a, " + ", b, "X1 + ", c, "X2")

# Imprimir el resultado
cat("La ecuación del modelo es:\n", ecuacion_texto)
## La ecuación del modelo es:
##  Y = 1.98 + 0.28X1 + 0.23X2

7.- Sobreponer la realidad con el modelo

# 1. Definir los límites del gráfico para que el plano llene toda la vista
# Usamos los mismos valores que tiene tu escena logarítmica
pm_log_seq <- seq(min(df_temp$x1), max(df_temp$x1), length = 20)
no_log_seq <- seq(min(df_temp$x2), max(df_temp$x2), length = 20)

# 2. Crear la malla (grid) para la predicción
plano_grid <- expand.grid(x1 = pm_log_seq, x2 = no_log_seq)

# 3. Calcular los valores Z (predicciones del modelo)
# Usamos los coeficientes del modelo 'modelo' que ya calculaste
z_matrix <- matrix(predict(modelo, newdata = plano_grid), 
                   nrow = length(pm_log_seq), ncol = length(no_log_seq))

# 4. Graficar realidad vs modelo
plot_ly() %>%
  # Nube de puntos (realidad) - usamos los datos transformados a log
  add_markers(x = ~df_temp$x1, y = ~df_temp$x2, z = ~df_temp$y,
              marker = list(size = 1.5, opacity = 0.3, color = "blue"),
              name = "Realidad") %>%
  # Plano (modelo)
  add_surface(x = pm_log_seq, y = no_log_seq, z = z_matrix,
              colorscale = list(c(0, 1), c("red", "red")),
              opacity = 0.6, showscale = FALSE, name = "Modelo") %>%
  layout(
    title = "Comparación: Realidad y Modelo Log-Lineal",
    scene = list(
      xaxis = list(title = "log10(PM 2.5 + 1)"),
      yaxis = list(title = "log10(NO2 + 1)"),
      zaxis = list(title = "log10(CO + 1)")
    )
  )

8.- Test de Bondad

Correlación de pearson

# 3. Calcular la correlación ahora sí con las mismas dimensiones
r <- round(cor(df_temp$y, predict(modelo)), 1)
r
## [1] 0.7

Coeficiente de determinación

r2 <- (r^2) * 100
r2
## [1] 49

9.- Restricciones

NO2:D = {x ∈ ℝ | x ≥ 0}

PM2.5:D = {x ∈ ℝ | x ≥ 0}

CO:D = {y ∈ ℝ | y ≥ 0}

Error del modelo: ¿Existe un valor de No2 y de PM 2.5 que reemplazado en la ecuación del modelo genere un valor fuera del dominio de CO ?

el modelo no presenta restricciones que generen resultados negativos o fuera del dominio de la variable dependiente. La estructura del modelo garantiza que, para cualquier entrada dentro del dominio de \(NO_2\) y \(PM_{2.5}\) (\(x \geq 0\)), la salida se mantenga siempre dentro del rango de concentración esperado para el \(CO\).

10.- Estimaciones

¿Que valor de CO se espera cuando tengamos una concentración de 200 µg/m³ de PM 2.5 y de 500 µg/m³ de NO2?

# Definir valores de entrada
x1_input <- 200
x2_input <- 500

# Calcular Y usando los coeficientes de tu modelo
y_log <- a + (b * log10(x1_input + 1)) + (c * log10(x2_input + 1))

# Convertir de regreso a la escala original
estimacion_CO <- 10^y_log - 1

cat("Con PM2.5 =", x1_input, "y NO2 =", x2_input, 
    ", la estimación de CO es:", round(estimacion_CO, 2), "µg/m³")
## Con PM2.5 = 200 y NO2 = 500 , la estimación de CO es: 1760.4 µg/m³

11.- Conclusiones

Entre el \(CO\), el \(PM_{2.5}\) y el \(NO_2\) (medidos en µg/m³), existe una relación de tipo lineal definida por la ecuación del plano $ Y = 1.98 + 0.28X1 + 0.23X2$ .Esta relación presenta una correlación de Pearson de 0.7, lo que indica una correlación positiva moderada-fuerte. Asimismo, el coeficiente de determinación (\(R^2\)) es de 49% de la variabilidad del \(CO\), mientras que el 51% restante responde a factores externos