#Variable Cuantitativa Continua
# Ozono
#Autor: Ariana Viteri
#Fecha:31/05/2026

0.- Carga de Librerias

library(gt)
## Warning: package 'gt' was built under R version 4.5.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.5.3
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(e1071)
## Warning: package 'e1071' was built under R version 4.5.3

1.- Carga de Datos

#Cargar los datos 
datos <- read.csv("~/semestre 3 y 4/Estadistica/Datos Cambiados.csv",
                  header = TRUE, dec = ".", sep = ",")

2.- Selección de variable

#----------------------------------------
# Selección de variable

# NOTA: Asumimos que la columna se llama 'O3'
ozono <- datos$O3[datos$O3 != "-"]

# Conversión a numérico
ozono <- as.numeric(ozono)

3.- Frecuencia

# =========================================================
# FRECUENCIAS PARA ozono

# Tamaño de muestra
n <- length(ozono)

# Valor mínimo y máximo
min_ozono <- min(ozono, na.rm = TRUE)
max_ozono <- max(ozono, na.rm = TRUE)

# Rango
R <- max_ozono - min_ozono

# Número de intervalos (Regla de Sturges)
k_detallado <- ceiling(1 + 3.322 * log10(n))

# Amplitud de clase
A <- R / k_detallado

# Mostrar resultados
cat("Número de intervalos (k):", k_detallado, "\n")
## Número de intervalos (k): 16
cat("Amplitud de clase:", A, "\n")
## Amplitud de clase: 16.1075
# Generación de límites de intervalos
Li <- seq(from = min_ozono, to = max_ozono - A, by = A)
Ls <- c(seq(from = min_ozono + A, to = max_ozono - A, by = A), max_ozono)

# Redondeo
ozono <- round(ozono, 3)
Li <- round(Li, 3)
Ls <- round(Ls, 3)

# Marcas de clase
MC <- (Li + Ls) / 2

# Frecuencias absolutas
ni <- numeric(length(Li))

for(i in 1:length(Li)){
  if(i < length(Li)){
    ni[i] <- sum(ozono >= Li[i] & ozono < Ls[i])
  } else {
    ni[i] <- sum(ozono >= Li[i] & ozono <= Ls[i])
  }
}

# Frecuencias relativas y acumuladas
hi <- (ni / n) * 100
Ni_asc <- cumsum(ni)
Ni_desc <- rev(cumsum(rev(ni)))
Hi_asc <- cumsum(hi)
Hi_desc <- rev(cumsum(rev(hi)))

# Intervalos
Intervalo <- paste0("[", round(Li, 2), " - ", round(Ls, 2), ")")

# Último intervalo cerrado
Intervalo[length(Intervalo)] <- paste0(
  "[",
  round(Li[length(Li)], 2),
  " - ",
  round(Ls[length(Ls)], 2),
  "]"
)

# Tabla de distribución de frecuencias
TDF_ozono <- data.frame(
  Intervalo = Intervalo,
  MC = round(MC, 2),
  ni = ni,
  hi = round(hi, 2),
  Ni_ascendente = Ni_asc,
  Ni_descendente = Ni_desc,
  Hi_ascendente = round(Hi_asc, 2),
  Hi_descendente = round(Hi_desc, 2)
)

# Fila de totales
totales <- data.frame(
  Intervalo = "Totales",
  MC = "-",
  ni = sum(ni),
  hi = round(sum(hi), 2),
  Ni_ascendente = "-",
  Ni_descendente = "-",
  Hi_ascendente = "-",
  Hi_descendente = "-"
)

# Tabla completa
TDF_ozono_completa <- rbind(TDF_ozono, totales)

4.- Tabla de distribución de frecuencia

Tabla con regla de Sturges

#TABLA DE FRECUENCIAS DETALLADA

TDF_ozono_completa %>%
  gt() %>%
  tab_header(
    title = "Tabla Nro. 1",
    subtitle = "Distribución de frecuencia de concentración de Ozono (O3), estudio calidad del aire en India entre 2015-2020"
  ) %>%
  tab_source_note(
    source_note = md("Grupo: 1 <br> Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india ")
  ) %>%
  tab_style(
    style = cell_borders(sides = "left", color = "black", weight = px(2)),
    locations = cells_body()
  ) %>%
  tab_style(
    style = cell_borders(sides = "right", color = "black", weight = px(2)),
    locations = cells_body()
  ) %>%
  tab_style(
    style = cell_borders(sides = "left", color = "black", weight = px(2)),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_borders(sides = "right", color = "black", weight = px(2)),
    locations = cells_column_labels()
  ) %>%
  tab_options(
    table.border.top.color = "black",
    table.border.bottom.color = "black",
    table.border.top.style = "solid",
    table.border.bottom.style = "solid",
    column_labels.border.top.color = "black",
    column_labels.border.bottom.color = "black",
    column_labels.border.bottom.width = px(2),
    row.striping.include_table_body = TRUE,
    heading.border.bottom.color = "black",
    heading.border.bottom.width = px(2),
    table_body.hlines.color = "gray",
    table_body.border.bottom.color = "black"
  )
Tabla Nro. 1
Distribución de frecuencia de concentración de Ozono (O3), estudio calidad del aire en India entre 2015-2020
Intervalo MC ni hi Ni_ascendente Ni_descendente Hi_ascendente Hi_descendente
[0.01 - 16.12) 8.06 4880 19.13 4880 25509 19.13 100
[16.12 - 32.23) 24.17 8602 33.72 13482 20629 52.85 80.87
[32.23 - 48.33) 40.28 6518 25.55 20000 12027 78.4 47.15
[48.33 - 64.44) 56.39 3291 12.90 23291 5509 91.31 21.6
[64.44 - 80.55) 72.49 1298 5.09 24589 2218 96.39 8.69
[80.55 - 96.66) 88.6 521 2.04 25110 920 98.44 3.61
[96.66 - 112.76) 104.71 226 0.89 25336 399 99.32 1.56
[112.76 - 128.87) 120.82 96 0.38 25432 173 99.7 0.68
[128.87 - 144.98) 136.92 45 0.18 25477 77 99.87 0.3
[144.98 - 161.08) 153.03 19 0.07 25496 32 99.95 0.13
[161.08 - 177.19) 169.14 9 0.04 25505 13 99.98 0.05
[177.19 - 193.3) 185.25 1 0.00 25506 4 99.99 0.02
[193.3 - 209.41) 201.35 2 0.01 25508 3 100 0.01
[209.41 - 225.51) 217.46 0 0.00 25508 1 100 0
[225.51 - 241.62) 233.57 0 0.00 25508 1 100 0
[241.62 - 257.73] 249.68 1 0.00 25509 1 100 0
Totales - 25509 100.00 - - - -
Grupo: 1
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india

Tabla de intervalos reducidos

# ==============================================================================
# Por una gran cantidad de intervalos se realizara una reducción de filas en la
# tabla Nro. 2 creando solo 10 intervalos
# ==============================================================================

# TABLA 2
k_tabla2 <- 10

# Nueva amplitud
A2 <- R / k_tabla2

# Nuevos límites
Li2 <- seq(from = min_ozono, to = max_ozono - A2, by = A2)
Ls2 <- c(seq(from = min_ozono + A2, to = max_ozono - A2, by = A2), max_ozono)

# Redondeo
Li2 <- round(Li2, 3)
Ls2 <- round(Ls2, 3)

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

# Frecuencias absolutas
ni2 <- numeric(length(Li2))

for(i in 1:length(Li2)){
  
  if(i < length(Li2)){
    ni2[i] <- sum(ozono >= Li2[i] & ozono < Ls2[i])
  } else {
    ni2[i] <- sum(ozono >= Li2[i] & ozono <= Ls2[i])
  }
}

# Frecuencias relativas y acumuladas
hi2 <- (ni2 / n) * 100
Ni2_asc <- cumsum(ni2)
Ni2_desc <- rev(cumsum(rev(ni2)))
Hi2_asc <- cumsum(hi2)
Hi2_desc <- rev(cumsum(rev(hi2)))

# Intervalos
Intervalo2 <- paste0("[", round(Li2,2), " - ", round(Ls2,2), ")")

Intervalo2[length(Intervalo2)] <- paste0(
  "[",
  round(Li2[length(Li2)],2),
  " - ",
  round(Ls2[length(Ls2)],2),
  "]"
)

# Tabla 2
TDF_ozono_10 <- data.frame(
  Intervalo = Intervalo2,
  MC = round(MC2,2),
  ni = ni2,
  hi = round(hi2,2),
  Ni_ascendente = Ni2_asc,
  Ni_descendente = Ni2_desc,
  Hi_ascendente = round(Hi2_asc,2),
  Hi_descendente = round(Hi2_desc,2)
)

# Totales
totales2 <- data.frame(
  Intervalo = "Totales",
  MC = "-",
  ni = sum(ni2),
  hi = sum(hi2),
  Ni_ascendente = "-",
  Ni_descendente = "-",
  Hi_ascendente = "-",
  Hi_descendente = "-"
)

TDF_ozono_10_completa <- rbind(TDF_ozono_10, totales2)

# TABLA 2: Distribución de frecuencias de ozono con 10 intervalos

TDF_ozono_10_completa %>%
  gt() %>%
  tab_header(
    title = "Tabla Nro. 2",
    subtitle = "Distribución de frecuencia de concentración de Ozono (O3), estudio calidad del aire en India entre 2015-2020"
  ) %>%
  tab_source_note(
    source_note = md("Grupo: 1 <br> Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india ")
  ) %>%
  tab_style(
    style = cell_borders(sides = "left", color = "black", weight = px(2)),
    locations = cells_body()
  ) %>%
  tab_style(
    style = cell_borders(sides = "right", color = "black", weight = px(2)),
    locations = cells_body()
  ) %>%
  tab_style(
    style = cell_borders(sides = "left", color = "black", weight = px(2)),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_borders(sides = "right", color = "black", weight = px(2)),
    locations = cells_column_labels()
  ) %>%
  tab_options(
    table.border.top.color = "black",
    table.border.bottom.color = "black",
    table.border.top.style = "solid",
    table.border.bottom.style = "solid",
    column_labels.border.top.color = "black",
    column_labels.border.bottom.color = "black",
    column_labels.border.bottom.width = px(2),
    row.striping.include_table_body = TRUE,
    heading.border.bottom.color = "black",
    heading.border.bottom.width = px(2),
    table_body.hlines.color = "gray",
    table_body.border.bottom.color = "black"
  )
Tabla Nro. 2
Distribución de frecuencia de concentración de Ozono (O3), estudio calidad del aire en India entre 2015-2020
Intervalo MC ni hi Ni_ascendente Ni_descendente Hi_ascendente Hi_descendente
[0.01 - 25.78) 12.9 10067 39.46 10067 25509 39.46 100
[25.78 - 51.55) 38.67 10796 42.32 20863 15442 81.79 60.54
[51.55 - 77.33) 64.44 3564 13.97 24427 4646 95.76 18.21
[77.33 - 103.1) 90.21 796 3.12 25223 1082 98.88 4.24
[103.1 - 128.87) 115.98 209 0.82 25432 286 99.7 1.12
[128.87 - 154.64) 141.76 56 0.22 25488 77 99.92 0.3
[154.64 - 180.41) 167.53 17 0.07 25505 21 99.98 0.08
[180.41 - 206.19) 193.3 3 0.01 25508 4 100 0.02
[206.19 - 231.96) 219.07 0 0.00 25508 1 100 0
[231.96 - 257.73] 244.84 1 0.00 25509 1 100 0
Totales - 25509 100.00 - - - -
Grupo: 1
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india

5.- Graficos de distribución de frecuencia

Histogramas de cantidad

#===========================
# Histograma de R studio
# Primero: Crea el objeto sin graficar
Histograma_ozono <- hist(ozono, breaks = 13, plot = FALSE)

# Segundo: Ahora sí, usa el objeto en el gráfico
hist(ozono, breaks = 13,
     main = "Grafica Nro.1 de distribución de frecuencias de concentración de Ozono\nen el estudio calidad del aire en India de 2015-2020",
     xlab = "Ozono (µg/m³)",
     ylab = "Cantidad",
     ylim = c(0, max(Histograma_ozono$counts)),
     col = "darkseagreen3", 
     cex.main = 0.9,
     cex.lab = 1,
     cex.axis = 0.9,
     xaxt = "n")
axis(1, at = Histograma_ozono$breaks,
     labels = round(Histograma_ozono$breaks, 0), las = 1,
     cex.axis = 0.9)
grid()

#================================
#Histograma con relación a la totalidad de los datos
# Crear objeto histograma
Histograma_ozono <- hist(ozono, breaks = 13, plot = FALSE)
par(mgp = c(3.2, 1, 0))
hist(ozono, breaks = 13,
     main = "Grafica Nro.2 de distribución de frecuencias de concentración de Ozono\nen el estudio calidad del aire en India de 2015-2020",
     xlab = "Ozono (µg/m³)",
     ylab = "Cantidad",
     ylim = c(0, 25500),
     col = "darkseagreen3",
     cex.main = 0.9,
     cex.lab = 1,
     cex.axis = 0.9,
     xaxt = "n",
     yaxt = "n")

# Eje X
axis(1,
     at = Histograma_ozono$breaks,
     labels = round(Histograma_ozono$breaks, 0),
     las = 1,
     cex.axis = 0.9)

# Eje Y
axis(2,
     at = seq(0, 25000, by = 5000),
     labels = seq(0, 25000, by = 5000),
     las = 1,
     cex.axis = 0.9)
grid()

Histogramas Porcentuales

#------------------------------------------------
# Histograma porcentual de Ozono
bp <- barplot(hi2,
              space = 0,
              names.arg = FALSE,
              xaxt = "n",
              yaxt = "n",
              main = "Grafica Nro.3 de distribución porcentual de Ozono\nen el estudio calidad del aire en India de 2015-2020",
              xlab = "Ozono (µg/m³)",
              ylab = "Porcentaje (%)",
              col = "darkseagreen3",
              border = "black",
              ylim = c(0, 40),
              cex.main = 0.8)

# Eje X
axis(1,
     at = c(0, 2, 4, 6, 8, 10),
     labels = c(0, 50, 100, 150, 200, 260),
     las = 1)

# Eje Y
axis(2,
     at = seq(0, 50, by = 10),
     labels = seq(0, 50, by = 10),
     las = 1)
grid()

#-------------------------------------------------------
# Histograma porcentual de Ozono con respecto al todo
bp <- barplot(hi2,
              space = 0,
              names.arg = FALSE,
              xaxt = "n",
              yaxt = "n",
              main = "Grafica Nro.4 de distribución porcentual de Ozono\nen el estudio calidad del aire en India de 2015-2020",
              xlab = "Ozono (µg/m³)",
              ylab = "Porcentaje (%)",
              col = "darkseagreen3",
              border = "black",
              ylim = c(0, 100),
              cex.main = 0.8)

# Eje X
axis(1,
     at = c(0, 2, 4, 6, 8, 10),
     labels = c(0, 50, 100, 150, 200, 260),
     las = 1)

# Eje Y
axis(2,
     at = seq(0, 100, by = 20),
     labels = seq(0, 100, by = 20),
     las = 1)
grid()

Diagrama de caja y bigotes

#-----------------------------------------------------------
# Diagrama de caja y bigotes
boxplot(ozono,
        horizontal = TRUE,
        xaxt = "n",
        yaxt = "n",
        main = "Gráfica Nro.5: Diagrama de caja de la concentración de Ozono\nen el estudio calidad del aire en India de 2015-2020",
        xlab = "Ozono (µg/m³)",
        col = "turquoise3",
        border = "black",
        cex.main = 0.9,
        cex.lab = 1,
        cex.axis = 0.9)

# Eje X personalizado
axis(1,
     at = seq(0, 260, by = 40),
     labels = seq(0, 260, by = 40),
     las = 1)
grid()

Diagrama de Ojivas

# ==============================================================================
# OJIVA ASCENDENTE Y DESCENDENTE generada por R studio
plot(Ls2, Ni2_asc,
     type = "b",
     pch = 16,
     col = "turquoise3",
     lwd = 1,
     ylim = c(0, n),
     xlim = c(10, 270),
     xaxt = "n",
     xlab = "Ozono",
     ylab = "Cantidad",
     main = "Gráfica N°6: Ojiva ascendente y descendente de la\nconcentración de Ozono",
     cex.main = 1)

# Eje X cada 100 unidades
axis(1, at = seq(0, 260, by = 10))

# Ojiva descendente
lines(Ls2, Ni2_desc,
      type = "b",
      pch = 16,
      col = "black",
      lwd = 1)

grid()
box()

# ==============================================================================
# OJIVA PORCENTUAL ASCENDENTE Y DESCENDENTE

plot(Ls2, Hi2_asc,
     type = "b",
     pch = 16,
     col = "turquoise3",
     lwd = 1,
     ylim = c(0, 100),
     xlim = c(10, 260),
     xaxt = "n",
     xlab = "Ozono",
     ylab = "Porcentaje (%)",
     main = "Gráfica N°7: Ojiva porcentual ascendente y descendente de la\nconcentración de Ozono",
     cex.main = 1)

# Eje X cada 100 unidades
axis(1, at = seq(0, 260, by = 20))

# Ojiva porcentual descendente
lines(Ls2, Hi2_desc,
      type = "b",
      pch = 16,
      col = "black",
      lwd = 1)
grid()
box()

6.-Indicadores Estadísticos

# =========================================================
# Calculo previo de indicadores
X <- mean(ozono, na.rm = TRUE)       # Media
Me <- median(ozono, na.rm = TRUE)    # Mediana

# Funcion para la Moda
# Obtenemos el intervalo de la clase con la frecuencia más alta (Moda de la distribución simplificada)
moda_index <- which.max(TDF_ozono_10_completa$ni[1:(nrow(TDF_ozono_10_completa)-1)])
Mo <- TDF_ozono_10_completa$Intervalo[moda_index]

desv <- sd(ozono, na.rm = TRUE)      # Desviacion estandar
CV <- (desv / X) * 100               # Coeficiente de variacion

# Libreria para Asimetria y Curtosis
library(e1071)

As <- skewness(ozono, na.rm = TRUE)
K <- kurtosis(ozono, na.rm = TRUE)

# Creacion del data frame
Tabla_indicadores <- data.frame(
  Variable = "Ozono",
  Rango = paste0("[", round(min(ozono),2), " - ", round(max(ozono),2), "]"),
  Media = X,
  Mediana = Me,
  Moda = Mo,
  DesvEst = desv,
  CV = CV,
  Asimetria = As,
  Curtosis = K
)

# Visualizacion de la tabla
library(gt)

Tabla_indicadores %>%
  gt() %>%
  cols_label(
    Variable = "Variable",
    Rango = "Rango",
    Media = "Media (X)",
    Mediana = "Mediana (Me)",
    Moda = "Moda (Mo)",
    DesvEst = "Desv. Est. (sd)",
    CV = "CV (%)",
    Asimetria = "Asimetria (As)",
    Curtosis = "Curtosis (K)"
  ) %>%
  tab_header(
    title = "Tabla Nro. 3",
    subtitle = "Indicadores Estadisticos de la concentracion de Ozono, estudio calidad del aire en India entre 2015-2020"
  ) %>%
  tab_source_note(
    source_note = "Autor: Grupo 1 | Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india"
  ) %>%
  tab_spanner(
    label = "Tendencia Central",
    columns = c(Media, Mediana, Moda)
  ) %>%
  tab_spanner(
    label = "Dispersion",
    columns = c(DesvEst, CV)
  ) %>%
  tab_spanner(
    label = "Forma",
    columns = c(Asimetria, Curtosis)
  ) %>%
  fmt_number(
    columns = c(Media, Mediana, DesvEst, CV, Asimetria, Curtosis),
    decimals = 2
  ) %>%
  tab_style(
    style = cell_borders(
      sides = c("left", "right", "top", "bottom"),
      color = "black",
      weight = px(1)
    ),
    locations = list(
      cells_body(columns = everything(), rows = everything()),
      cells_column_labels(columns = everything()),
      cells_column_spanners(spanners = everything())
    )
  ) %>%
  tab_options(
    table.border.top.color = "black",
    table.border.bottom.color = "black",
    table.border.left.color = "black",
    table.border.right.color = "black",
    table_body.hlines.color = "black",
    table_body.vlines.color = "black",
    column_labels.border.bottom.width = px(2)
  )
Tabla Nro. 3
Indicadores Estadisticos de la concentracion de Ozono, estudio calidad del aire en India entre 2015-2020
Variable Rango
Tendencia Central
Dispersion
Forma
Media (X) Mediana (Me) Moda (Mo) Desv. Est. (sd) CV (%) Asimetria (As) Curtosis (K)
Ozono [0.01 - 257.73] 34.49 30.84 [25.78 - 51.55) 21.69 62.90 1.33 3.43
Autor: Grupo 1 | Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india

7.- Conclusión

En conclusión:

La variable Ozono (O₃) fluctúa entre un mínimo de 0.01 y un máximo de 257.73 microgramos por metro cúbico (µg/m³), con una media de 34.49 y una desviación estándar de 21.69. Debido a un coeficiente de variación del 62.90%, se concluye que es un conjunto de valores heterogéneos con una dispersión considerable. Los datos se acumulan de manera predominante en el intervalo modal [25.78 - 51.55). No obstante, se han detectado 713 valores atípicos, los cuales representan concentraciones inusualmente elevadas de ozono que pueden influir en la calidad del aire y afectar moderadamente al medio ambiente.