0.- Carga de Librerias
#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
2. CONTEO (FRECUENCIAS)
# ===== CÁLCULOS PARA LA TABLA DE DISTRIBUCIÓN DE FRECUENCIAS (TDF) =====
# Implementación de la fórmula de Sturges
min <- min(NO, na.rm = TRUE)
max <- max(NO, na.rm = TRUE)
R = max - min
k = 1 + (3.3) * log(length(NO))
k <- floor(k)
A <- R / k
# Generación de intervalos
Li <- seq(from = min, to = max - A, by = A)
Ls <- c(seq(from = min + A, to = max - A, by = A), max) # último límite = max
MC <- (Li + Ls) / 2
# Creación de ni (frecuencia absoluta)
ni <- numeric(length(Li))
for (i in 1:length(Li)) {
if (i < length(Li)) {
ni[i] <- sum(NO >= Li[i] & NO < Ls[i], na.rm = TRUE)
} else {
ni[i] <- sum(NO >= Li[i] & NO <= Ls[i], na.rm = TRUE) # Último intervalo cerrado
}
}
# Creación de hi (frecuencia relativa en porcentaje)
N <- sum(ni)
hi <- (ni / N) * 100
# Creación de frecuencias acumuladas
Ni_asc <- cumsum(ni)
Ni_desc <- rev(cumsum(rev(ni)))
Hi_asc <- cumsum(hi)
Hi_desc <- rev(cumsum(rev(hi)))
# Formateo de los intervalos como texto
Intervalo <- paste0("[", round(Li, 2), " - ", round(Ls, 2), ")")
Intervalo[length(Intervalo)] <- paste0("[", round(Li[length(Li)], 2), " - ",
round(Ls[length(Ls)], 2), "]")
3. TABLA DE DISTRIBUCIÓN DE FRECUENCIA
3.1. TABLA GENERAL
# Creación del Data Frame principal
TDF_NO <- data.frame(
Intervalo = Intervalo,
MC = round(MC, 2),
ni = ni,
hi = round(hi, 2),
Ni_asc = Ni_asc,
Ni_desc = Ni_desc,
Hi_asc = round(Hi_asc, 2),
Hi_desc = round(Hi_desc, 2)
)
# Crear fila de totales
totales <- data.frame(
Intervalo = "Totales",
MC = "-",
ni = sum(ni),
hi = sum(hi),
Ni_asc = "-",
Ni_desc = "-",
Hi_asc = "-",
Hi_desc = "-"
)
# Agregar al final del data frame
TDF_NO <- rbind(TDF_NO, totales)
# ===== GENERACIÓN Y ESTILO DE LA TABLA (gt) =====
library(gt)
library(dplyr)
TDF_NO %>%
gt() %>%
# ===== ENCABEZADO =====
tab_header(
title = md("## **Tabla Nro. 1**"),
subtitle = md(
"*Distribución de frecuencia de concentración de NO* *Estudio de calidad del aire en India (2015–2020)*"
)
) %>%
# ===== FUENTE =====
tab_source_note(
source_note = md(
"**Autor:** Grupo 1
**Fuente:** https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india"
)
) %>%
# ===== ESTILO GENERAL =====
opt_table_font(
font = list(
google_font("Poppins"),
default_fonts()
)
) %>%
tab_options(
# Bordes generales
table.border.top.color = "black",
table.border.bottom.color = "black",
table.border.top.width = px(3),
table.border.bottom.width = px(3),
# Encabezados
heading.align = "center",
heading.title.font.size = px(22),
heading.subtitle.font.size = px(14),
heading.background.color = "#F4F6F7",
# Columnas
column_labels.font.weight = "bold",
column_labels.font.size = px(14),
column_labels.background.color = "#D6EAF8",
column_labels.border.top.color = "black",
column_labels.border.bottom.color = "black",
column_labels.border.bottom.width = px(2),
# Filas
table_body.hlines.color = "#B3B6B7",
table_body.vlines.color = "#B3B6B7", # ← líneas verticales
table_body.border.bottom.color = "black",
# Colores alternados
row.striping.include_table_body = TRUE,
row.striping.background_color = "#F8F9F9",
# Fuente
table.font.size = px(13),
# Notas
source_notes.font.size = px(11),
source_notes.background.color = "#F4F6F7",
# Espaciado
data_row.padding = px(8)
) %>%
# ===== BORDES VERTICALES COMPLETOS =====
tab_style(
style = cell_borders(
sides = c("left", "right"),
color = "#A6ACAF",
weight = px(1)
),
locations = cells_body()
) %>%
tab_style(
style = cell_borders(
sides = c("left", "right"),
color = "black",
weight = px(1.5)
),
locations = cells_column_labels()
) %>%
# ===== ALINEACIÓN =====
cols_align(
align = "center",
columns = everything()
)
Tabla Nro. 1
|
| Distribución de frecuencia de concentración de NO Estudio de calidad del aire en India (2015–2020) |
| Intervalo |
MC |
ni |
hi |
Ni_asc |
Ni_desc |
Hi_asc |
Hi_desc |
| [0.02 - 11.51) |
5.76 |
14740 |
56.80 |
14740 |
25949 |
56.8 |
100 |
| [11.51 - 23) |
17.25 |
5622 |
21.67 |
20362 |
11209 |
78.47 |
43.2 |
| [23 - 34.49) |
28.75 |
2369 |
9.13 |
22731 |
5587 |
87.6 |
21.53 |
| [34.49 - 45.98) |
40.24 |
1115 |
4.30 |
23846 |
3218 |
91.9 |
12.4 |
| [45.98 - 57.47) |
51.73 |
637 |
2.45 |
24483 |
2103 |
94.35 |
8.1 |
| [57.47 - 68.96) |
63.22 |
445 |
1.71 |
24928 |
1466 |
96.07 |
5.65 |
| [68.96 - 80.45) |
74.7 |
302 |
1.16 |
25230 |
1021 |
97.23 |
3.93 |
| [80.45 - 91.94) |
86.2 |
223 |
0.86 |
25453 |
719 |
98.09 |
2.77 |
| [91.94 - 103.43) |
97.68 |
138 |
0.53 |
25591 |
496 |
98.62 |
1.91 |
| [103.43 - 114.92) |
109.17 |
93 |
0.36 |
25684 |
358 |
98.98 |
1.38 |
| [114.92 - 126.41) |
120.67 |
82 |
0.32 |
25766 |
265 |
99.29 |
1.02 |
| [126.41 - 137.9) |
132.16 |
59 |
0.23 |
25825 |
183 |
99.52 |
0.71 |
| [137.9 - 149.39) |
143.64 |
31 |
0.12 |
25856 |
124 |
99.64 |
0.48 |
| [149.39 - 160.88) |
155.14 |
33 |
0.13 |
25889 |
93 |
99.77 |
0.36 |
| [160.88 - 172.37) |
166.62 |
14 |
0.05 |
25903 |
60 |
99.82 |
0.23 |
| [172.37 - 183.86) |
178.12 |
11 |
0.04 |
25914 |
46 |
99.87 |
0.18 |
| [183.86 - 195.35) |
189.61 |
6 |
0.02 |
25920 |
35 |
99.89 |
0.13 |
| [195.35 - 206.84) |
201.1 |
8 |
0.03 |
25928 |
29 |
99.92 |
0.11 |
| [206.84 - 218.33) |
212.58 |
4 |
0.02 |
25932 |
21 |
99.93 |
0.08 |
| [218.33 - 229.82) |
224.07 |
5 |
0.02 |
25937 |
17 |
99.95 |
0.07 |
| [229.82 - 241.31) |
235.56 |
0 |
0.00 |
25937 |
12 |
99.95 |
0.05 |
| [241.31 - 252.8) |
247.06 |
1 |
0.00 |
25938 |
12 |
99.96 |
0.05 |
| [252.8 - 264.29) |
258.54 |
0 |
0.00 |
25938 |
11 |
99.96 |
0.04 |
| [264.29 - 275.78) |
270.03 |
4 |
0.02 |
25942 |
11 |
99.97 |
0.04 |
| [275.78 - 287.27) |
281.52 |
1 |
0.00 |
25943 |
7 |
99.98 |
0.03 |
| [287.27 - 298.76) |
293.02 |
2 |
0.01 |
25945 |
6 |
99.98 |
0.02 |
| [298.76 - 310.25) |
304.5 |
1 |
0.00 |
25946 |
4 |
99.99 |
0.02 |
| [310.25 - 321.74) |
316 |
0 |
0.00 |
25946 |
3 |
99.99 |
0.01 |
| [321.74 - 333.23) |
327.48 |
0 |
0.00 |
25946 |
3 |
99.99 |
0.01 |
| [333.23 - 344.72) |
338.97 |
0 |
0.00 |
25946 |
3 |
99.99 |
0.01 |
| [344.72 - 356.21) |
350.46 |
1 |
0.00 |
25947 |
3 |
99.99 |
0.01 |
| [356.21 - 367.7) |
361.96 |
0 |
0.00 |
25947 |
2 |
99.99 |
0.01 |
| [367.7 - 379.19) |
373.44 |
0 |
0.00 |
25947 |
2 |
99.99 |
0.01 |
| [379.19 - 390.68] |
384.94 |
2 |
0.01 |
25949 |
2 |
100 |
0.01 |
| Totales |
- |
25949 |
100.00 |
- |
- |
- |
- |
Autor: Grupo 1
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india |
3.2. TABLA SIMPLIFICADA
# ===== Proceso de simplificación de intervalos =====
# Forzamos a que existan 10 filas exactas
k_simpl <- 10
# Calculamos la nueva amplitud redondeada hacia arriba para no perder datos
A_simpl <- ceiling(R / k_simpl)
# Generación de los nuevos intervalos (usamos el 'min' original de la variable NO)
Li_simpl <- seq(from = floor(min), by = A_simpl, length.out = k_simpl)
Ls_simpl <- seq(from = floor(min) + A_simpl, by = A_simpl, length.out = k_simpl)
MC_simpl <- (Li_simpl + Ls_simpl) / 2
# Creación de ni (frecuencia absoluta) para la tabla 2 de NO
ni_simpl <- numeric(length(Li_simpl))
for (i in 1:length(Li_simpl)) {
if (i < length(Li_simpl)) {
ni_simpl[i] <- sum(NO >= Li_simpl[i] & NO < Ls_simpl[i], na.rm = TRUE)
} else {
ni_simpl[i] <- sum(NO >= Li_simpl[i] & NO <= Ls_simpl[i], na.rm = TRUE)
}
}
# Creación de hi (frecuencia relativa) para la tabla 2
N_simpl <- sum(ni_simpl)
hi_simpl <- (ni_simpl / N_simpl) * 100
# Creación de frecuencias acumuladas para la tabla 2
Ni_asc_simpl <- cumsum(ni_simpl)
Ni_desc_simpl <- rev(cumsum(rev(ni_simpl)))
Hi_asc_simpl <- cumsum(hi_simpl)
Hi_desc_simpl <- rev(cumsum(rev(hi_simpl)))
# Formateo de los intervalos como texto (sin decimales)
Intervalo_simpl <- paste0("[", Li_simpl, " - ", Ls_simpl, ")")
Intervalo_simpl[length(Intervalo_simpl)] <- paste0("[", Li_simpl[length(Li_simpl)], " - ", Ls_simpl[length(Ls_simpl)], "]")
# Creación del Data Frame para la tabla simplificada de NO
TDF_NO_simpl <- data.frame(
Intervalo = Intervalo_simpl,
MC = MC_simpl,
ni = ni_simpl,
hi = round(hi_simpl, 2),
Ni_asc = Ni_asc_simpl,
Ni_desc = Ni_desc_simpl,
Hi_asc = round(Hi_asc_simpl, 2),
Hi_desc = round(Hi_desc_simpl, 2)
)
# Crear fila de totales
totales_simpl <- data.frame(
Intervalo = "Totales",
MC = "-",
ni = sum(ni_simpl),
hi = sum(hi_simpl),
Ni_asc = "-",
Ni_desc = "-",
Hi_asc = "-",
Hi_desc = "-"
)
# Unir la fila de totales a la nueva tabla
TDF_NO_simpl <- rbind(TDF_NO_simpl, totales_simpl)
# ===== GENERACIÓN Y ESTILO DE LA TABLA NRO 2 (gt) =====
TDF_NO_simpl %>%
gt() %>%
# ===== ENCABEZADO =====
tab_header(
title = md("## **Tabla Nro. 2**"),
subtitle = md(
"*Distribución de frecuencia simplificada de concentración de NO, estudio calidad del aire en India entre 2015-2020*"
)
) %>%
# ===== FUENTE =====
tab_source_note(
source_note = md(
"**Autor:** Grupo 1
**Fuente:** https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india"
)
) %>%
# ===== ESTILO GENERAL =====
opt_table_font(
font = list(
google_font("Poppins"),
default_fonts()
)
) %>%
tab_options(
table.border.top.color = "black",
table.border.bottom.color = "black",
table.border.top.width = px(3),
table.border.bottom.width = px(3),
heading.align = "center",
heading.title.font.size = px(22),
heading.subtitle.font.size = px(14),
heading.background.color = "#F4F6F7",
column_labels.font.weight = "bold",
column_labels.font.size = px(14),
column_labels.background.color = "#D6EAF8",
column_labels.border.top.color = "black",
column_labels.border.bottom.color = "black",
column_labels.border.bottom.width = px(2),
table_body.hlines.color = "#B3B6B7",
table_body.vlines.color = "#B3B6B7",
table_body.border.bottom.color = "black",
row.striping.include_table_body = TRUE,
row.striping.background_color = "#F8F9F9",
table.font.size = px(13),
source_notes.font.size = px(11),
source_notes.background.color = "#F4F6F7",
data_row.padding = px(8)
) %>%
# ===== BORDES VERTICALES COMPLETOS =====
tab_style(
style = cell_borders(
sides = c("left", "right"),
color = "#A6ACAF",
weight = px(1)
),
locations = cells_body()
) %>%
tab_style(
style = cell_borders(
sides = c("left", "right"),
color = "black",
weight = px(1.5)
),
locations = cells_column_labels()
) %>%
# ===== ALINEACIÓN =====
cols_align(
align = "center",
columns = everything()
)
Tabla Nro. 2
|
| Distribución de frecuencia simplificada de concentración de NO, estudio calidad del aire en India entre 2015-2020 |
| Intervalo |
MC |
ni |
hi |
Ni_asc |
Ni_desc |
Hi_asc |
Hi_desc |
| [0 - 40) |
20 |
23347 |
89.97 |
23347 |
25949 |
89.97 |
100 |
| [40 - 80) |
60 |
1867 |
7.19 |
25214 |
2602 |
97.17 |
10.03 |
| [80 - 120) |
100 |
506 |
1.95 |
25720 |
735 |
99.12 |
2.83 |
| [120 - 160) |
140 |
169 |
0.65 |
25889 |
229 |
99.77 |
0.88 |
| [160 - 200) |
180 |
37 |
0.14 |
25926 |
60 |
99.91 |
0.23 |
| [200 - 240) |
220 |
11 |
0.04 |
25937 |
23 |
99.95 |
0.09 |
| [240 - 280) |
260 |
5 |
0.02 |
25942 |
12 |
99.97 |
0.05 |
| [280 - 320) |
300 |
4 |
0.02 |
25946 |
7 |
99.99 |
0.03 |
| [320 - 360) |
340 |
1 |
0.00 |
25947 |
3 |
99.99 |
0.01 |
| [360 - 400] |
380 |
2 |
0.01 |
25949 |
2 |
100 |
0.01 |
| Totales |
- |
25949 |
100.00 |
- |
- |
- |
- |
Autor: Grupo 1
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india |
4. HISTOGRAMA
4.1. HISTOGRAMA LOCAL
# ===== GRÁFICA LOCAL (Eje Y automático generado por R - NO) =========
# Ajustamos márgenes para que los números del eje X rotados entren perfectamente
par(mar = c(6, 5, 4, 2) + 0.1)
# Aseguramos tener los cortes de los 10 intervalos simplificados listos para la variable NO
cortes_exactos <- c(Li_simpl, Ls_simpl[length(Ls_simpl)])
# Dibujamos el histograma
hist(NO,
breaks = cortes_exactos,
main = "Gráfica Nro 1: Distribución de la Concentración de NO\nEstudio sobre calidad del aire en India (2015-2020)",
xlab = "", # Lo dejamos vacío para colocarlo más abajo y que no choque
ylab = "Días",
col = "purple",
border = "black",
xaxt = "n", # Apagamos el eje X automático
freq = TRUE,
panel.first = grid(col = "lightgray", lty = 2) # Cuadrícula de fondo
)
## Warning in plot.window(xlim, ylim, log, ...): "panel.first" es un parámetro
## gráfico inválido
## Warning in title(main = main, sub = sub, xlab = xlab, ylab = ylab, ...):
## "panel.first" es un parámetro gráfico inválido
## Warning in axis(1, ...): "panel.first" es un parámetro gráfico inválido
## Warning in axis(2, at = yt, ...): "panel.first" es un parámetro gráfico
## inválido
# Dibujamos el eje X con los valores rotados a 90 grados (verticales)
axis(1,
at = cortes_exactos,
labels = cortes_exactos,
las = 2, # las = 2 rota las etiquetas verticalmente
cex.axis = 0.8)
# Colocamos el título del eje X manualmente más abajo
mtext("Concentración de NO (µg/m3)", side = 1, line = 4.5, font = 1)

4.2. HISTOGRAMA GLOBAL
# ====================================================================
# ===== GRÁFICA NRO 2: HISTOGRAMA (Con límite Y personalizado - NO) ===
# ====================================================================
# Ajustamos márgenes para dar espacio al texto vertical del eje X
par(mar = c(6, 5, 4, 2) + 0.1)
# Aseguramos tener los cortes de los 10 intervalos listos para la variable NO
cortes_exactos <- c(Li_simpl, Ls_simpl[length(Ls_simpl)])
# CALCULAMOS EL TECHO DEL EJE Y AUTOMÁTICAMENTE PARA EL NO
# Buscamos la frecuencia más alta y la redondeamos un poco hacia arriba para que respire la gráfica
max_frecuencia <- max(ni_simpl, na.rm = TRUE)
techo_y <- ceiling(max_frecuencia / 5000) * 5000 # Redondea al múltiplo de 5000 superior
# Dibujamos el histograma
hist(NO,
breaks = cortes_exactos,
main = "Gráfica Nro 2: Distribución de la Concentración de NO\nEstudio sobre calidad del aire en India (2015-2020)",
xlab = "", # Vacío para colocarlo manualmente abajo
ylab = "Días",
ylim = c(0, techo_y), # Techo visual adaptado dinámicamente para NO
col = "lightblue",
border = "black",
xaxt = "n",
yaxt = "n", # Apagamos el eje Y automático
freq = TRUE,
panel.first = grid(col = "lightgray", lty = 2) # Cuadrícula de fondo
)
## Warning in plot.window(xlim, ylim, log, ...): "panel.first" es un parámetro
## gráfico inválido
## Warning in title(main = main, sub = sub, xlab = xlab, ylab = ylab, ...):
## "panel.first" es un parámetro gráfico inválido
## Warning in axis(1, ...): "panel.first" es un parámetro gráfico inválido
## Warning in axis(2, at = yt, ...): "panel.first" es un parámetro gráfico
## inválido
# Dibujamos nuestro eje X rotado a 90 grados (vertical)
axis(1,
at = cortes_exactos,
labels = cortes_exactos,
las = 2, # las = 2 gira las etiquetas verticalmente
cex.axis = 0.8)
# Colocamos el título del eje X manualmente más abajo para que no choque con los números
mtext("Concentración de NO (µg/m3)", side = 1, line = 4.5, font = 1)
# Eje Y manual con saltos cada 5,000 unidades hasta el techo calculado
marcas_y <- seq(0, techo_y, by = 5000)
axis(2,
at = marcas_y,
labels = marcas_y,
las = 1,
cex.axis = 0.8)

4.3. HISTOGRAMA LOCAL PORCENTUAL
# ====================================================================
# ===== GRÁFICA NRO 3: DISTRIBUCIÓN PORCENTUAL (NO - FIJADO A 100%) ==
# ====================================================================
# Ajustamos los márgenes ampliamente
par(mar = c(6, 5, 4, 2) + 0.1)
# Aseguramos tener los cortes listos
cortes_exactos <- c(Li_simpl, Ls_simpl[length(Ls_simpl)])
# Generamos el diagrama (Forzamos el ylim exactamente a 100)
posiciones <- barplot(hi_simpl,
main = "Gráfica Nro 3: Distribución Porcentual de NO\nEstudio sobre calidad del aire en India (2015-2020)",
ylab = "Días (%)",
xlab = "",
ylim = c(0, 100), # ← CORRECCIÓN: El techo visual ahora es 100%
col = "#A9DFBF",
border = "black",
space = 0,
xaxt = "n",
yaxt = "n",
las = 1)
# Creamos la cuadrícula de fondo adaptada al nuevo techo de 100
grid(nx = NA, ny = NULL, col = "lightgray", lty = 2)
# Dibujamos la "regleta" exacta (el eje X) rotada a 90 grados
axis(1,
at = 0:k_simpl,
labels = cortes_exactos,
las = 2,
cex.axis = 0.8)
# Colocamos el título del eje X manualmente
mtext("Concentración de NO (µg/m3)", side = 1, line = 4.5, font = 1)
# CORRECCIÓN: Eje Y manual con saltos exactos de 20% en 20% hasta el 100%
marcas_y_porc <- seq(0, 100, by = 20)
axis(2,
at = marcas_y_porc,
labels = paste0(marcas_y_porc, "%"),
las = 1,
cex.axis = 0.8)

4.4. HISTOGRAMA GLOBAL PROCENTUAL
# ====================================================================
# ===== GRÁFICA NRO 4: DISTRIBUCIÓN PORCENTUAL GLOBAL (NO) - AJUSTADA
# ====================================================================
par(mar = c(6, 5, 4, 2) + 0.1)
cortes_exactos <- c(Li_simpl, Ls_simpl[length(Ls_simpl)])
total_registros <- sum(ni_simpl)
techo_global_ficticio <- total_registros * 1.11
# Definimos las posiciones Y (basadas en datos reales) y etiquetas (porcentajes)
marcas_y_proporcionales <- seq(0, total_registros, length.out = 6)
etiquetas_y_porc <- c("0%", "20%", "40%", "60%", "80%", "100%")
# Generamos el barplot
posiciones_global <- barplot(ni_simpl,
main = "Gráfica Nro 4: Distribución Porcentual Global de NO\nEstudio sobre calidad del aire en India (2015-2020)",
ylab = "Días (%)",
xlab = "",
ylim = c(0, techo_global_ficticio),
col = "lightblue", border = "black",
space = 0, xaxt = "n", yaxt = "n", las = 1)
# DIBUJAMOS LA CUADRÍCULA MANUALMENTE PARA QUE COINCIDA CON EL EJE Y
# Esto dibuja una línea gris horizontal en cada marca de porcentaje
for(y in marcas_y_proporcionales) {
abline(h = y, col = "lightgray", lty = 2)
}
# Dibujamos los ejes
axis(1, at = 0:k_simpl, labels = cortes_exactos, las = 2, cex.axis = 0.8)
mtext("Concentración de NO (µg/m3)", side = 1, line = 4.5, font = 1)
axis(2, at = marcas_y_proporcionales, labels = etiquetas_y_porc, las = 1, cex.axis = 0.8)

6. OJIVAS
6.1. OJIVAS (NI)
# ====================================================================
# ===== GRÁFICA NRO 6: OJIVAS LOCALES (Diseño espaciado - NO) ========
# ====================================================================
# Ampliamos el margen izquierdo para que los números grandes no choquen
par(mar = c(5, 5.5, 4, 2) + 0.1)
# Usamos los límites superiores de los intervalos simplificados de NO
x <- Ls_simpl
# Dibujamos la primera gráfica (Ojiva Ascendente - Naranja)
plot(
x, Ni_asc_simpl,
type = "b",
col = "orange",
pch = 19,
main = "Gráfica Nro 6: Distribución Acumulada (Ojivas) de NO\nEstudio calidad del aire en India (2015-2020)",
xlab = "Concentración de NO (µg/m3)", # Unidad actualizada
ylab = "Cantidad Acumulada (Días)",
ylim = c(0, N_simpl),
las = 1,
cex.main = 0.9,
yaxt = "n" # Apagamos el eje Y automático
)
# Superponemos la segunda línea (Ojiva Descendente - Azul)
lines(
x, Ni_desc_simpl,
type = "b",
col = "blue",
pch = 19
)
# Dibujamos un eje Y limpio y automático
# pretty() calculará marcas perfectas según tu N_simpl de NO
marcas_y_ojiva <- pretty(c(0, N_simpl))
axis(2,
at = marcas_y_ojiva,
labels = marcas_y_ojiva,
las = 1,
cex.axis = 0.8)
# Agregamos la leyenda
legend("right",
legend = c("Ascendente (Ojiva Menor que)", "Descendente (Ojiva Mayor que)"),
col = c("orange", "blue"),
pch = 19,
lty = 1,
bty = "n",
cex = 0.8)

6.2. OJIVAS (HI)
# ====================================================================
# ===== GRÁFICA NRO 7: OJIVAS PORCENTUALES (Frecuencias Relativas - NO)
# ====================================================================
# Ajustamos márgenes (abajo 6, izquierda 5)
par(mar = c(6, 5, 4, 2) + 0.1)
# Usamos los límites superiores de los intervalos simplificados
x <- Ls_simpl
# Dibujamos la primera gráfica (Ojiva Ascendente Porcentual - Naranja)
plot(
x, Hi_asc_simpl, # Usamos frecuencias relativas porcentuales (Hi)
type = "b",
col = "orange",
pch = 19,
lwd = 2,
main = "Gráfica Nro 7: Ojiva Porcentual Local de Concentración de NO\nEstudio calidad del aire en India (2015-2020)",
xlab = "", # Vacío para colocarlo manualmente
ylab = "Porcentaje Días (%)",
ylim = c(0, 100), # Techo en 100%
xaxt = "n", # Apagamos ejes automáticos
yaxt = "n",
panel.first = grid(col = "lightgray", lty = 2) # Cuadrícula de fondo
)
# Superponemos la segunda línea (Ojiva Descendente Porcentual - Azul)
lines(
x, Hi_desc_simpl, # Versión descendente de Hi
type = "b",
col = "blue",
pch = 19,
lwd = 2
)
# EJE X ROTADO A 90 GRADOS (Vertical)
axis(1,
at = x,
labels = x,
las = 2, # Texto vertical
cex.axis = 0.9)
# Colocamos el título del eje X manualmente
mtext("Concentración de NO (µg/m3)", side = 1, line = 4.5, font = 1)
# EJE Y LIMPIO CON SÍMBOLO % (Saltos de 20% en 20%)
marcas_y_porc_ojiva <- seq(0, 100, by = 20)
axis(2,
at = marcas_y_porc_ojiva,
labels = paste0(marcas_y_porc_ojiva, "%"),
las = 1,
cex.axis = 0.9)
# LEYENDA (Estilo limpio sin recuadro)
legend("right", # Ubicado a la derecha para no tapar el cruce de las ojivas
legend = c("Ascendente", "Descendente"),
col = c("orange", "blue"),
pch = 19,
lty = 1,
lwd = 2,
bty = "n", # Sin contorno
cex = 0.9)

7. INDICADORES ESTADÍSTICOS
# ====================================================================
# ===== INDICADORES ESTADÍSTICOS Y TABLA RESUMEN (NO) ================
# ====================================================================
# Cargar librerías necesarias
library(e1071)
library(gt)
library(dplyr)
# --- Indicadores de Tendencia Central ---
X <- mean(NO, na.rm = TRUE)
Me <- median(NO, na.rm = TRUE)
# Para la Moda en datos agrupados, usamos el intervalo con mayor frecuencia de la variable NO
Mo <- Intervalo_simpl[which.max(ni_simpl)]
# --- Indicadores de Dispersión ---
var_no <- var(NO, na.rm = TRUE)
desv <- sd(NO, na.rm = TRUE)
CV <- (desv / X) * 100
# --- Indicadores de Forma ---
As <- skewness(NO, na.rm = TRUE)
K <- kurtosis(NO, na.rm = TRUE)
# --- Datos dinámicos para la tabla ---
Variable <- "NO"
# Rango exacto (Utiliza los valores Min y Max calculados para el NO)
min_no <- min(NO, na.rm = TRUE)
max_no <- max(NO, na.rm = TRUE)
Rango <- paste0("[", round(min_no, 2), " - ", round(max_no, 2), "]")
# Identificación automática de valores atípicos
atipicos_reales <- boxplot.stats(NO)$out
valoresatipicos <- paste0(length(atipicos_reales), " atípicos (>", round(min(atipicos_reales), 2), ")")
# ===== CREACIÓN DEL DATA FRAME =====
Tabla_indicadores_NO <- data.frame(
Variable = Variable,
Rango = Rango,
X = round(X, 3),
Me = round(Me, 2),
Mo = Mo,
sd = round(desv, 2),
CV = round(CV, 2),
As = round(As, 2),
K = round(K, 2),
valoresatipicos = valoresatipicos
)
colnames(Tabla_indicadores_NO) <- c("Variable", "Rango", "X", "Me", "Mo", "sd", "CV", "As", "K", "Valores atípicos")
# ===== GENERACIÓN Y ESTILO DE LA TABLA NRO 3 (gt) =====
Tabla_indicadores_NO %>%
gt() %>%
# ===== ENCABEZADO =====
tab_header(
title = md("## **Tabla Nro. 3**"),
subtitle = md("*Indicadores Estadísticos de concentración de NO, estudio calidad del aire en India entre 2015-2020*")
) %>%
# ===== FUENTE =====
tab_source_note(
source_note = md("**Autor:** Grupo 1\n**Fuente:** https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india")
) %>%
# ===== ESTILO GENERAL =====
opt_table_font(
font = list(
google_font("Poppins"),
default_fonts()
)
) %>%
tab_options(
table.border.top.color = "black",
table.border.bottom.color = "black",
table.border.top.width = px(3),
table.border.bottom.width = px(3),
heading.align = "center",
heading.background.color = "#F4F6F7",
column_labels.font.weight = "bold",
column_labels.background.color = "#D6EAF8",
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,
row.striping.background_color = "#F8F9F9",
table_body.hlines.color = "#B3B6B7",
table_body.border.bottom.color = "black"
) %>%
cols_align(
align = "center",
columns = everything()
)
Tabla Nro. 3
|
| Indicadores Estadísticos de concentración de NO, estudio calidad del aire en India entre 2015-2020 |
| Variable |
Rango |
X |
Me |
Mo |
sd |
CV |
As |
K |
Valores atípicos |
| NO |
[0.02 - 390.68] |
17.575 |
9.89 |
[0 - 40) |
22.79 |
129.65 |
3.88 |
25.16 |
2459 atípicos (>41.44) |
| Autor: Grupo 1
Fuente: https://www.kaggle.com/datasets/rohanrao/air-quality-data-in-india |