Как эффективно объединить два набора данных?
Я пытаюсь объединить два довольно больших, но не смехотворно (360,000 X 4, 57,000 X 4) - набора данных одним общим идентификатором. Я пробовал обычный merge()
, merge.data.table()
и sqldf()
. Каждый раз, когда у меня заканчивается память (cannot allocate vector of size...
). Есть ли решение этой проблемы? Или R-плохой инструмент для объединения данных? head()
приведено ниже (я пытаюсь слиться на студента.Имя):
ID10 STUDENT.NAME FATHER.NAME MOTHER.NAME
1 1 DEEKSHITH J JAYANNA SWARNA
2 4 MANIKANTHA D DEVARAJ MANJULA
3 5 NAGESH T THIMMAIAH N SHIVAMMA
4 6 NIZAMUDDIN R NOOR MOHAMMED BIBI
5 7 PRABHU YELLAPPA YELLAPPA MALLAMMA
6 8 SADDAM PASHA NISAR AHMED ZAREENA
3 ответов
из природы вашей проблемы должно быть, что вы делаете много-много слияния, где каждый студент происходит много раз в каждом фрейме данных. Возможно, вы захотите проверить, сколько раз. Если каждый студент встречается дважды в каждом фрейме данных, это означает, что один студент сделает 4 строки. если студент происходит 10 раз, слияние добавит 100 строк. Сначала проверьте, сколько строк вы получите. Это функция, которую я использую для этого:
count.rows <- function(x,y,v,all=FALSE){
tx <- table(x[[v]])
ty <- table(y[[v]])
val <- val <- names(tx)[match(names(tx),names(ty),0L) > 0L]
cts <- rbind(tx[match(val,names(tx))],ty[match(val,names(ty))])
colnames(cts) <- val
sum(apply(cts,2,prod,na.rm=all),na.rm=TRUE)
}
count.rows(DF1,DF2,"STUDENT.NAME")
если бы вы сделали то ,что вы попросили меня (прочитайте R документации), вы увидите, что сложность зависит от длины ответа. Это связано не с самим алгоритмом слияния, а с привязкой всех результатов вместе. Если вы действительно хотите меньше голодного решения памяти, вам нужно особенно избавиться от этой привязки. Следующий алгоритм делает это за вас. Я написал это, чтобы вы могли найти логику, и ее можно оптимизировать. Имейте в виду, что он не дает того же результата, он копирует все столбцы обеих таблиц данных. Так вы возможно, стоит немного подправить это.
mymerge <- function(x,y,v,count.only=FALSE){
ix <- match(v,names(x))
iy <- match(v,names(y))
xx <- x[,ix]
yy <- y[,iy]
ox <- order(xx)
oy <- order(yy)
xx <- xx[ox]
yy <- yy[oy]
nx <- length(xx)
ny <- length(yy)
val <- unique(xx)
val <- val[match(val,yy,0L) > 0L]
cts <- cbind(table(xx)[val],table(yy)[val])
dimr <- sum(apply(cts,1,prod),na.rm=TRUE)
idx <- vector("numeric",dimr)
idy <- vector("numeric",dimr)
ndx <- embed(c(which(!duplicated(xx)),nx+1),2)[unique(xx) %in% val,]
ndy <- embed(c(which(!duplicated(yy)),ny+1),2)[unique(yy) %in% val,]
count = 1
for(i in 1:nrow(ndx)){
nx <- abs(diff(ndx[i,]))
ny <- abs(diff(ndy[i,]))
ll <- nx*ny
idx[count:(count+ll-1)] <-
rep(ndx[i,2]:(ndx[i,1]-1),ny)
idy[count:(count+ll-1)] <-
rep(ndy[i,2]:(ndy[i,1]-1),each=nx)
count <- count+ll
}
x <- x[ox[idx],]
names(y) <- paste("y.",names(y),sep="")
x[names(y)] <- y[oy[idy],]
rownames(x) <- 1:nrow(x)
x
}
некоторый код тестирования, чтобы вы могли видеть, что он работает:
DF1 <- data.frame(
ID = 1:10,
STUDENT.NAME=letters[1:10],
SCORE = 1:10
)
id <- c(3,11,4,6,6,12,1,4,7,10,5,3)
DF2 <- data.frame(
ID = id,
STUDENT.NAME=letters[id],
SCORE = 1:12
)
mymerge(DF1,DF2,"STUDENT.NAME")
делая то же самое с двумя фреймами данных 0,5 миллиона строк и 4 столбца с до 10 совпадений на имя студента, он возвращает фрейм данных с 5,8 миллиона строк и 8 столбцов andd дает следующее изображение в памяти :
желтое поле-это вызов слияния, зеленое поле-вызов mymerge. Память колеблется от 2,3 Гб до 3,74 Гб, поэтому вызов слияния использует 1.45 Gb и mymerge немного более 0.8 Gb. По-прежнему нет ошибок" из памяти"... Код тестирования для этого ниже:
Names <- sapply(
replicate(120000,sample(letters,4,TRUE),simplify=FALSE),
paste,collapse="")
DF1 <- data.frame(
ID10 = 1:500000,
STUDENT.NAME = sample(Names[1:50000],500000,TRUE),
FATHER.NAME = sample(letters,500000,TRUE),
SCORE1 = rnorm(500000),
stringsAsFactors=FALSE
)
id <- sample(500000,replace=TRUE)
DF2 <- data.frame(
ID20 = DF1$ID10,
STUDENT.NAME = DF1$STUDENT.NAME[id],
SCORE = rnorm(500000),
SCORE2= rnorm(500000),
stringsAsFactors=FALSE
)
id2 <- sample(500000,20000)
DF2$STUDENT.NAME[id2] <- sample(Names[100001:120000],20000,TRUE)
gc()
system.time(X <- merge(DF1,DF2,"STUDENT.NAME"))
Sys.sleep(1)
gc()
Sys.sleep(1)
rm(X)
gc()
Sys.sleep(3)
system.time(X <- mymerge(DF1,DF2,"STUDENT.NAME"))
Sys.sleep(1)
gc()
rm(X)
gc()
вы пробовали данные.таблица пакета? Это более эффективная память и может быть во много раз быстрее. Но, как отмечали другие, этот вопрос не имеет кода, поэтому возможно, вы просто неправильно используете merge.
Я согласен с другими комментаторами, которые говорят, что этот вопрос отсутствует в его описании (отсутствует как код, так и полное описание данных), но я также задаюсь вопросом, не был ли он уже отвечен одной из этих ссылок:
R: как rbind два огромных фрейма данных без исчерпания памяти
цитата, предложенная @G. Grothendieck (который, вероятно, должен быть посвящен в рыцари за его многочисленные вклады в функциональность R), особенно часть, касающаяся использования внешнего файла: http://code.google.com/p/sqldf/#Example_6._File_Input
и еще одна последняя мысль: после сохранения вашей работы, выключения компьютера, восстановления только с помощью R и загрузки только ваших наборов данных, попробуйте cbind(.... спичка.(.) ) маневр такой:
cbind(df1,df2[match(df1$STUDENT.NAME,df2$STUDENT.NAME)),])
Он не будет иметь те же колокола и свистки, что и слияние, но он должен быть достаточно эффективным и успешным, если проблема заключается только в фрагментированной памяти в вашем текущий сеанс. Это не частичные совпадения. Если это было ваше ожидание, вы должны были указать на это. Имена, как известно, грязные, если исходят из независимых источников.