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