Necesito leer archivos .json en R, y va muy lento. Para un archivo de 67.000 líneas, tardó más de 10 minutos en cargarse. Aquí está mi código:

library(dplyr)
library(tidyr)
library(rjson)

f<-data.frame(Reduce(rbind, lapply(readLines("filename.jsonl"),fromJSON)))
f2<-f%>%
  unnest(cols = names(f))

Aquí hay una muestra del archivo .jsonl

{"UID": "a1", "str1": "Who should win?", "str2": "Who should we win?", "length1": 3, "length2": 4, "prob1": -110.5, "prob2": -108.7}
{"UID": "a2", "str1": "What had she walked through?", "str2": "What had it walked through?", "length1": 5, "length2": 5, "prob1": -154.6, "prob2": -154.8}

Entonces, mis preguntas son: (1) ¿Por qué tarda tanto en ejecutarse y (2) ¿Cómo lo soluciono?

0
A.Vail 21 oct. 2019 a las 18:11

2 respuestas

La mejor respuesta

Creo que la forma más eficiente de leer archivos de líneas json es usar la función stream_in() del paquete jsonlite . stream_in() requiere un connection como entrada, pero puede usar la siguiente función para leer en un archivo de texto normal:

read_json_lines <- function(file){
  con <- file(file, open = "r")
  on.exit(close(con))
  jsonlite::stream_in(con, verbose = FALSE)
}
3
Stefan F 21 oct. 2019 a las 15:19

También puede consultar ndjson. Es un envoltorio alrededor de la súper conveniente biblioteca json de C ++ de Niels Lohmann. La interfaz es similar a jsonlite:

df <- ndjson::stream_in('huge_file.jsonl')

Alternativamente, puede paralelizarlo. Claro, depende de su configuración específica (por ejemplo, CPU, HDD, archivo), pero puede intentarlo. Trabajo con bastante frecuencia en volcados de BigQuery. En el caso de tablas más grandes, la salida se divide en archivos. Esto permite paralelizarlo a nivel de archivo (leer y analizar varios archivos en paralelo y fusionar las salidas):

library(furrr)

# my machine has more than 30 cores and a quite fast SSD
# Therefore, it utilises all 20 cores
plan(multisession, workers = 20)

df <- future_map_dfr(
   # this returns a list containing all my jsonline files
   list.files(path = "../data/panel", pattern="00*", full.names=T),
   # each file is parsed separately 
   function(f) jsonlite::stream_in(file(f))
)
0
NaN 9 dic. 2020 a las 14:01