Чтение в очень, очень большом NDJSON
У меня есть файл NDJSON размером 33 ГБ, который мне нужно прочитать в data.table в R. Он сжат в файл 2 ГБ, в идеале я бы хотел сохранить его сжатым.
Структура не так важна, за исключением этого (при импорте через jsonlite::stream_in
), данные мне нужны только в нескольких простых столбцах. Подавляющее большинство веса данных хранится в list
В трех столбцах я хочу отбросить как можно скорее.
Мои две проблемы: как я могу распараллелить чтение, и как я могу ограничить использование памяти (сейчас мой работник над этим файлом использует 175 ГБ памяти)?
Что я делаю сейчас:
dt.x <- data.table(flatten(stream_in(gzfile("source.gz"))[, -c(5:7)]))
Идеи:
Может быть, есть какой-то способ игнорировать часть NDJSON во время stream_in
?
Могу ли я разобрать gzfile
соединение, например, с регулярным выражением, прежде чем оно перейдет к stream_in
удалить лишние данные?
Могу ли я сделать что-то вроде readLines
на gzfile
подключение для чтения данных 1 миллион строк на одного работника?
РЕДАКТИРОВАТЬ: Если это вообще возможно, моя цель состоит в том, чтобы сделать это портативным для других пользователей и держать его полностью в R.
1 ответ
Использование jqr с readr
Вот расшифровка, иллюстрирующая, как использовать jqr для чтения сжатого файла NDJSON (он же JSONL):
$ R --vanilla
> library(readr)
> library(jqr)
> read_lines("objects.json.gz") %>% jq('.a')
[
1,
2,
3
]
>
С помощью read_file()
дает тот же результат. Поскольку эти функции должны разархивировать весь файл, требования к памяти будут существенными.
Чтение каждого объекта JSON отдельно
Поскольку файл представляет собой NDJSON, мы можем радикально уменьшить объем оперативной памяти, требуемой для чтения одной сущности JSON за раз:
con = file("objects.json", "r");
while ( length(line <- readLines(con, n = 1)) > 0) {
print( line %>% jq('.a') );
}
JQ
Вероятно, существуют более эффективные способы использования jqr, но если целью является эффективность как в пространстве, так и во времени, то лучше всего использовать версию jq для командной строки.
подсчитывать
Если вам нужно заранее подсчитать количество строк в (разархивированном) файле, то для экономии памяти я бы, вероятно, использовал system2
а также wc
если возможно; все остальное не получится, вы можете запустить фрагмент так:
n<-0;
con = file("objects.json", "r");
while (TRUE) {
readLines(con, n = 1);
if (length(line) == 0) { break; }
n <- n+1;
}