R: как связать два огромных фрейма данных без нехватки памяти - PullRequest
19 голосов
/ 21 января 2011

У меня есть два фрейма данных 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, как Д.Д. Лонг предлагает в своем ответе на этот вопрос

Ответы [ 4 ]

26 голосов
/ 21 января 2011

Вместо того, чтобы читать их в 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. Здесь мы использовали значения по умолчанию, поэтому это было не так важно.

19 голосов
/ 27 августа 2012

Обратите внимание на пакет data.table R для эффективной работы над объектами с более чем несколькими миллионами записей.

Версия 1.8.2 этого пакета предлагает функцию rbindlist, благодаря которой вы можете очень эффективно достичь желаемого,Таким образом, вместо rbind(a5r, a6r) вы можете:

library(data.table)
rbindlist(list(a5r, a6r))
1 голос
/ 08 июля 2016

Для полноты этой темы на тему объединения больших файлов попробуйте использовать команды оболочки для файлов, чтобы объединить их. В окнах это команда «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-команды при отображении потоков данных.

1 голос
/ 21 января 2011

Попробуйте создать 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, может быть, вам удастся сэкономить время / ресурсы.

...