Чтение в очень, очень большом 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;
}
Другие вопросы по тегам