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

Другие вопросы по тегам