R: как объединить два огромных кадра данных без исчерпания памяти
У меня есть два кадра данных df1
и df2
, каждый из которых содержит около 10 миллионов строк и 4 столбца. Я читал их в R, используя RODBC/sqlQuery, без проблем, но когда я пытаюсь rbind
их, я получаю, что больше всего боюсь сообщений об ошибках R: cannot allocate memory
. Там должны быть более эффективные способы сделать rbind
более эффективно - у кого есть свои любимые трюки по этому поводу, которые они хотят поделиться? Например, я нашел этот пример в документе для sqldf
:
# rbind
a7r <- rbind(a5r, a6r)
a7s <- sqldf("select * from a5s union all select * from a6s")
Это лучший/рекомендуемый способ сделать это?
UPDATE
Я получил его для работы с использованием ключевого аргумента dbname = tempfile()
в вызове sqldf
выше, поскольку JD Long предлагает в своем ответе этот вопрос
Ответы
Ответ 1
Вместо того, чтобы читать их в R в начале, а затем объединять их, вы могли бы SQLite прочитать их и объединить их перед отправкой их в R. Таким образом, файлы никогда не будут индивидуально загружаться в R.
# create two sample files
DF1 <- data.frame(A = 1:2, B = 2:3)
write.table(DF1, "data1.dat", sep = ",", quote = FALSE)
rm(DF1)
DF2 <- data.frame(A = 10:11, B = 12:13)
write.table(DF2, "data2.dat", sep = ",", quote = FALSE)
rm(DF2)
# now we do the real work
library(sqldf)
data1 <- file("data1.dat")
data2 <- file("data2.dat")
sqldf(c("select * from data1",
"insert into data1 select * from data2",
"select * from data1"),
dbname = tempfile())
Это дает:
> sqldf(c("select * from data1", "insert into data1 select * from data2", "select * from data1"), dbname = tempfile())
A B
1 1 2
2 2 3
3 10 12
4 11 13
Эта более короткая версия также работает, если порядок строк неважен:
sqldf("select * from data1 union select * from data2", dbname = tempfile())
Для получения дополнительной информации см. домашнюю страницу sqldf http://sqldf.googlecode.com и ?sqldf
. Обратите особое внимание на аргументы формата файла, так как они близки, но не идентичны read.table
. Здесь мы использовали значения по умолчанию, поэтому проблема была меньше.
Ответ 2
Обратите внимание на пакет data.table
R для эффективных операций над объектами с более чем несколькими миллионами записей.
Версия 1.8.2 этого пакета предлагает функцию rbindlist
, благодаря которой вы можете добиться того, что вы хотите очень эффективно. Таким образом, вместо rbind(a5r, a6r)
вы можете:
library(data.table)
rbindlist(list(a5r, a6r))
Ответ 3
Попробуйте создать data.frame
нужного размера, поэтому импортируйте свои данные с помощью индексов.
dtf <- as.data.frame(matrix(NA, 10, 10))
dtf1 <- as.data.frame(matrix(1:50, 5, 10, byrow=TRUE))
dtf2 <- as.data.frame(matrix(51:100, 5, 10, byrow=TRUE))
dtf[1:5, ] <- dtf1
dtf[6:10, ] <- dtf2
Я предполагаю, что rbind
вырастает объект без предварительного выделения его размеров... Я не уверен, это всего лишь предположение. Сегодня вечером я расчешу "The R Inferno" или "Data Manipulation with R". Может быть, merge
выполнит трюк...
ИЗМЕНИТЬ
И вы должны иметь в виду, что (возможно) ваша система и/или R не могут справиться с чем-то большим. Попробуйте RevolutionR, возможно, вам удастся сэкономить время/ресурсы.
Ответ 4
Для полноты этой темы на тему объединения: в больших файлах попробуйте использовать команды оболочки для файлов, чтобы их объединить. В окнах, которые являются командой "COPY" с флагом "/B". Пример:
system(command =
paste0(
c("cmd.exe /c COPY /Y"
, '"file_1.csv" /B'
, '+ "file_2.csv" /B'
, '"resulting_file.csv" /B'
), collapse = " "
)
)#system
Требуется, чтобы файлы не имели заголовка и того же разделителя и т.д. и т.д.
Скорость и универсальность команд оболочки иногда являются большим преимуществом, поэтому не забывайте CLI-команды при отображении потоков данных.