R пересекает data.frame по нескольким критериям
Я пытаюсь заполнить двоичный вектор на основе пересечения двух data.frames по нескольким критериям.
У меня работает код, но я чувствую, что это слишком много памяти, чтобы получить двоичный вектор.
Когда я применяю свой код к моим полным данным (40 мм + строки). У меня начинаются проблемы с памятью.
Есть ли более простой способ получить вектор?
Вот некоторые примеры данных (например, подвыборка будет включать только об. В полной выборке):
ob1_1 <- as.data.frame(cbind(c(1999),c("111","222","666","777")),stringsAsFactors=FALSE)
ob2_1 <- as.data.frame(cbind(c(2000),c("111","333","555","777")),stringsAsFactors=FALSE)
ob3_1 <- as.data.frame(cbind(c(2001),c("111","222","333","777")),stringsAsFactors=FALSE)
ob4_1 <- as.data.frame(cbind(c(2002),c("111","444","555","777")),stringsAsFactors=FALSE)
full_sample <- rbind(ob1_1,ob2_1,ob3_1,ob4_1)
colnames(full_sample) <- c("yr","ID")
ob1_2 <- as.data.frame(cbind(c(1999),c("111","222","777")),stringsAsFactors=FALSE)
ob2_2 <- as.data.frame(cbind(c(2000),c("333")),stringsAsFactors=FALSE)
ob3_2 <- as.data.frame(cbind(c(2001),c("888")),stringsAsFactors=FALSE)
ob4_2 <- as.data.frame(cbind(c(2002),c("111","444","555","777")),stringsAsFactors=FALSE)
sub_sample <- rbind(ob1_2,ob2_2,ob3_2,ob4_2)
colnames(sub_sample) <- c("yr","ID")
Вот мой рабочий код:
q_intersect <- ""
q_intersect <- paste(q_intersect , "select a.yr, a.ID ", sep=" ")
q_intersect <- paste(q_intersect , "from full_sample a ", sep=" ")
q_intersect <- paste(q_intersect , "intersect ", sep=" ")
q_intersect <- paste(q_intersect , "select b.yr, b.ID ", sep=" ")
q_intersect <- paste(q_intersect , "from sub_sample b ", sep=" ")
q_intersect <- trim(gsub(" {2,}", " ", q_intersect ))
intersect_temp <- cbind(sqldf(q_intersect ),1)
colnames(intersect_temp ) <- c("yr","ID","in_both")
q_expand <- ""
q_expand <- paste(q_expand , "select in_both ", sep=" ")
q_expand <- paste(q_expand , "from full_sample a ", sep=" ")
q_expand <- paste(q_expand , "left join intersect_temp b ", sep=" ")
q_expand <- paste(q_expand , "on a.yr=b.yr ", sep=" ")
q_expand <- paste(q_expand , "and a.ID=b.ID ", sep=" ")
q_expand <- trim(gsub(" {2,}", " ", q_expand ))
solution <- as.integer(sqldf(q_expand)[,1])
solution [is.na(solution )] <- 0
Заранее спасибо за любую помощь!
2 ответа
Не совсем понятно, чего вы пытаетесь достичь, но я верю, что что-то подобное будет намного проще.
library(data.table)
fullDT <- data.table(full_sample, key=c("yr", "ID"))
subDT <- data.table(sub_sample, key=c("yr", "ID"))
fullDT[ , intersect := 0L]
fullDT[subDT, intersect := 1, nomatch=0]
Идея в том, что вы установите key
каждого data.table
быть столбцами, которые вы хотите пересечь. Когда вы звоните full[sub], nomatch=0]
вы получаете ваше внутреннее соединение, и мы устанавливаем только эти значения 1
; значения, не указанные во внутреннем соединении, остаются 0
, как установлено в строке ранее.
fullDT
# yr ID intersect
# 1: 1999 111 1
# 2: 1999 222 1
# 3: 1999 666 0
# 4: 1999 777 1
# 5: 2000 111 0
# 6: 2000 333 1
# 7: 2000 555 0
# 8: 2000 777 0
# 9: 2001 111 0
# 10: 2001 222 0
# 11: 2001 333 0
# 12: 2001 777 0
# 13: 2002 111 1
# 14: 2002 444 1
# 15: 2002 555 1
# 16: 2002 777 1
Более простой SQL Я понимаю, что вы хотите создать фрейм данных из одного столбца с тем же количеством строк, что и full_sample
так, что данная строка в выводе содержит 1, если соответствующая строка в full_sample
имеет соответствие sub_sample
строка и 0 в противном случае.
В этом случае несколько операторов SQL могут быть сведены в один более простой оператор SQL, как показано ниже. Левое соединение гарантирует, что все строки full_sample
включаются, и естественное объединение вызывает объединение всех имен столбцов, общих для двух входных фреймов данных.
sqldf("select s.yr is not null as solution
from full_sample f natural left join sub_sample s")
(Кстати, обратите внимание, что строковые литералы могут проходить по нескольким строкам, как это показано, поэтому нет необходимости вставлять несколько строк вместе.)
База данных Out of Memory sqldf по умолчанию использует базу данных in memory, но вы можете указать имя файла (которое не должно существовать заранее) через dbname=
Аргумент для использования как вне базы данных. В этом случае вы не будете ограничены памятью.
sqldf("select s.yr is not null as solution
from full_sample f natural left join sub_sample s", dbname = "mydb")
(Также вы можете улучшить производительность в некоторых случаях с помощью индексов. См. Примеры на домашней странице sqldf.)
ОБНОВЛЕНИЕ: добавлено простое решение SQL