Чтение в кусках за раз, используя fread в package data.table

Я пытаюсь ввести большой файл с разделителями табуляции (около 2 ГБ) с помощью функции fread в пакете data.table. Однако, поскольку он настолько велик, он не полностью вписывается в память. Я попытался ввести его в куски, используя аргументы skip и nrow, такие как:

chunk.size = 1e6
done = FALSE
chunk = 1
while(!done)
{
    temp = fread("myfile.txt",skip=(chunk-1)*chunk.size,nrow=chunk.size-1)
    #do something to temp
    chunk = chunk + 1
    if(nrow(temp)<2) done = TRUE
}

В вышеприведенном случае я читаю по 1 миллиону строк за раз, выполняя вычисления на них, а затем получая следующий миллион и т.д. Проблема с этим кодом заключается в том, что после получения каждого фрагмента fread необходимо начать сканирование файла с самого начала, так как после каждой итерации цикла, skip увеличивается на миллион. В результате после каждого фрагмента fread занимает больше времени и дольше, чтобы фактически перейти к следующему фрагменту, что делает это очень неэффективным.

Есть ли способ сказать fread сделать паузу, скажем, 1 миллион строк, а затем продолжить чтение с этой точки без перезапуска в начале? Любые решения, или это будет новый запрос функции?

Ответы

Ответ 1

Вы должны использовать пакет LaF. Это вводит какой-то указатель на ваши данные, тем самым избегая - для очень больших данных - раздражающего поведения чтения всего файла. Насколько мне известно, fread() в data.table pckg необходимо знать общее количество строк, что требует времени для данных GB. Используя указатель в LaF, вы можете перейти к каждой строке (строкам), которую хотите; и читать в кусках данных, на которые вы можете применить свою функцию, а затем перейти к следующему фрагменту данных. На моем маленьком ПК я пропустил csv файл размером 25 ГБ с шагом 10e6 и извлек все необходимые наблюдения - 5e6 - каждый кусок 10e6 занял 30 секунд.

UPDATE:

library('LaF')
huge_file <- 'C:/datasets/protein.links.v9.1.txt'

#First detect a data model for your file:
model <- detect_dm_csv(huge_file, sep=" ", header=TRUE)

Затем создайте соединение с вашим файлом с помощью модели:

df.laf <- laf_open(model)

После выполнения вы можете делать всевозможные вещи, не зная размер файла, как в data.table pckgs. Например, поместите указатель на строку 100e6 и прочитайте здесь 1e6 строк данных:

goto(df.laf, 100e6)
data <- next_block(df.laf,nrows=1e6)

Теперь data содержит 1e6 строк вашего CSV файла (начиная с строки 100e6).

Вы можете читать в кусках данных (размер зависит от вашей памяти) и поддерживать только то, что вам нужно. например huge_file в моем примере указывает на файл со всеми известными последовательностями белка и имеет размеp > 27 ГБ - путь к большому для моего ПК. Чтобы получить только последовательность человека, я отфильтровал с использованием идентификатора организма, который для человека равен 9606, и это должно появиться в начале переменной protein1. Грязный путь заключается в том, чтобы перевести его в простой цикл и просто прочитать один блок данных за раз:

library('dplyr')
library('stringr')

res <- df.laf[1,][0,]
for(i in 1:10){
  raw <-
    next_block(df.laf,nrows=100e6) %>% 
    filter(str_detect(protein1,"^9606\\."))
  res <- rbind(res, raw)

    }

Теперь res содержит отфильтрованные данные человека. Но лучше - и для более сложных операций, например. вычисление по данным "на лету" - функция process_blocks() принимает в качестве аргумента функцию. Следовательно, в функции вы делаете то, что хотите на каждой части данных. Прочтите документацию.

Ответ 2

Вы можете использовать readr read_*_chunked для чтения в данных и, например, фильтруйте его по отдельности. См. здесь и здесь для примера:

# Cars with 3 gears
f <- function(x, pos) subset(x, gear == 3)
read_csv_chunked(readr_example("mtcars.csv"), DataFrameCallback$new(f), chunk_size = 5)