Nota: El sitio del Datos Abiertos mencionado, aparentemente ya no existe a la fecha (2017/04/15)
Este es un ejemplo nada elaborado del uso del servicio de datos abiertos disponibles por parte del municipio de Lima Metropolitana, en el cual ponemos en un mapa los paraderos de corredor TGA (Tacna-Garcilazo-Armedáriz) en un par de formas distintas.
El servicio en cuestión usa Junar como plataforma. Existen
librerías para varios lenguajes de programación, pero no para R; pero felizmente
el API es basado en REST y la respuesta de las llamadas es un arreglo en JSON, de manera
que es factible consumir los datos usando la librería jsonlite
.
Obteniendo y procesando los datos
Vamos a usar el API del Portal de Datos Abiertos de Lima Metropolitana.
Primero: Definimos un par de funciones que podamos reusar
library(jsonlite)
# Pasamos el URL de API endpoint, y la API developer key
# por defecto sólo retorna los datos, sin metadata
get_data <- function (api_url, api_key, include_metadata=FALSE) {
api_data <- fromJSON(paste0(api_url, "?auth_key=", api_key))
data <- extract_dataframe(api_data)
if (include_metadata==FALSE) {
return(data)
} else {
metadata <- api_data[c("id", "title", "description", "user",
"tags", "created_at", "source", "link")]
metadata["timestamp"] <- as.Date(api_data$result$fTimestamp,
origin = "1970-01-01")
return(list(metadata=metadata, data=data))
}
}
# Hace el trabajo de convertir los datos de un formato secuencial
# a una data.frame de R
extract_dataframe <- function(api_data) {
header <- subset(api_data$result$fArray, fHeader==TRUE)$fStr
values <- subset(api_data$result$fArray, is.na(fHeader))$fStr
header_len <- api_data$result$fCols
df <- as.data.frame(
matrix(values,
ncol=header_len,
byrow = TRUE),
stringsAsFactors=FALSE)
colnames(df) <- header
return(df)
}
Segundo: Conseguimos los datos de la geolocalización de los paraderos del corredor TGA troncal en Lima.
# cargando las librerías necesarias
require(jsonlite, quietly=TRUE)
source("api_key.R")
# formato: api_key <- "YOUR API KEY"
api_url <- "http://api.lima.datosabiertos.pe/datastreams/invoke/PARAD-GEO-REFER-DE-LA"
troncal_fulldata <- get_data(api_url, api_key, TRUE)
troncal <- troncal_fulldata$data
Tercero: Ahora los datos de las rutas alimentadoras
api_url <- "http://api.lima.datosabiertos.pe/datastreams/invoke/PARAD-GEO-REFER-DEL-69193"
alimentadora_fulldata <- get_data(api_url, api_key, TRUE)
alimentadora <- alimentadora_fulldata$data
Cuarto: Combinamos las dos data frames y ajustamos los tipos de algunas columnas
stops <- rbind(troncal,alimentadora)
# corregir los tipos de datos de algunas columnas
stops$LONGITUD <- as.numeric(stops$LONGITUD)
stops$LATITUD <- as.numeric(stops$LATITUD)
stops$CORREDOR <- as.factor(stops$CORREDOR)
stops$TAP <- as.factor(stops$TAP)
Un mapa simple, no interactivo
Usamos el paquete ggmap
para generar un mapa estático (formato PNG).
require(ggmap, quietly=TRUE)
# usamos el tipo "toner" de stamen
lima_tga <- get_map(c(lon=mean(stops$LONGITUD), lat=mean(stops$LATITUD)),
source="stamen", maptype="toner", zoom=12)
tga_map <- ggmap(lima_tga, extent="device")
tga_map + geom_point(data=stops, size=2,
aes(x=LONGITUD, y=LATITUD, col=TAP))
Un mapa interactivo
Y usando el paquete googleVis
, incrustamos un mapa interactivo
usando el API de Google Maps.
require(googleVis, quietly=TRUE)
rutas <- as.character(stops$TAP)
d2 <- data.frame("latlong"=paste(stops$LATITUD, stops$LONGITUD, sep=":"),
"tip" = paste(paste0("<b>Corredor: TGA - Ruta: ",
rutas, "</b>"),
stops$NOMBRE, sep="<br/>"))
# usemos íconos marcadores de http://www.icons-land.com/
mapmarkers <- "{default: {
normal: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/32/Map-Marker-Flag-4-Right-Azure-icon.png',
selected: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/32/Map-Marker-Flag-4-Right-Chartreuse-icon.png'
}}"
tga_map2 <- gvisMap(d2, locationvar="latlong", tipvar="tip",
options=list(
showTip=TRUE,
enableScrollWheel=TRUE,
mapType='normal',
useMapTypeControl=TRUE,
width=600, height=600,
icons=mapmarkers))
print(tga_map2, "chart")
Problemas encontrados
Durante la exploración del API de Datos Abiertos de Lima Metropolitana, encontré algunos problemas, mayormente subsanables.
A la fecha de publicación, varios URLs generan 404, por ejemplo:
- http://lima.datosabiertos.pe/visualizations/5612/informacion-epidemiologica-desde-2004/
- http://lima.datosabiertos.pe/datastreams/75006/informacion-epidemiologica-desde-2004/
- http://lima.datosabiertos.pe/datastreams/79479/control-nutricional-2013/
- http://lima.datosabiertos.pe/datastreams/75983/vista-certificados-defensa-civil
Varias colecciones llevan a páginas sin listado de datos:
- http://lima.datosabiertos.pe/dashboards/7900/presupuesto-de-ingresos-y-gastos-mml/
- http://lima.datosabiertos.pe/dashboards/7928/proyectos-de-inversion-publica-mml/
- http://lima.datosabiertos.pe/dashboards/8088/transporte-urbano/
- http://lima.datosabiertos.pe/dashboards/7914/salud/
En al menos un caso, no han anonimizado los datos apropiadamente, lo cual es un problema en vista de la Ley de Protección de Datos Personales1. El conjunto de datos en cuestión es la lista de operadores de transporte público. Además, hay inconsistencia de datos como DNIs con menos de 8 digitos.
Otro defecto es que a muchos recursos les falta documentación del significado, unidades, tipo de variable y rangos de valores para los campos. Falta también documentar como se ha codificado valores faltantes (missing data) en cada caso.
Finalmente, un campo importante (fTimestamp
) que indica cuan “fresca” es la
información en el recurso, está mal documentado, según la documentación
2 debe de contener una Unix timestamp, lo cual se expresa usualmente en
segundos. Al ver uno de los ejemplos
(http://es.wiki.junar.com/index.php/Lima_API#Ejemplo), encontramos que se tiene
un valor para este campo de: fTimestamp = 1379683400514
, y asumiendo que son
segundos, esto implicaría que la fecha referida sería 45690-06-04 21:01:54
UTC (??!!). Como imagino que los responsables de este recurso no tienen una
Tardis (o “time machine” equivalente ), y
teniendo en cuenta de que el orden de magnitud del valor es $10^{12}$ cuando
lo esperado es de un orden $10^9$,
entonces, es probable de que el campo esté en milisegundos y no en segundos
como está documentado.
Si usamos milisegundos, obtenemos una fecha mas razonable: 2013-09-20
13:23:20 UTC
Información para reproducibilidad
Este artículo es una versión modificada de lo publicado anteriormente en los siguientes URLs:
- Código original en formato RMarkdown: https://github.com/jmcastagnetto/lima-datosabiertos-r/tree/master/tga_georef
- Artículo en RPubs: http://rpubs.com/jesuscastagnetto/paraderos-tga-georef
sessionInfo()
## R version 3.1.1 (2014-07-10)
## Platform: x86_64-pc-linux-gnu (64-bit)
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] googleVis_0.5.2 mapproj_1.2-1 maps_2.3-2 ggmap_2.3
## [5] ggplot2_1.0.0 jsonlite_0.9.8 knitr_1.6 setwidth_1.0-3
##
## loaded via a namespace (and not attached):
## [1] colorspace_1.2-2 digest_0.6.4 evaluate_0.5.5
## [4] formatR_0.10 grid_3.1.1 gtable_0.1.2
## [7] httr_0.3 labeling_0.2 MASS_7.3-33
## [10] munsell_0.4.2 plyr_1.8.1 png_0.1-7
## [13] proto_0.3-10 Rcpp_0.11.2 RCurl_1.95-4.1
## [16] reshape2_1.4 RgoogleMaps_1.2.0.6 rjson_0.2.14
## [19] RJSONIO_1.2-0.2 scales_0.2.4 stringr_0.6.2
## [22] tools_3.1.1
- Mas información acerca de la ley y reglamento en http://www.minjus.gob.pe/proteccion-de-datos-personales/ [return]
- http://es.wiki.junar.com/index.php/Lima_API [return]