0.- Carga de librerías
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
1.- Carga datos
datos <- read.csv("C:/Users/JOSELYN/Documents/UNIVERSIDAD 3-4 SEMESTRE/ESTADISTICA/MAPA QGIS/Datos Cambiados.csv",
header = TRUE, dec = ".", sep = ",")
2.- Selección de variable aleatoria
# Justificación porque la variable es continua: porque puede tomar cualquier valor dentro de los números reales positivos, incluido el cero, sin presentar saltos en su dominio o la variable cambia de manera gradual y no pasa bruscamente de un valor a otro.
# Extracción de la variable Dióxido de azufre (SO2)
# Se eliminan los guiones "-" que representan valores inexistentes
dioxidoazufre <- datos$SO2[datos$SO2 != "-"]
# Conversión a numérico y limpieza de posibles NA generados
dioxidoazufre <- as.numeric(dioxidoazufre)
dioxidoazufre <- dioxidoazufre[!is.na(dioxidoazufre)]
3.- tabla de distribución de frecuencia
# Forzamos exactamente 10 intervalos sin decimales (de 0 a 200 en saltos de 20)
cortes_exactos <- seq(0, 200, by = 20)
Histograma_dioxidoazufre <- hist(dioxidoazufre, breaks = cortes_exactos, plot = FALSE)
breaks <- Histograma_dioxidoazufre$breaks
Li <- breaks[1:(length(breaks)-1)]
Ls <- breaks[2:length(breaks)]
ni <- Histograma_dioxidoazufre$counts
N <- length(dioxidoazufre)
# Cálculo de frecuencias y probabilidad
hi <- (ni / N) * 100
p_s <- ni / N
TDF_dioxidoazufresimplificado <- data.frame(
# Al ser cortes enteros, ya no necesitamos la función round() aquí
Intervalo = paste0("[", Li, " - ", Ls, ")"),
ni = ni,
hi = round(hi, 2),
p_s = round(p_s, 2) # Reducimos la probabilidad a solo 2 decimales
)
colnames(TDF_dioxidoazufresimplificado) <- c(
"Intervalo",
"ni",
"hi(%)",
"p(s)"
)
totales <- data.frame(
Intervalo = "Totales",
ni = sum(ni), # suma total de ni
hi = sum(hi), # debe sumar 100
p_s = sum(round(p_s, 2)) # Ajustado para que cuadre visualmente con la columna
)
colnames(totales) <- c(
"Intervalo",
"ni",
"hi(%)",
"p(s)"
)
# Agregar al final de la tabla
TDF_dioxidoazufresimplificado <- rbind(TDF_dioxidoazufresimplificado, totales)
TDF_dioxidoazufresimplificado %>%
gt() %>%
tab_header(
title = md("*Tabla Nro. 1*"),
subtitle = md("**Distribución de frecuencia simplificada de concentración de dióxido de azufre, estudio calidad del aire en India entre 2015-2020**")
) %>%
tab_source_note(
source_note = md("Autor: Ariel Chiluisa\nFuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india")
) %>%
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",
# Líneas verticales para los márgenes exteriores
table.border.left.color = "black",
table.border.left.style = "solid",
table.border.left.width = px(1),
table.border.right.color = "black",
table.border.right.style = "solid",
table.border.right.width = px(1),
# Líneas verticales para separar las columnas internamente
column_labels.vlines.color = "black",
column_labels.vlines.style = "solid",
column_labels.vlines.width = px(1),
table_body.vlines.color = "black",
table_body.vlines.style = "solid",
table_body.vlines.width = px(1)
)
| Tabla Nro. 1 |
| Distribución de frecuencia simplificada de concentración de dióxido de azufre, estudio calidad del aire en India entre 2015-2020 |
| Intervalo |
ni |
hi(%) |
p(s) |
| [0 - 20) |
21244 |
82.74 |
0.83 |
| [20 - 40) |
2735 |
10.65 |
0.11 |
| [40 - 60) |
953 |
3.71 |
0.04 |
| [60 - 80) |
287 |
1.12 |
0.01 |
| [80 - 100) |
174 |
0.68 |
0.01 |
| [100 - 120) |
128 |
0.50 |
0.00 |
| [120 - 140) |
83 |
0.32 |
0.00 |
| [140 - 160) |
40 |
0.16 |
0.00 |
| [160 - 180) |
28 |
0.11 |
0.00 |
| [180 - 200) |
5 |
0.02 |
0.00 |
| Totales |
25677 |
100.00 |
1.00 |
| Autor: Ariel Chiluisa
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india |
4.- Gráfica distribución de frecuencia
# Histograma porcentual de la variable
par(mar = c(5.1, 4.1, 4.1, 2.1)) # Ajuste de márgenes para que no se corten los textos
# Se crea el lienzo base (ocultando las barras por defecto con col=NA y border=NA)
plot(Histograma_dioxidoazufre,
freq = FALSE,
col = NA,
border = NA,
main = "Gráfica N°1: Distribución porcentual de la concentración de Dióxido de Azufre\nen estudio calidad del aire en India, 2015-2020",
xlab = "Dióxido de azufre (µg/m³)",
ylab = "Porcentaje",
xaxt = "n",
yaxt = "n",
cex.main = 1,
ylim = c(0, max(hi) * 1.1))
# Cuadrícula para facilitar la lectura
abline(v = breaks, col = "gray70", lty = 2, lwd = 0.8)
abline(h = pretty(c(0, max(hi))), col = "gray70", lty = 2, lwd = 0.8)
# Barras porcentuales superpuestas (usando hi)
rect(
breaks[-length(breaks)],
0,
breaks[-1],
hi,
col = "orange", # Color adaptado a tu variable SO2
border = "black"
)
# Eje X con los límites de los intervalos enteros
axis(1,
at = breaks,
labels = breaks, # Se eliminó el round() para mostrar los enteros limpios
las = 1,
cex.axis = 0.9)
# Eje Y en porcentaje
axis(2,
at = pretty(c(0, max(hi))),
labels = pretty(c(0, max(hi))),
las = 1)
# Borde exterior de la gráfica
box()

5.- Tratamiento de datos
#---------------------------------------------------------------------------------------------------------------------------------------
# Simplificamos los datos y realizamos un ajuste para ignorar los intervalos vacíos o con muy pocos datos que se encuentran en el extremo superior y así mejorar el ajuste de nuestro modelo.
#---------------------------------------------------------------------------------------------------------------------------------------
# TDF
# Filtramos los datos para eliminar los valores atípicos (mayores a 140)
# Creamos una nueva variable para no dañar tus datos originales
dioxidoazufre_ajustado <- dioxidoazufre[dioxidoazufre <= 140]
# Definimos los nuevos cortes exactos de 0 a 140 en saltos de 20
cortes_ajustados <- seq(0, 140, by = 20)
# Creamos el histograma interno con los datos filtrados
Histograma_ajustado <- hist(dioxidoazufre_ajustado, breaks = cortes_ajustados, plot = FALSE)
breaks_ajust <- Histograma_ajustado$breaks
Li_ajust <- breaks_ajust[1:(length(breaks_ajust)-1)]
Ls_ajust <- breaks_ajust[2:length(breaks_ajust)]
ni_ajust <- Histograma_ajustado$counts
N_ajustado <- length(dioxidoazufre_ajustado)
# Cálculo de frecuencias y probabilidad con la nueva cantidad de datos
hi_ajust <- (ni_ajust / N_ajustado) * 100
p_s_ajust <- ni_ajust / N_ajustado
TDF_ajustada <- data.frame(
Intervalo = paste0("[", Li_ajust, " - ", Ls_ajust, ")"),
ni = ni_ajust,
hi = round(hi_ajust, 2),
p_s = round(p_s_ajust, 2)
)
colnames(TDF_ajustada) <- c(
"Intervalo",
"ni",
"hi(%)",
"p(s)"
)
totales_ajust <- data.frame(
Intervalo = "Totales",
ni = sum(ni_ajust),
hi = sum(hi_ajust),
p_s = sum(round(p_s_ajust, 2))
)
colnames(totales_ajust) <- c(
"Intervalo",
"ni",
"hi(%)",
"p(s)"
)
# Agregar la fila de totales al final
TDF_ajustada <- rbind(TDF_ajustada, totales_ajust)
# Imprimir la tabla con el mismo formato profesional
TDF_ajustada %>%
gt() %>%
tab_header(
title = md("*Tabla Nro. 2*"),
subtitle = md("**Distribución de frecuencia especifica de concentración de dióxido de azufre**")
) %>%
tab_source_note(
source_note = md("Autor: Ariel Chiluisa\nFuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india")
) %>%
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",
table.border.left.color = "black",
table.border.left.style = "solid",
table.border.left.width = px(1),
table.border.right.color = "black",
table.border.right.style = "solid",
table.border.right.width = px(1),
column_labels.vlines.color = "black",
column_labels.vlines.style = "solid",
column_labels.vlines.width = px(1),
table_body.vlines.color = "black",
table_body.vlines.style = "solid",
table_body.vlines.width = px(1)
)
| Tabla Nro. 2 |
| Distribución de frecuencia especifica de concentración de dióxido de azufre |
| Intervalo |
ni |
hi(%) |
p(s) |
| [0 - 20) |
21244 |
82.97 |
0.83 |
| [20 - 40) |
2735 |
10.68 |
0.11 |
| [40 - 60) |
953 |
3.72 |
0.04 |
| [60 - 80) |
287 |
1.12 |
0.01 |
| [80 - 100) |
174 |
0.68 |
0.01 |
| [100 - 120) |
128 |
0.50 |
0.00 |
| [120 - 140) |
83 |
0.32 |
0.00 |
| Totales |
25604 |
100.00 |
1.00 |
| Autor: Ariel Chiluisa
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india |
# GDF Especifica
par(mar = c(5.1, 4.1, 4.1, 2.1)) # Ajuste de márgenes para que no se corten los textos
# Se crea el lienzo base ocultando las barras por defecto con col=NA y border=NA
plot(Histograma_ajustado,
freq = FALSE,
col = NA,
border = NA,
main = "Gráfica N°2: Distribución porcentual simplificada de la Concentración\nde Dióxido de Azufre, India (2015-2020)",
xlab = "Dióxido de azufre (µg/m³)",
ylab = "Porcentaje",
xaxt = "n",
yaxt = "n",
cex.main = 1,
ylim = c(0, max(hi_ajust) * 1.1))
# Cuadrícula para facilitar la lectura, ajustada a los nuevos límites
abline(v = cortes_ajustados, col = "gray70", lty = 2, lwd = 0.8)
abline(h = pretty(c(0, max(hi_ajust))), col = "gray70", lty = 2, lwd = 0.8)
# Barras porcentuales superpuestas (usando la nueva hi_ajust)
rect(
cortes_ajustados[-length(cortes_ajustados)],
0,
cortes_ajustados[-1],
hi_ajust,
col = "orange", # Mantenemos el color adaptado a la variable SO2
border = "black"
)
# Eje X con los límites de los intervalos exactos (0 a 140)
axis(1,
at = cortes_ajustados,
labels = cortes_ajustados,
las = 1,
cex.axis = 0.9)
# Eje Y en porcentaje, autoajustado a las nuevas proporciones
axis(2,
at = pretty(c(0, max(hi_ajust))),
labels = pretty(c(0, max(hi_ajust))),
las = 1)
# Borde exterior de la gráfica
box()

6.- Conjetura
#Se conjetura que la concentración de dióxido de azufre (SO2) sigue un modelo de probabilidad exponencial, ya que la variable en su histograma muestra que la mayor parte de los datos está concentrada en el extremo izquierdo (bajos valores) y la frecuencia disminuye rápidamente conforme aumentan los valores.
7.- Parámetros
# ==============================================================================
# 6. CÁLCULO DE LOS PARÁMETROS DEL MODELO EXPONENCIAL
# ==============================================================================
# Calculamos la media de los datos ajustados
media <- mean(dioxidoazufre_ajustado)
# Calculamos el parámetro λ del modelo exponencial
lambda <- 1 / media
# Mostramos los resultados
cat("Media =", round(media, 4), "\n")
## Media = 14.1173
cat("Parámetro lambda =", round(lambda, 6), "\n")
## Parámetro lambda = 0.070835
8.- Sobreposición de la realidad con el modelo
# ==============================================================================
# 7. SOBREPOSICIÓN DE LA REALIDAD CON EL MODELO EXPONENCIAL
# ==============================================================================
# Histograma porcentual (lienzo vacío)
hist(
dioxidoazufre_ajustado,
breaks = cortes_ajustados,
freq = FALSE,
col = NA,
border = NA,
main = "Gráfica Nro 3\nComparación de la Realidad y el Modelo Exponencial\nde la Concentración de Dióxido de Azufre en India 2015–2020",
xlab = "Dióxido de azufre (µg/m³)",
ylab = "Densidad de probabilidad",
xaxt = "n",
yaxt = "n",
ylim = c(0, max(hi_ajust) * 1.25)
)
# Cuadrícula
abline(v = cortes_ajustados, col = "gray80", lty = 2)
abline(h = pretty(c(0, max(hi_ajust))), col = "gray80", lty = 2)
# Barras de la realidad
rect(
cortes_ajustados[-length(cortes_ajustados)],
0,
cortes_ajustados[-1],
hi_ajust,
col = "orange",
border = "black"
)
# Curva del modelo exponencial
x <- seq(0, 140, length = 1000)
y <- dexp(x, rate = lambda) * 20 * 100
lines(
x,
y,
col = "darkred",
lwd = 3
)
# Ejes
axis(
1,
at = cortes_ajustados,
labels = cortes_ajustados
)
axis(
2,
at = pretty(c(0, max(hi_ajust))),
las = 1
)
# Leyenda
legend(
"topright",
legend = c("Datos reales (FO)", "Modelo Exponencial (FE)"),
col = c("orange", "darkred"),
lty = c(NA, 1),
pch = c(22, NA),
pt.bg = c("orange", NA),
pt.cex = 2,
lwd = c(1, 3),
bty = "n"
)
# Borde
box()

9.- Test de Bondad
9.1 Test de pearson
# ==============================================================================
# 9.1 TEST DE PEARSON
# ==============================================================================
# Frecuencias observadas
fo_pearson <- ni_ajust
# Frecuencias esperadas según el modelo exponencial
fe_pearson <- N_ajustado * (
pexp(Ls_ajust, rate = lambda) -
pexp(Li_ajust, rate = lambda)
)
# Coeficiente de correlación de Pearson
Coef_Pearson <- cor(fo_pearson, fe_pearson) * 100
# Resultado
cat("Coeficiente de Pearson (%):", Coef_Pearson, "\n")
## Coeficiente de Pearson (%): 99.30358
9.2 Test Chi-cuadrado
# ==============================================================================
# 9.2 TEST DE CHI-CUADRADO
# ==============================================================================
# Total de observaciones
N_chi <- sum(ni_ajust)
# Frecuencia relativa observada
fo <- ni_ajust / N_chi
# Número de intervalos
k <- length(fo)
# Probabilidades teóricas por intervalo
P <- c(0)
for(i in 1:k){
P[i] <- pexp(
Ls_ajust[i],
rate = lambda
) -
pexp(
Li_ajust[i],
rate = lambda
)
}
# Frecuencias esperadas
fe <- P
# Estadístico Chi-cuadrado
Chi_Calculado <- sum((fo - fe)^2 / fe)
# Grados de libertad
gl <- k - 1
# Valor crítico para α = 0.05
Chi_Critico <- qchisq(0.95, df = gl)
# Resultados
cat("Chi Calculado:", Chi_Calculado, "\n")
## Chi Calculado: 0.1387734
cat("Chi Crítico:", Chi_Critico, "\n")
## Chi Crítico: 12.59159
# Decisión
if (Chi_Calculado < Chi_Critico) {
print("Evalua H0: El modelo exponencial es adecuado.")
} else {
print("Se rechaza H0: El modelo exponencial no es adecuado.")
}
## [1] "Evalua H0: El modelo exponencial es adecuado."
10.- Cálculo de probabilidades
# ==============================================================================
# 10. CÁLCULO DE PROBABILIDADES
# ==============================================================================
# --------------------------------------
# PREGUNTA DE PORCENTAJE
# ¿Cuál es la probabilidad de que la concentración de SO2
# no supere los 40 µg/m³?
prob_40 <- pexp(40, rate = lambda)
prob_40_porcentaje <- prob_40 * 100
cat(
"Probabilidad de que el SO2 no supere los 40 µg/m³:",
round(prob_40_porcentaje, 2),
"%\n"
)
## Probabilidad de que el SO2 no supere los 40 µg/m³: 94.12 %
# --------------------------------------
# PREGUNTA DE CANTIDAD
# ¿Cuántos días en 10 años se espera que el SO2
# no supere los 40 µg/m³?
dias_10_anios <- prob_40 * (10 * 365)
cat(
"Cantidad esperada de días:",
round(dias_10_anios),
"días\n"
)
## Cantidad esperada de días: 3435 días
# --------------------------------------
# PREGUNTA DE INTERVALO
# ¿Cuál es la probabilidad de que la concentración de SO2
# se encuentre entre 20 y 40 µg/m³?
prob_20_40 <- (
pexp(40, rate = lambda) -
pexp(20, rate = lambda)
) * 100
cat(
"Probabilidad entre 20 y 40 µg/m³:",
round(prob_20_40, 2),
"%\n"
)
## Probabilidad entre 20 y 40 µg/m³: 18.37 %
# ==============================================================================
# DEMOSTRACIÓN GRÁFICA
# ==============================================================================
x <- seq(0, 140, by = 0.01)
y <- dexp(x, rate = lambda)
y <- y * 100 * 20
plot(
x,
y,
col = "orange",
lwd = 2,
type = "l",
xlim = c(0, 140),
ylim = c(0, max(y)),
main = "Gráfica N°4: Cálculo de Probabilidad",
ylab = "Densidad de probabilidad",
xlab = "Dióxido de azufre (µg/m³)",
xaxt = "n"
)
axis(
1,
at = seq(0, 140, 20),
las = 1
)
# Área P(SO2 ≤ 40)
x_section <- seq(0, 40, 0.01)
y_section <- dexp(x_section, rate = lambda)
y_section <- y_section * 100 * 20
lines(
x_section,
y_section,
col = "darkgreen",
lwd = 2
)
polygon(
c(x_section, rev(x_section)),
c(y_section, rep(0, length(y_section))),
col = rgb(0, 0.6, 0, 0.35),
border = NA
)
# Área P(20 ≤ SO2 ≤ 40)
x_section2 <- seq(20, 40, 0.01)
y_section2 <- dexp(x_section2, rate = lambda)
y_section2 <- y_section2 * 100 * 20
lines(
x_section2,
y_section2,
col = "blue",
lwd = 2
)
polygon(
c(x_section2, rev(x_section2)),
c(y_section2, rep(0, length(y_section2))),
col = rgb(0, 0, 1, 0.35),
border = NA
)
legend(
"topright",
legend = c(
"Modelo exponencial",
"Área P(SO2 ≤ 40)",
"Área P(20 ≤ SO2 ≤ 40)"
),
col = c(
"orange",
"darkgreen",
"blue"
),
lwd = 2,
bty = "o",
bg = "white"
)
grid()

11.- Intervalo de confianza
# ==============================================================================
# 11. INTERVALO DE CONFIANZA
# ==============================================================================
# Calculamos la media
media_ic <- mean(dioxidoazufre_ajustado)
# Desviación estándar
desviacion_ic <- sd(dioxidoazufre_ajustado)
# Tamaño de muestra
n_ic <- length(dioxidoazufre_ajustado)
# Error máximo
error <- 1.96 * (desviacion_ic / sqrt(n_ic))
# Límites del intervalo
limite_inferior <- round(media_ic - error, 2)
limite_superior <- round(media_ic + error, 2)
# Tabla
tabla_intervalo <- data.frame(
Intervalo = paste0(
"P [",
limite_inferior,
" < µ < ",
limite_superior,
"] = 95%"
)
)
# Presentación de la tabla
tabla_intervalo %>%
gt() %>%
tab_header(
title = md("*Tabla Nro. 3*"),
subtitle = md("**Intervalo de confianza de la concentración de dióxido de azufre (SO₂) en el estudio de calidad del aire en India (2015–2020)**")
) %>%
tab_source_note(
source_note = md(
"Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india"
)
) %>%
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",
table.border.left.color = "black",
table.border.left.style = "solid",
table.border.left.width = px(1),
table.border.right.color = "black",
table.border.right.style = "solid",
table.border.right.width = px(1)
)
12 .- Conclusión
En conclusión:
El análisis de la concentración de SO2
(µg/m³) revela un modelo exponencial (λ = 0.069), validado mediante las
pruebas de Pearson y Chi-cuadrado. Con un nivel de confianza del 95%, se
estima que el promedio real de concentración se sitúa en el rango
[13.92, 14.32] µg/m³. Si bien existe una probabilidad del 6.26% de
superar el límite legal de 40 µg/m³, la distribución demuestra que las
concentraciones predominantes son bajas, confirmando una calidad del
aire generalmente estable en la región durante el periodo 2015-2020.