El ruido ambiental es uno de los factores de contaminación más frecuentemente ignorados en entornos urbanos y académicos. Niveles elevados de ruido pueden afectar la concentración, el aprendizaje y la salud de quienes habitan o transitan los espacios de una institución educativa.
Este informe presenta un análisis exploratorio de los niveles de presión sonora (dB) registrados en dos zonas del campus universitario:
Los datos fueron recolectados mediante el micrófono integrado de un teléfono móvil, registrando el nivel de presión sonora en decibeles (dB) con una frecuencia de 1 lectura por segundo, acompañada de coordenadas GPS para cada punto de medición.
Objetivos del análisis:
🔔 Referencia OMS: La Organización Mundial de la Salud recomienda que los niveles de ruido en entornos educativos no superen los 55 dB durante el día para no afectar la salud auditiva ni el rendimiento cognitivo.
Las mediciones se realizaron con el micrófono integrado del teléfono (sensor Phone Microphone, paquete Builtin), que registra el nivel de sonido en decibeles. Cada registro corresponde a una lectura tomada cada segundo a lo largo de un recorrido a pie por cada zona del campus, el día 10 de febrero de 2026.
| Variable | Tipo | Descripción |
|---|---|---|
ObjectID |
Numérica | Identificador único de cada medición |
Session_Name |
Texto | Zona de medición (Ciencias / Licenciatura) |
Timestamp |
Fecha-hora | Fecha y hora exacta de la medición |
Latitude |
Numérica | Latitud GPS del punto |
Longitude |
Numérica | Longitud GPS del punto |
X1.Measurement_Value |
Numérica | Nivel de sonido en decibeles (dB) |
El análisis completo fue desarrollado en R con los siguientes paquetes:
| Paquete | Uso principal |
|---|---|
dplyr, tidyr |
Manipulación y transformación de datos |
ggplot2 |
Visualización estadística |
leaflet |
Mapas interactivos con puntos GPS |
rcartocolor |
Paletas de color CartoCOLORS (TealGrn, PurpOr) |
kableExtra |
Tablas formateadas en HTML |
lubridate |
Manejo de fechas y horas |
scales |
Formateo de ejes y etiquetas |
En R, antes de usar cualquier función especializada es necesario cargar el paquete que la contiene. El siguiente bloque verifica si cada paquete está instalado y, de no estarlo, lo instala automáticamente antes de cargarlo:
Los archivos exportados por la aplicación de medición contienen
8 filas de metadatos del sensor (nombre del
dispositivo, tipo de medición, unidades, etc.) antes de los datos
reales. El parámetro skip = 8 permite omitirlas y leer
directamente la tabla:
ciencias <- read.csv("C:/Users/rizek/Downloads/Rdatos/ciencias.csv", skip = 8)
licenciatura <- read.csv("C:/Users/rizek/Downloads/Rdatos/licenciatura.csv", skip = 8)
cat("Registros cargados - Ciencias: ", nrow(ciencias), "\n")## Registros cargados - Ciencias: 1164
## Registros cargados - Licenciatura: 1194
Para trabajar cómodamente con los datos es necesario: renombrar las
columnas a nombres más claros, convertir el campo de fecha/hora a un
formato que R pueda interpretar con lubridate, y eliminar
filas con valores nulos o incorrectos:
preparar_datos <- function(df, zona) {
df %>%
rename(
id = ObjectID,
sesion = Session_Name,
timestamp = Timestamp,
lat = Latitude,
lon = Longitude,
db = X1.Measurement_Value
) %>%
mutate(
zona = zona,
# ymd_hms() convierte el texto "2026-02-10T15:13:27" a objeto fecha-hora
timestamp = ymd_hms(timestamp),
# Número de segundo dentro del recorrido (útil para la serie de tiempo)
segundo = row_number(),
# Truncar al minuto para promedios agrupados
minuto = floor_date(timestamp, "min")
) %>%
# Eliminar registros con dB nulo o igual a cero (errores del sensor)
filter(!is.na(db), db > 0)
}
ciencias <- preparar_datos(ciencias, "Ciencias")
licenciatura <- preparar_datos(licenciatura, "Licenciatura")
# Combinar ambos datasets en uno solo para análisis comparativos
datos <- bind_rows(ciencias, licenciatura) %>%
mutate(zona = factor(zona, levels = c("Ciencias", "Licenciatura")))
cat("Total de registros combinados:", nrow(datos), "\n")## Total de registros combinados: 2358
| id | sesion | timestamp | lat | lon | db | zona | segundo | minuto |
|---|---|---|---|---|---|---|---|---|
| 1 | Ciencias | 2026-02-10 15:13:27 | 3.375763 | -76.53371 | 63.80528 | Ciencias | 1 | 2026-02-10 15:13:00 |
| 2 | Ciencias | 2026-02-10 15:13:28 | 3.375763 | -76.53371 | 66.61991 | Ciencias | 2 | 2026-02-10 15:13:00 |
| 3 | Ciencias | 2026-02-10 15:13:29 | 3.375763 | -76.53371 | 64.59090 | Ciencias | 3 | 2026-02-10 15:13:00 |
| 4 | Ciencias | 2026-02-10 15:13:30 | 3.375763 | -76.53371 | 63.72378 | Ciencias | 4 | 2026-02-10 15:13:00 |
| 5 | Ciencias | 2026-02-10 15:13:31 | 3.375763 | -76.53371 | 64.49533 | Ciencias | 5 | 2026-02-10 15:13:00 |
| 6 | Ciencias | 2026-02-10 15:13:32 | 3.375763 | -76.53371 | 65.19057 | Ciencias | 6 | 2026-02-10 15:13:00 |
| 7 | Ciencias | 2026-02-10 15:13:33 | 3.375763 | -76.53371 | 65.36375 | Ciencias | 7 | 2026-02-10 15:13:00 |
| 8 | Ciencias | 2026-02-10 15:13:34 | 3.375763 | -76.53371 | 64.64674 | Ciencias | 8 | 2026-02-10 15:13:00 |
| 9 | Ciencias | 2026-02-10 15:13:35 | 3.375656 | -76.53384 | 66.58007 | Ciencias | 9 | 2026-02-10 15:13:00 |
| 10 | Ciencias | 2026-02-10 15:13:36 | 3.375655 | -76.53385 | 65.53665 | Ciencias | 10 | 2026-02-10 15:13:00 |
Las estadísticas descriptivas permiten resumir numéricamente el comportamiento de una variable. Para el nivel de sonido son especialmente relevantes la media (nivel promedio), la desviación estándar (variabilidad) y los percentiles (distribución de los valores).
colores_zona <- c("Ciencias" = COL_CIENCIAS, "Licenciatura" = COL_LICENCIATURA)
resumen <- datos %>%
group_by(zona) %>%
summarise(
N = n(),
Media = round(mean(db), 2),
Mediana = round(median(db), 2),
Minimo = round(min(db), 2),
Maximo = round(max(db), 2),
DS = round(sd(db), 2),
CV_pct = round((sd(db) / mean(db)) * 100, 1),
P25 = round(quantile(db, 0.25), 2),
P75 = round(quantile(db, 0.75), 2),
.groups = "drop"
)
resumen %>%
kable(
caption = "Tabla 1. Estadísticas descriptivas del nivel de sonido (dB) por zona",
col.names = c("Zona", "N", "Media (dB)", "Mediana (dB)", "Min (dB)",
"Max (dB)", "DS (dB)", "CV (%)", "P25", "P75")
) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE) %>%
column_spec(1, bold = TRUE) %>%
row_spec(1, color = COL_CIENCIAS) %>%
row_spec(2, color = COL_LICENCIATURA)| Zona | N | Media (dB) | Mediana (dB) | Min (dB) | Max (dB) | DS (dB) | CV (%) | P25 | P75 |
|---|---|---|---|---|---|---|---|---|---|
| Ciencias | 1164 | 66.09 | 65.69 | 54.08 | 82.64 | 4.98 | 7.5 | 62.29 | 69.51 |
| Licenciatura | 1194 | 70.84 | 70.73 | 57.00 | 85.32 | 4.99 | 7.1 | 67.16 | 74.43 |
Interpretación: La media indica el nivel sonoro típico del recorrido. La desviación estándar (DS) mide cuánto varían las lecturas: una DS alta sugiere un ambiente con picos pronunciados. El coeficiente de variación (CV) relativiza esa variabilidad respecto a la media, permitiendo comparar la homogeneidad del ruido entre zonas.
proporcion <- datos %>%
group_by(zona) %>%
summarise(
Total = n(),
Sobre_umbral = sum(db > UMBRAL_OMS),
Pct_sobre = round(Sobre_umbral / Total * 100, 1),
.groups = "drop"
)
proporcion %>%
kable(
caption = "Tabla 2. Mediciones que superan el umbral OMS de 55 dB",
col.names = c("Zona", "Total mediciones", "Sobre 55 dB", "% sobre umbral")
) %>%
kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
column_spec(4, bold = TRUE,
color = "white",
background = c(COL_CIENCIAS, COL_LICENCIATURA))| Zona | Total mediciones | Sobre 55 dB | % sobre umbral |
|---|---|---|---|
| Ciencias | 1164 | 1162 | 99.8 |
| Licenciatura | 1194 | 1194 | 100.0 |
Interpretación: Un porcentaje alto de mediciones sobre 55 dB indica que el ambiente supera con frecuencia el límite recomendado, lo que puede tener implicaciones para la salud auditiva y el rendimiento académico de quienes habitan esas zonas.
El histograma muestra la frecuencia con que se repite cada rango de valores de dB. Permite identificar si los datos se concentran en valores bajos o altos, y si la distribución es simétrica o sesgada:
ggplot(datos, aes(x = db, fill = zona)) +
geom_histogram(binwidth = 2, color = "white", alpha = 0.85) +
geom_vline(xintercept = UMBRAL_OMS, linetype = "dashed",
color = "#7b2d00", linewidth = 0.8) +
annotate("text", x = UMBRAL_OMS + 0.8, y = Inf,
label = "Limite OMS (55 dB)", vjust = 2, hjust = 0,
color = "#7b2d00", size = 3.5) +
facet_wrap(~zona, ncol = 1) +
scale_fill_manual(values = colores_zona) +
scale_x_continuous(breaks = seq(40, 100, by = 5)) +
labs(
title = "Distribucion de niveles de sonido por zona",
subtitle = "Cada barra representa la cantidad de segundos en ese rango de dB",
x = "Nivel de sonido (dB)",
y = "Frecuencia (segundos)",
caption = "La linea discontinua indica el limite recomendado por la OMS (55 dB)"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(color = "#0d6b6e", face = "bold"),
plot.subtitle = element_text(color = "gray50"),
strip.text = element_text(face = "bold", size = 12),
legend.position = "none"
)La curva de densidad es una versión suavizada del histograma que facilita la comparación visual entre dos distribuciones en el mismo gráfico, mostrando qué valores son más probables en cada zona:
ggplot(datos, aes(x = db, fill = zona, color = zona)) +
geom_density(alpha = 0.35, linewidth = 1) +
geom_vline(xintercept = UMBRAL_OMS, linetype = "dashed",
color = "#7b2d00", linewidth = 0.8) +
annotate("text", x = UMBRAL_OMS + 0.5, y = Inf,
label = "OMS 55 dB", vjust = 2, hjust = 0,
color = "#7b2d00", size = 3.5) +
scale_fill_manual(values = colores_zona) +
scale_color_manual(values = colores_zona) +
labs(
title = "Densidad de probabilidad del nivel sonoro",
subtitle = "Comparacion entre zonas del campus",
x = "Nivel de sonido (dB)",
y = "Densidad",
fill = "Zona", color = "Zona"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(color = "#0d6b6e", face = "bold"),
plot.subtitle = element_text(color = "gray50"),
legend.position = "bottom"
)El boxplot resume la distribución en cinco números clave: mínimo, primer cuartil (P25), mediana, tercer cuartil (P75) y máximo. Los puntos fuera de los bigotes son valores atípicos — momentos de ruido excepcionalmente alto o bajo:
ggplot(datos, aes(x = zona, y = db, fill = zona)) +
geom_boxplot(alpha = 0.75, outlier.color = "gray40",
outlier.size = 1, linewidth = 0.6) +
geom_hline(yintercept = UMBRAL_OMS, linetype = "dashed",
color = "#7b2d00", linewidth = 0.8) +
annotate("text", x = 0.55, y = UMBRAL_OMS + 1.5,
label = "OMS 55 dB", hjust = 0, color = "#7b2d00", size = 3.5) +
scale_fill_manual(values = colores_zona) +
labs(
title = "Distribucion del nivel sonoro por zona",
x = NULL,
y = "Nivel de sonido (dB)"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(color = "#0d6b6e", face = "bold"),
legend.position = "none"
)Interpretación: Una caja desplazada hacia arriba indica que la mayoría de las mediciones fueron altas. Los bigotes largos sugieren alta variabilidad. Los puntos atípicos señalan momentos puntuales de ruido inusual (un vehículo, una conversación cercana, etc.).
La serie de tiempo grafica la evolución del nivel de sonido segundo a segundo durante el recorrido. Permite identificar patrones temporales: ¿el ruido sube al cruzar una calle?, ¿hay momentos de menor intensidad entre edificios?
ggplot(datos, aes(x = segundo, y = db, color = zona)) +
geom_line(alpha = 0.45, linewidth = 0.35) +
geom_smooth(method = "loess", span = 0.15,
se = TRUE, linewidth = 1.2, alpha = 0.2) +
geom_hline(yintercept = UMBRAL_OMS, linetype = "dashed",
color = "#7b2d00", linewidth = 0.7) +
annotate("text", x = 5, y = UMBRAL_OMS + 2,
label = "OMS 55 dB", hjust = 0, color = "#7b2d00", size = 3.2) +
facet_wrap(~zona, ncol = 1, scales = "free_x") +
scale_color_manual(values = colores_zona) +
scale_x_continuous(labels = function(x) paste0(x, "s")) +
labs(
title = "Evolucion del nivel de sonido durante el recorrido",
subtitle = "Linea delgada: lectura real | Banda: tendencia suavizada (LOESS)",
x = "Segundo del recorrido",
y = "Nivel de sonido (dB)"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(color = "#0d6b6e", face = "bold"),
plot.subtitle = element_text(color = "gray50", size = 10),
strip.text = element_text(face = "bold"),
legend.position = "none"
)Interpretación: La línea delgada muestra las fluctuaciones reales de dB segundo a segundo. La banda suavizada (método LOESS) revela la tendencia general del recorrido, eliminando el ruido aleatorio puntual. Los picos sostenidos en el tiempo son especialmente relevantes para la evaluación de exposición al ruido.
El análisis espacial permite visualizar dónde dentro del campus se registraron los niveles más altos de ruido. Cada punto en el mapa corresponde a una medición GPS, coloreado según su nivel en dB. Los mapas son interactivos: puedes hacer zoom y clic sobre cada punto para ver los detalles.
pal_c <- colorNumeric(
palette = rcartocolor::carto_pal(7, "TealGrn"),
domain = ciencias$db
)
leaflet(ciencias) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
lng = ~lon, lat = ~lat,
radius = 5,
fillColor = ~pal_c(db),
color = ~pal_c(db),
fillOpacity = 0.85,
stroke = FALSE,
popup = ~paste0(
"<b>dB:</b> ", round(db, 1), "<br>",
"<b>Hora:</b> ", format(timestamp, "%H:%M:%S"), "<br>",
"<b>Zona:</b> Ciencias"
)
) %>%
addLegend("bottomright", pal = pal_c, values = ~db,
title = "Nivel (dB)", opacity = 0.9)pal_l <- colorNumeric(
palette = rcartocolor::carto_pal(7, "Purp"),
domain = licenciatura$db
)
leaflet(licenciatura) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
lng = ~lon, lat = ~lat,
radius = 5,
fillColor = ~pal_l(db),
color = ~pal_l(db),
fillOpacity = 0.85,
stroke = FALSE,
popup = ~paste0(
"<b>dB:</b> ", round(db, 1), "<br>",
"<b>Hora:</b> ", format(timestamp, "%H:%M:%S"), "<br>",
"<b>Zona:</b> Licenciatura"
)
) %>%
addLegend("bottomright", pal = pal_l, values = ~db,
title = "Nivel (dB)", opacity = 0.9)pal_zonas <- colorFactor(
palette = c(COL_CIENCIAS, COL_LICENCIATURA),
domain = datos$zona
)
leaflet(datos) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addCircleMarkers(
lng = ~lon, lat = ~lat,
radius = 4,
fillColor = ~pal_zonas(zona),
color = ~pal_zonas(zona),
fillOpacity = 0.75,
stroke = FALSE,
popup = ~paste0(
"<b>Zona:</b> ", zona, "<br>",
"<b>dB:</b> ", round(db, 1), "<br>",
"<b>Hora:</b> ", format(timestamp, "%H:%M:%S")
)
) %>%
addLegend("bottomright", pal = pal_zonas, values = ~zona,
title = "Zona", opacity = 0.9)El gráfico de violín combina el boxplot con la curva de densidad: el ancho del violín en cada punto indica la frecuencia relativa de ese nivel de dB. Es especialmente útil para comparar la forma completa de dos distribuciones simultáneamente:
ggplot(datos, aes(x = zona, y = db, fill = zona)) +
geom_violin(trim = FALSE, alpha = 0.7, linewidth = 0.5) +
geom_boxplot(width = 0.08, fill = "white",
outlier.shape = NA, linewidth = 0.6) +
geom_hline(yintercept = UMBRAL_OMS, linetype = "dashed",
color = "#7b2d00", linewidth = 0.8) +
annotate("text", x = 0.55, y = UMBRAL_OMS + 1.5,
label = "OMS 55 dB", hjust = 0, color = "#7b2d00", size = 3.5) +
scale_fill_manual(values = colores_zona) +
labs(
title = "Comparacion de la distribucion sonora entre zonas",
subtitle = "El ancho del violin indica la frecuencia relativa de cada nivel de dB",
x = NULL,
y = "Nivel de sonido (dB)"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(color = "#0d6b6e", face = "bold"),
plot.subtitle = element_text(color = "gray50"),
legend.position = "none"
)Esta gráfica agrega las lecturas por minuto, lo que suaviza las fluctuaciones y permite comparar la tendencia general entre zonas a lo largo del tiempo de medición:
por_minuto <- datos %>%
group_by(zona, minuto) %>%
summarise(media_db = mean(db), .groups = "drop") %>%
group_by(zona) %>%
mutate(min_recorrido = row_number())
ggplot(por_minuto, aes(x = min_recorrido, y = media_db,
color = zona, group = zona)) +
geom_line(linewidth = 1) +
geom_point(size = 2.5, alpha = 0.8) +
geom_hline(yintercept = UMBRAL_OMS, linetype = "dashed",
color = "#7b2d00", linewidth = 0.7) +
scale_color_manual(values = colores_zona) +
labs(
title = "Media de dB por minuto durante el recorrido",
subtitle = "Promedio de todas las lecturas de cada minuto",
x = "Minuto del recorrido",
y = "Media dB",
color = "Zona"
) +
theme_minimal(base_size = 12) +
theme(
plot.title = element_text(color = "#0d6b6e", face = "bold"),
plot.subtitle = element_text(color = "gray50"),
legend.position = "bottom"
)Con base en los análisis realizados, se pueden extraer las siguientes observaciones:
Sobre los niveles generales: Los niveles de sonido registrados en ambas zonas superan frecuentemente el umbral de 55 dB recomendado por la OMS para entornos educativos, lo que sugiere condiciones acústicas que podrían afectar la concentración y el bienestar de estudiantes y docentes.
Sobre las diferencias entre zonas: La comparación estadística y visual entre Ciencias y Licenciatura revela diferencias en los niveles promedio y en la variabilidad del ruido. Estas diferencias pueden atribuirse a factores como el tráfico vehicular cercano, la densidad de personas, la actividad propia de cada facultad, o la configuración arquitectónica de cada zona.
Sobre la variabilidad temporal: La serie de tiempo evidencia que el nivel de ruido no es constante — existen picos asociados posiblemente a eventos puntuales (vehículos, grupos de personas) y momentos de menor intensidad entre edificios o en corredores.
Sobre la distribución espacial: Los mapas interactivos permiten identificar puntos de mayor ruido dentro de cada zona, información valiosa para proponer medidas de mitigación acústica focalizadas en los lugares más críticos.
Este informe aplicó técnicas de analítica de datos ambientales para estudiar el ruido en dos zonas del campus universitario. Las principales conclusiones son:
Como trabajo futuro se recomienda ampliar el período de medición para capturar variaciones a distintas horas del día, incluir más zonas del campus, y correlacionar los niveles de ruido con datos de afluencia estudiantil.
Datos y herramienta de medición
Normativa y salud ambiental
Paquetes de R utilizados
Informe generado con R Markdown · Datos recolectados el 10 de febrero de 2026 · Campus universitario, Colombia