En redes de distribución de agua potable digitalizadas en SIG (QGIS/PostGIS), la apariencia cartográfica no garantiza conectividad topológica adecuada para análisis avanzados. Este estudio piloto se enfocó en transformar una red digitalizada hacia una representación como grafo y evaluar cuantitativamente su estructura por DMA. Se procesaron cinco DMAs (EPSG:32719), convirtiendo nudos y válvulas en nodos y tuberías en aristas, asignando conectividad mediante los extremos geométricos de cada tubería hacia su nodo más cercano con tolerancia de 0.05 m. Con el grafo construido se calcularon métricas topológicas (nodos, aristas, densidad, componentes conexas, porcentaje de nodos con grado 1 y longitud total). Adicionalmente, se aplicó un procedimiento de validación/corrección en el dominio de grafos (A–B) y se utilizó Wilcoxon pareado (α=0.05) como verificación piloto. Se concluye que el enfoque QGIS/PostGIS→Grafo estandariza una validación cuantitativa por DMA y establece una base confiable para la etapa principal del estudio.
Palabras clave: grafos; QGIS; PostGIS; DMA; conectividad; calidad topológica; Wilcoxon.
Los SIG permiten gestionar redes como capas georreferenciadas, pero pequeñas incongruencias geométricas pueden afectar la conectividad analítica sin ser evidentes en el mapa. La teoría de grafos formaliza la conectividad y permite cuantificar propiedades estructurales comparables por DMA. El objetivo de este artículo piloto es validar cuantitativamente la red mediante su conversión a grafo y la evaluación estadística de métricas topológicas, como etapa previa al desarrollo del tema principal.
Objetivo general: Validar cuantitativamente la calidad topológica de redes por DMA mediante su conversión desde QGIS/PostGIS a grafos y el cálculo de métricas, incluyendo estadística descriptiva y validación piloto (Wilcoxon).
Se trabajó con cinco DMAs exportados desde QGIS/PostGIS a GeoPackage. Se construyó un grafo no dirigido por DMA donde los nodos corresponden a nudos y válvulas y las aristas a tuberías. Se consideraron dos estados:
Se calcularon métricas: número de nodos, aristas, densidad, componentes conexas, porcentaje de nodos con grado 1 y longitud total. Se reportó estadística descriptiva y Wilcoxon pareado (α=0.05).
library(readr)
library(dplyr)
library(tidyr)
library(knitr)
metrics_all <- read_csv("data/metricas_5DMA_AB.csv", show_col_types = FALSE)
wide <- read_csv("data/tabla_5DMA_AB_delta.csv", show_col_types = FALSE)
metrics_all
## # A tibble: 10 × 8
## dma estado n_nodos n_aristas densidad n_componentes pct_grado1
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 DMA01 A 543 651 0.00442 2 20.6
## 2 DMA01 B 535 628 0.00440 1 21.3
## 3 DMA02 A 92 100 0.0239 5 31.5
## 4 DMA02 B 88 96 0.0251 1 35.2
## 5 DMA03 A 151 176 0.0155 1 25.2
## 6 DMA03 B 151 174 0.0154 1 26.5
## 7 DMA04 A 118 125 0.0181 1 39.0
## 8 DMA04 B 118 124 0.0180 1 39.8
## 9 DMA05 A 45 47 0.0475 1 42.2
## 10 DMA05 B 45 47 0.0475 1 42.2
## # ℹ 1 more variable: longitud_total_m <dbl>
wide
## # A tibble: 5 × 17
## dma n_nodos_A n_nodos_B n_aristas_A n_aristas_B densidad_A densidad_B
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 DMA01 543 535 651 628 0.00442 0.00440
## 2 DMA02 92 88 100 96 0.0239 0.0251
## 3 DMA03 151 151 176 174 0.0155 0.0154
## 4 DMA04 118 118 125 124 0.0181 0.0180
## 5 DMA05 45 45 47 47 0.0475 0.0475
## # ℹ 10 more variables: n_componentes_A <dbl>, n_componentes_B <dbl>,
## # pct_grado1_A <dbl>, pct_grado1_B <dbl>, longitud_total_m_A <dbl>,
## # longitud_total_m_B <dbl>, d_n_componentes <dbl>, d_pct_grado1 <dbl>,
## # d_densidad <dbl>, d_longitud_m <dbl>
kable(metrics_all, digits = 4,
caption = "Métricas topológicas por DMA en estados A y B (piloto n=5).")
| dma | estado | n_nodos | n_aristas | densidad | n_componentes | pct_grado1 | longitud_total_m |
|---|---|---|---|---|---|---|---|
| DMA01 | A | 543 | 651 | 0.0044 | 2 | 20.6262 | 32109.184 |
| DMA01 | B | 535 | 628 | 0.0044 | 1 | 21.3084 | 32079.499 |
| DMA02 | A | 92 | 100 | 0.0239 | 5 | 31.5217 | 5772.750 |
| DMA02 | B | 88 | 96 | 0.0251 | 1 | 35.2273 | 5765.743 |
| DMA03 | A | 151 | 176 | 0.0155 | 1 | 25.1656 | 9473.718 |
| DMA03 | B | 151 | 174 | 0.0154 | 1 | 26.4901 | 9470.653 |
| DMA04 | A | 118 | 125 | 0.0181 | 1 | 38.9831 | 6578.788 |
| DMA04 | B | 118 | 124 | 0.0180 | 1 | 39.8305 | 6577.888 |
| DMA05 | A | 45 | 47 | 0.0475 | 1 | 42.2222 | 2748.221 |
| DMA05 | B | 45 | 47 | 0.0475 | 1 | 42.2222 | 2748.221 |
kable(wide, digits = 4,
caption = "Diferencias por DMA (Δ = B − A) para variables topológicas.")
| dma | n_nodos_A | n_nodos_B | n_aristas_A | n_aristas_B | densidad_A | densidad_B | n_componentes_A | n_componentes_B | pct_grado1_A | pct_grado1_B | longitud_total_m_A | longitud_total_m_B | d_n_componentes | d_pct_grado1 | d_densidad | d_longitud_m |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| DMA01 | 543 | 535 | 651 | 628 | 0.0044 | 0.0044 | 2 | 1 | 20.6262 | 21.3084 | 32109.184 | 32079.499 | -1 | 0.6823 | 0.0000 | -29.6849 |
| DMA02 | 92 | 88 | 100 | 96 | 0.0239 | 0.0251 | 5 | 1 | 31.5217 | 35.2273 | 5772.750 | 5765.743 | -4 | 3.7055 | 0.0012 | -7.0071 |
| DMA03 | 151 | 151 | 176 | 174 | 0.0155 | 0.0154 | 1 | 1 | 25.1656 | 26.4901 | 9473.718 | 9470.653 | 0 | 1.3245 | -0.0002 | -3.0651 |
| DMA04 | 118 | 118 | 125 | 124 | 0.0181 | 0.0180 | 1 | 1 | 38.9831 | 39.8305 | 6578.788 | 6577.888 | 0 | 0.8475 | -0.0001 | -0.9000 |
| DMA05 | 45 | 45 | 47 | 47 | 0.0475 | 0.0475 | 1 | 1 | 42.2222 | 42.2222 | 2748.221 | 2748.221 | 0 | 0.0000 | 0.0000 | 0.0000 |
resumen <- metrics_all %>%
group_by(estado) %>%
summarise(
med_componentes = median(n_componentes),
iqr_componentes = IQR(n_componentes),
med_pct_grado1 = median(pct_grado1),
iqr_pct_grado1 = IQR(pct_grado1),
med_longitud_m = median(longitud_total_m),
iqr_longitud_m = IQR(longitud_total_m),
.groups = "drop"
)
kable(resumen, digits = 4, caption = "Mediana e IQR por estado (A vs B).")
| estado | med_componentes | iqr_componentes | med_pct_grado1 | iqr_pct_grado1 | med_longitud_m | iqr_longitud_m |
|---|---|---|---|---|---|---|
| A | 1 | 1 | 31.5217 | 13.8175 | 6578.788 | 3700.967 |
| B | 1 | 0 | 35.2273 | 13.3404 | 6577.888 | 3704.910 |
w_comp <- wilcox.test(wide$n_componentes_B, wide$n_componentes_A, paired = TRUE, exact = FALSE)
w_deg1 <- wilcox.test(wide$pct_grado1_B, wide$pct_grado1_A, paired = TRUE, exact = FALSE)
w_len <- wilcox.test(wide$longitud_total_m_B, wide$longitud_total_m_A, paired = TRUE, exact = FALSE)
w_comp
##
## Wilcoxon signed rank test with continuity correction
##
## data: wide$n_componentes_B and wide$n_componentes_A
## V = 0, p-value = 0.3711
## alternative hypothesis: true location shift is not equal to 0
w_deg1
##
## Wilcoxon signed rank test with continuity correction
##
## data: wide$pct_grado1_B and wide$pct_grado1_A
## V = 10, p-value = 0.1003
## alternative hypothesis: true location shift is not equal to 0
w_len
##
## Wilcoxon signed rank test with continuity correction
##
## data: wide$longitud_total_m_B and wide$longitud_total_m_A
## V = 0, p-value = 0.1003
## alternative hypothesis: true location shift is not equal to 0
tests <- data.frame(
variable = c("n_componentes", "pct_grado1", "longitud_total_m"),
V = c(as.numeric(w_comp$statistic), as.numeric(w_deg1$statistic), as.numeric(w_len$statistic)),
p_value = c(w_comp$p.value, w_deg1$p.value, w_len$p.value)
)
kable(tests, digits = 4, caption = "Resultados Wilcoxon pareado (A vs B).")
| variable | V | p_value |
|---|---|---|
| n_componentes | 0 | 0.3711 |
| pct_grado1 | 10 | 0.1003 |
| longitud_total_m | 0 | 0.1003 |
plot(wide$n_componentes_A, wide$n_componentes_B,
xlab = "A: n_componentes", ylab = "B: n_componentes",
main = "Cambio pareado por DMA (A→B)")
abline(0, 1)
text(wide$n_componentes_A, wide$n_componentes_B, labels = wide$dma, pos = 3)
En el piloto se observan mejoras marcadas en conectividad (reducción de componentes) en DMAs con inconsistencias iniciales, mientras que DMAs previamente consistentes permanecen estables. El contraste Wilcoxon no muestra significancia estadística a α=0.05, coherente con el tamaño muestral reducido (n=5) y con la concentración del cambio en algunos DMAs. Estos resultados respaldan el uso del flujo QGIS/PostGIS→Grafo como validación cuantitativa previa, y justifican escalar el análisis a un mayor número de DMAs (p. ej., 10 o 87) para fortalecer la inferencia.
sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Bolivia.utf8 LC_CTYPE=Spanish_Bolivia.utf8
## [3] LC_MONETARY=Spanish_Bolivia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Bolivia.utf8
##
## time zone: America/La_Paz
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.51 tidyr_1.3.2 dplyr_1.1.4 readr_2.1.6
##
## loaded via a namespace (and not attached):
## [1] crayon_1.5.3 vctrs_0.7.1 cli_3.6.5 rlang_1.1.7
## [5] xfun_0.56 purrr_1.2.1 generics_0.1.4 jsonlite_2.0.0
## [9] bit_4.6.0 glue_1.8.0 htmltools_0.5.9 sass_0.4.10
## [13] hms_1.1.4 rmarkdown_2.30 evaluate_1.0.5 jquerylib_0.1.4
## [17] tibble_3.3.1 tzdb_0.5.0 fastmap_1.2.0 yaml_2.3.12
## [21] lifecycle_1.0.5 compiler_4.5.2 pkgconfig_2.0.3 rstudioapi_0.18.0
## [25] digest_0.6.39 R6_2.6.1 utf8_1.2.6 tidyselect_1.2.1
## [29] parallel_4.5.2 vroom_1.7.0 pillar_1.11.1 magrittr_2.0.4
## [33] bslib_0.10.0 bit64_4.6.0-1 tools_4.5.2 cachem_1.1.0