работа с большими списками, которые становятся слишком большими для оперативной памяти при работе с ними

За исключением работы на машине с большим объемом оперативной памяти, как я могу работать с большими списками в R, например, помещать их на диск, а затем работать с его разделами?

Вот некоторый код для создания типов списков, которые я использую

n = 50; i = 100
WORD <- vector(mode = "integer", length = n)
for (i in 1:n){
  WORD[i] <- paste(sample(c(rep(0:9,each=5),LETTERS,letters),5,replace=TRUE),collapse='')
}
dat <- data.frame(WORD =  WORD,
                  COUNTS = sample(1:50, n, replace = TRUE))
dat_list <- lapply(1:i, function(i) dat) 

В моем реальном случае использования каждый фрейм данных в списке уникален, в отличие от быстрого примера здесь. Я стремлюсь к n = 4000 и i = 100 000

Это один из примеров того, что я хочу сделать с этим списком фреймов данных:

FUNC <- function(x) {rep(x$WORD, times = x$COUNTS)}
la <- lapply(dat_list, FUNC)

В моем реальном случае это работает в течение нескольких часов, заполняет ОЗУ и большую часть подкачки, а затем RStudio зависает и показывает сообщение с бомбой (RStudio был вынужден завершить работу из-за ошибки в сеансе R).

Я вижу, что bigmemory ограничен матрицами, а ff, похоже, не обрабатывает списки. Какие есть другие варианты? Если здесь возможен sqldf или связанный с ним метод нехватки памяти, с чего мне начать? Я не могу получить достаточно из документации, чтобы добиться какого-либо прогресса, и был бы признателен за любые указатели. Обратите внимание, что инструкции «купить больше оперативной памяти» будут игнорироваться! Это для пакета, который, как я надеюсь, подойдет для обычных настольных компьютеров (т.е. компьютерных лабораторий старшекурсников).

ОБНОВЛЕНИЕ В дополнение к полезным комментариям от SimonO101 и Ари, вот несколько тестов, сравнивающих кадры данных и таблицы данных, циклы и lapply, а также с gc и без него.

# self-contained speed test of untable
n = 50; i = 100
WORD <- vector(mode = "integer", length = n)
for (i in 1:n){
  WORD[i] <- paste(sample(c(rep(0:9,each=5),LETTERS,letters),5,replace=TRUE),collapse='')
}
# as data table
library(data.table)
dat_dt <- data.table(WORD = WORD, COUNTS = sample(1:50, n, replace = TRUE))
dat_list_dt <- lapply(1:i, function(i) dat_dt)

# as data frame
dat_df <- data.frame(WORD =  WORD, COUNTS = sample(1:50, n, replace = TRUE))
dat_list_df <- lapply(1:i, function(i) dat_df)

# increase object size
y <- 10
dt <- c(rep(dat_list_dt, y))
df <- c(rep(dat_list_df, y))
# untable
untable <- function(x) rep(x$WORD, times = x$COUNTS)


# preallocate objects for loop to fill
df1 <- vector("list", length = length(df))
dt1 <- vector("list", length = length(dt))
df3 <- vector("list", length = length(df))
dt3 <- vector("list", length = length(dt))
# functions for lapply
df_untable_gc <- function(x) { untable(df[[x]]); if (x%%10) invisible(gc()) }
dt_untable_gc <- function(x) { untable(dt[[x]]); if (x%%10) invisible(gc()) }
# speedtests
library(microbenchmark)
microbenchmark(
  for(i in 1:length(df)) { df1[[i]] <- untable(df[[i]]); if (i%%10) invisible(gc()) },
  for(i in 1:length(dt)) { dt1[[i]] <- untable(dt[[i]]); if (i%%10) invisible(gc()) },
  df2 <- lapply(1:length(df), function(i) df_untable_gc(i)),
  dt2 <- lapply(1:length(dt), function(i) dt_untable_gc(i)),
  for(i in 1:length(df)) { df3[[i]] <- untable(df[[i]])},
  for(i in 1:length(dt)) { dt3[[i]] <- untable(dt[[i]])},
  df4 <- lapply(1:length(df), function(i) untable(df[[i]]) ),
  dt4 <- lapply(1:length(dt), function(i) untable(dt[[i]]) ),

  times = 10)

И вот результаты, без явной сборки мусора, data.table намного быстрее и немного быстрее, чем цикл. С явной сборкой мусора (как я думаю, мог бы предложить SimonO101) все они имеют одинаковую скорость - намного медленнее! Я знаю, что использование gc немного спорно и, вероятно, бесполезно в данном случае, но я попробую его с моим реальным вариантом использования и посмотрю, имеет ли это какое-то значение. Конечно, у меня нет данных об использовании памяти для какой-либо из этих функций, что на самом деле является моей главной заботой. Кажется, что нет функции для бенчмаркинга памяти, эквивалентной функциям синхронизации (во всяком случае, для Windows).

Unit: milliseconds
                                                                                                 expr
 for (i in 1:length(df)) {     df1[[i]] <- untable(df[[i]])     if (i%%10)          invisible(gc()) }
 for (i in 1:length(dt)) {     dt1[[i]] <- untable(dt[[i]])     if (i%%10)          invisible(gc()) }
                                            df2 <- lapply(1:length(df), function(i) df_untable_gc(i))
                                            dt2 <- lapply(1:length(dt), function(i) dt_untable_gc(i))
                                         for (i in 1:length(df)) {     df3[[i]] <- untable(df[[i]]) }
                                         for (i in 1:length(dt)) {     dt3[[i]] <- untable(dt[[i]]) }
                                            df4 <- lapply(1:length(df), function(i) untable(df[[i]]))
                                            dt4 <- lapply(1:length(dt), function(i) untable(dt[[i]]))
          min           lq       median           uq         max neval
 37436.433962 37955.714144 38663.120340 39142.350799 39651.88118    10
 37354.456809 38493.268121 38636.424561 38914.726388 39111.20439    10
 36959.630896 37924.878498 38314.428435 38636.894810 39537.31465    10
 36917.765453 37735.186358 38106.134494 38563.217919 38751.71627    10
    28.200943    29.221901    30.205502    31.616041    34.32218    10
    10.230519    10.418947    10.665668    12.194847    14.58611    10
    26.058039    27.103217    27.560739    28.189448    30.62751    10
     8.835168     8.904956     9.214692     9.485018    12.93788    10

person Ben    schedule 19.05.2013    source источник
comment
Я вижу, вы используете конструкцию data.rame, и в этом случае data.table может оказать здесь огромную помощь, поскольку она изменяет значения по ссылке, т. е. заранее не создает несколько копий данных. Насколько я понимаю, помимо места, необходимого в ОЗУ для хранения всей таблицы, единственное необходимое дополнительное рабочее пространство ОЗУ - это длина самого длинного элемента списка (например, column). Тогда как с data.frame вам потребуется гораздо больше дополнительной оперативной памяти при работе с объектом. Из-за изменения по ссылке data.table также будет намного быстрее.   -  person Simon O'Hanlon    schedule 19.05.2013
comment
Кроме того, я не слишком уверен в тонкостях сборщика мусора, но при использовании lapply таким образом, перебирая множество data.frames, возможно (но это предположение), что сборка мусора уже обработана data.frames в памяти может не произойти до после выхода из цикла lapply, поэтому память заполняется. Возможно, здесь подойдет какой-то другой механизм построения/зацикливания?   -  person Simon O'Hanlon    schedule 19.05.2013
comment
@ SimonO101 Я согласен. У меня было несколько крупных операций с lapply, которые работали с простой перезаписи в цикл for.   -  person Ari B. Friedman    schedule 19.05.2013
comment
Спасибо за предложения, я сделаю некоторые тесты и отчитаюсь   -  person Ben    schedule 20.05.2013
comment
Небольшой бенчмаркинг подтверждает, что data.table быстрее, чем data.frame, и, по крайней мере, быстрее, чем явный цикл. Я протестирую его на моем реальном варианте использования и снова зарегистрируюсь (может быть, через некоторое время!). Еще раз спасибо за ваши полезные предложения.   -  person Ben    schedule 20.05.2013
comment
В интересах будущих искателей, я думаю, что хороший ответ на этот вопрос может быть получен из моего ответа здесь   -  person Ben    schedule 30.05.2013


Ответы (1)


Если вы действительно собираетесь использовать очень большие данные, вы можете использовать пакет h5r для записи файлов hdf5. Вы будете записывать и читать с жесткого диска на лету, а не использовать оперативную память. Я не использовал это, поэтому я могу мало помочь в его общем использовании, я упоминаю об этом, потому что я думаю, что для него нет учебника. Я получил эту идею, думая о pytables. Не уверен, что это решение подходит для вас.

person JEquihua    schedule 19.05.2013
comment
Спасибо за предложение. Я просто помещаю это сюда, чтобы вернуться к: bioconductor.org/ пакеты/2.11/bioc/html/rhdf5.html - person Ben; 20.05.2013
comment
Это другой пакет. Из того, что я слышал, пакет h5r считается более надежным: cran .r-project.org/web/packages/h5r/index.html - person JEquihua; 20.05.2013
comment
Да я знаю, просто собираю варианты. Можете ли вы добавить немного больше деталей к своему ответу о том, что вы знаете о надежности? hdf5 совершенно новый для меня! - person Ben; 20.05.2013
comment
И похоже, что не так много документации начального уровня, которая мне нужна для начала работы с пакетами hdf5... Что ж, возможно, это тема для другого вопроса... - person Ben; 20.05.2013
comment
Я тоже заметил это, поэтому я действительно не предоставил много информации. Прости! - person JEquihua; 20.05.2013
comment
Глядя на h5r справочный документ, кажется, что объект hdf5 должен быть «матрицей или вектором того же типа данных». Так что это ограничивает их использование в этом случае, когда я хочу хранить списки. Похоже, здесь более уместным может быть filehash. - person Ben; 30.05.2013