2013-03-24 18 views
5

Sto tentando di popolare un vettore binario basato sull'intersezione di due data.frames su più criteri.R intersect data.frame su più criteri

Ho il codice in funzione ma ritengo che la memoria sia eccessiva solo per ottenere il vettore binario.

Quando applico il mio codice ai miei dati completi (40mm + righe). Comincio ad avere problemi di memoria.

C'è un modo più semplice per produrre il vettore?

Ecco alcuni dati di esempio (ad esempio, campione sub include solo oss nel campione completo.):

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") 

Ecco il mio codice di lavoro:

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 

Grazie in anticipo per qualsiasi Aiuto!

+1

Si dovrebbe spiegare che cosa si vuole in linguaggio naturale. (Il codice che dici sta funzionando ha semplicemente bloccato la mia sessione di R, probabilmente perché non c'è alcuna funzione 'trim'?) No, la prima chiamata' sqldf' la blocca. –

+0

@Dwin, ho appena avuto lo stesso problema di blocco. Ma solo entro le prime 4 righe della parte 'q_intersect'. Per inciso, Brad, nella tua domanda precedente stavi usando 'data.table' e qui stai usando' data.frame'. È deliberato? –

+0

@RicardoSaporta Salve. Hanno appena taggato data.table. Vedi la discussione nella chatroom di ieri che mi ha corretto. Potrebbe fare uno sweep di disinserimento maggiore. La vista SO è che i tag sono per domande e non risposte. Con occhi nuovi, ora vedo e concordo che la ricerca "[data.table] data.table è: domanda" è come dovrebbe apparire "[data.table]" da solo. Comunque sembra ok (vedi chat) per taggare le risposte accettate usando 'data.table' dove voti> 10 pure. Per cercare le risposte usando data.table possiamo usare "[r] - [data.table] data.table is: answer". –

risposta

4

Non è del tutto chiaro cosa stai cercando di realizzare, ma credo che qualcosa del genere sarebbe molto più semplice.

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] 

L'idea è che si imposta la key di ogni data.table per essere le colonne che si desidera si intersecano. Quando chiami full[sub], nomatch=0] ottieni il tuo inner join, e impostiamo solo quei valori su 1; i valori non identificati nella giunzione interna vengono lasciati come 0, come impostato nella riga precedente.

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 
+0

Ricardo, questo sembra buono. Stavo usando data.frames per creare i dati di esempio, ma preferisco data.tables quando possibile. – Brad

+2

Questo è sicuramente molto più semplice del mio approccio. Grazie ancora! – Brad

+0

@Brad, nessun errore. Stavo chiedendo nei miei commenti sopra, semplicemente perché rende il problema molto più facile. –

2

semplificazione SQL mi risulta che si desidera creare un frame di dati di una colonna con lo stesso numero di righe come full_sample tale che una determinata riga nell'output contiene 1 se la riga corrispondente full_sample ha un accoppiamento sub_sample riga e 0 altrimenti.

In tal caso, le istruzioni SQL multiple possono essere raggruppate in un'unica istruzione SQL più semplice, come illustrato di seguito. L'unione sinistra assicura che tutte le righe di full_sample siano incluse e il join naturale causi il join su tutti i nomi di colonna comuni tra i due frame di dati di input.

sqldf("select s.yr is not null as solution 
     from full_sample f natural left join sub_sample s") 

(A proposito, si noti che le stringhe letterali possono scorrere su più righe come questo dimostra quindi non è necessario incollare più righe insieme.)

Out of Database Memoria sqldf di default utilizza un nel database di memoria ma è possibile specificare un nome file (che non deve essere presente in anticipo) tramite l'argomento dbname= da utilizzare come database esaurito. In quel caso non sarai limitato dalla memoria.

sqldf("select s.yr is not null as solution 
     from full_sample f natural left join sub_sample s", dbname = "mydb") 

(Inoltre è possibile migliorare le prestazioni in alcuni casi utilizzando indici Vedere la sqldf home page per gli esempi..)

UPDATE: aggiunti soluzione SQL semplice