Formato JSON en R

El formato JSON permite expresar datos jerarquizados (ramificados) de forma arbitraria, como las listas de R.

  1. 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" )
    )
    
  2. 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")
    
  3. 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)
    
  4. El dataframe obtenido a partir del JSON anterior (dj) tiene una estructura poco habitual porque
    1. Tiene un dataframe anidado:
      dim (dj)
      dj$size
      
    2. 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;
    
  5. 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
    
  6. 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
    
  7. 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
    
  8. 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
    
  9. 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
    

Author: Carleos Artime

Created: 2018-11-30 vie 10:36

Emacs 25.1.1 (Org mode 8.2.10)

Validate