Formato JSON en R
El formato JSON permite expresar datos jerarquizados (ramificados) de forma arbitraria, como las listas de R.
- Considera el siguiente ejemplo de datos en formato JSON
inspirado en https://docs.mongodb.com/manual/tutorial/query-documents/
[ { item: "journal", qty: 25, size: { s: 320, uom: "cc" }, status: "A" }, { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" }, { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" } ];
Crea mediante
list
un objeto en R que tenga esa misma estructura (sin cargar bibliotecas adicionales).d <- list ( list (item = "journal", qty = 25, size = list (s = 320, uom = "cc"), status = "A" ), list (item = "notebook", qty = 50, size = list (h = 8.5, w = 11, uom = "in"), status = "A" ), list (item = "paper", qty = 100, size = list (h = 8.5, w = 11, uom = "in"), status = "D" ), list (item = "planner", qty = 75, size = list (h = 22.85, w = 30, uom = "cm"), status = "D" ), list (item = "postcard", qty = 45, size = list (h = 10, w = 15.25, uom = "cm"), status = "A" ) )
- Desde R, guarda el objeto creado en el ejercicio anterior en un fichero de texto con formato JSON
(cargando previamente la biblioteca
jsonlite
).library (jsonlite) ## paso a paso j <- toJSON (d) # trasformar a JSON j <- toJSON (d, pretty = TRUE) # idem humanolegible cat (j, file="/tmp/ejemplo.json") # guardar en fichero ## de golpe write_json (d, "/tmp/ejemplo.json")
- Sal de R, vuelve a entrar en R e importa el contendo del fichero creado en el ejercicio anterior.
library (jsonlite) ## directamente dj <- read_json ("/tmp/ejemplo.json") ## indirectamente cadena <- readLines ("/tmp/ejemplo.json") dj <- fromJSON (cadena)
- El dataframe obtenido a partir del JSON anterior (
dj
) tiene una estructura poco habitual porque- Tiene un dataframe anidado:
dim (dj) dj$size
- Sus columnas son listas en lugar de vectores homogéneos:
dj$item class (dj$item)
Trasforma el dataframe en un dataframe habitual.
DJ <- flatten (dj) # incorpora dataframe anidado dim (DJ) # comprueba el nuevo número de columnas ## hace falta trasformar los NULL en NA para obtener un dataframe normal D <- data.frame (lapply (DJ, function (v) unlist (ifelse (sapply(v,is.null), NA, v)))) ## uso unlist porque pasar una lista como argumento de data.frame es como si se pasara cada componente como ## un argumento separado (véase ?data.frame); con unlist se trasforman en vectores; ## uso lapply en vez de sapply porque ## 1. sapply genera (si puede por la misma longitud de los resultados, como en este caso) una matriz; ## 2. una matriz es homogénea, por lo que los números se trasforman en cadenas; ## 3. lapply genera siempre una lista, que es una estructura heterogénea y puede albergar tanto números como cadenas;
- Tiene un dataframe anidado:
- Descargar los datos de trayectos de buses de Gijón
(también aquí y en Bellman)
## desde Firefox o escribiendo en Bellman: wget http://bellman.ciencias.uniovi.es/~carleos/master/manadine/curso2/AnalisisDatos2/analisisdatos2/datos/busgijontray.json
- Cargar esos datos en R
library (jsonlite) ## usando conversión automática (en la medida de lo posible) a dataframe d <- read_json ("~/Descargas/busgijontray.json", simplifyVector=TRUE) length (d); names (d) # investigar la estructura length (d[[1]]); names (d[[1]]) length (d[[1]][[1]]); names (d[[1]][[1]]); class (d[[1]][[1]]); dim (d[[1]][[1]]) D <- d$puntosTrayectos$puntoTrayecto # dataframe ## trasformando la lista en matriz a mano d <- read_json ("~/Descargas/busgijontray.json") length (d); names (d) # investigar la estructura length (d[[1]]); names (d[[1]]) length (d[[1]][[1]]); names (d[[1]][[1]]); class (d[[1]][[1]]); d[[1]][[1]][[1]] D <- do.call (rbind, d[[1]][[1]]) # genera matriz usando lista como si fuera lista de argumentos para rbind... D <- t (simplify2array (d[[1]][[1]])) # (otra forma de hacer lo mismo que el renglón anterior) D[1,]; D[1:5,1]; mode (D) # ...pero ¡es una matriz en modo "lista"! mode (D) <- "numeric" # la trasformamos en matriz normal... D <- as.data.frame (D) # ...y en dataframe
- Hallar las longitudes de los trayectos de cada línea a partir de las coordenadas UTM y guardar los resultados en una lista.
## a cada línea le corresponden diversos trayectos numerados de forma no consecutiva table (D$idlinea, D$idtrayecto) # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 23 24 25 26 27 28 29 30 31 32 # 1 173 191 0 0 0 209 110 129 172 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 2 0 0 214 198 235 215 0 0 184 173 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 4 0 0 147 142 208 195 153 166 168 169 162 151 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 6 247 247 195 194 126 129 199 169 122 263 152 118 256 245 121 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 10 135 153 166 188 183 190 98 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 12 144 170 0 0 0 0 0 208 228 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 14 429 459 174 243 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 15 0 0 0 0 0 0 0 0 0 0 175 196 138 157 85 87 153 117 95 106 0 0 0 0 0 0 0 0 0 0 # 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 269 278 324 326 311 320 277 292 270 236 # 18 248 240 0 0 189 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 20 326 303 226 239 262 268 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 21 0 0 0 0 192 190 109 109 261 271 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 24 367 383 412 363 453 469 498 449 243 0 0 252 0 525 439 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 25 356 380 444 468 470 501 125 165 429 458 544 580 174 203 336 346 450 467 81 91 0 0 0 0 0 0 0 0 0 0 # 26 165 177 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 28 74 56 136 102 118 134 157 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 30 74 49 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 31 21 46 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 34 136 105 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 35 210 200 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 36 69 53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 41 77 133 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 42 93 127 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 43 113 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 # 44 238 263 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 resultado <- by (D, D$idlinea, function (Di) by (Di, Di$idtrayecto, function (Dij) { suma <- 0 for (k in 2:nrow(Dij)) suma <- suma + sqrt ((Dij[k,"utmy"] - Dij[k-1,"utmy"]) ^ 2 + (Dij[k,"utmx"] - Dij[k-1,"utmx"]) ^ 2) suma }, simplify = FALSE), # para que no se pierdan los nombres de los trayectos simplify = FALSE) # al exportar después a JSON
- Conserva el resultado en un fichero JSON.
## versión para volver a cargar en R mediante unserializeJSON (conserva todos los atributos) cat (serializeJSON (resultado), file = "/tmp/resultado.json") ## versión para usar los datos con otros programas cat (toJSON (resultado, force=TRUE, pretty=TRUE), file="/tmp/resultado.json") ## hace falta "force" porque jsonlite no se da cuenta de que "by" es una lista
- Carga en R los estos datos del servicio de empleo, en los que la situación
laboral se codifica como: 1=ocupado, 2=parado.
Calcula, para cada individuo, el número de días que ha estado ocupado, suponiendo lo siguiente:
- Si la situación laboral última es 1, ha estado ocupado desde entonces hasta la actualidad.
- De la lista de chequeos históricos en la oficina del INEM, si la situación laboral es 1 se considera que ha estado ocupado hasta el siguiente chequeo.
- Si en el último chequeo histórico la situación laboral es 1, ha estado ocupado desde ese chequeo hasta la situación laboral última.
library (jsonlite) empleo <- read_json (paste0 ("http://bellman.ciencias.uniovi.es/~carleos/master/manadine/curso2/AnalisisDatos2/", "analisisdatos2/datos/empleo-situacion.json")) comoFecha <- function (cadena) as.Date (cadena, "%Y%m%d") dias.trabajando <- function (id) { a <- empleo[[id]] fslu <- comoFecha (a$FechaSituLaboralUltima[[1]]) if (a$SituacionLaboralUltima[[1]] == "1") dias <- Sys.Date() - fslu else dias <- as.difftime(0,units="days") # podría ponerse simplemente: dias <- 0 trabajando <- FALSE orden <- order (comoFecha (sapply (a$Historico, function(chequeo) chequeo$FechaInicio[[1]]))) historico <- a$Historico[orden] # en algunos casos no están ordenadas las fechas for (i in 1:length(historico)) { if (trabajando) dias <- dias + comoFecha(historico[[i]]$FechaInicio[[1]]) - comoFecha(historico[[i-1]]$FechaInicio[[1]]) trabajando <- historico[[i]]$SituacionLaboral[[1]] == "1" } if (trabajando) dias <- dias + fslu - comoFecha(historico[[length(historico)]]$FechaInicio[[1]]) dias } sapply (1:4, dias.trabajando) # ejemplo de aplicar la función