2013-03-09 8 views
6

Ich habe zwei Datenrahmen. Die erste sieht wie folgt aus:Werte von einem Datenrahmen in einen anderen übertragen

value <- seq(1, 100, length.out=20) 
df1 <- data.frame(id=as.character(1:20), 
        value=value, 
        stringsAsFactors=F) 

Ich habe einen zweiten Datenrahmen, die

df2 <- data.frame(id=as.character(c(1:5, 21:23)), 
        v2=NA, 
        stringsAsFactors=F) 

so aussieht muss ich die Übertragung von Werten df1-df2, aber nur dort, wo df1$id == df2$id. Also der Datenrahmen, den ich brauche, ist:

df2Needed <- data.frame(id=as.character(c(1:5, 21:23)), 
         v2=c(value[1:5], NA, NA, NA), 
         stringsAsFactors=F) 

Gibt es eine Möglichkeit, dies zu tun?

+0

Haben Sie überprüfen 'merge'? –

+0

warum machst du 'id' als charakter? Es sortiert die Namen nach Zeichen, was bedeutet, dass "21" nach "2" kommt. – Arun

Antwort

3

data.table Verwendung:

require(data.table) 
dt1 <- data.table(df1, key="id") 
dt2 <- data.table(df2) 

dt1[dt2$id, value] 

# id  value 
# 1: 1 1.000000 
# 2: 2 6.210526 
# 3: 3 11.421053 
# 4: 4 16.631579 
# 5: 5 21.842105 
# 6: 21  NA 
# 7: 22  NA 
# 8: 23  NA 

oder unter Verwendung einer Base merge als @TheodoreLytras unter Kommentar erwähnt:

# you don't need to have `v2` column in df2 
merge(df2, df1, by="id", all.x=T, sort=F) 

# id v2  value 
# 1 1 NA 1.000000 
# 2 2 NA 6.210526 
# 3 3 NA 11.421053 
# 4 4 NA 16.631579 
# 5 5 NA 21.842105 
# 6 21 NA  NA 
# 7 22 NA  NA 
# 8 23 NA  NA 
+0

vielen dank an alle für ausgezeichnete hilfe, data.table hat die arbeit gut gemacht. Scheint so, als ob dieses Paket einen Besuch wert ist! – luciano

+0

@RossAhmed, ich denke, diese 'data.table' Lösung (wie agstudy zeigt), ist * nicht * die effizienteste Implementierung. Ich poste mal eine bessere. Bitte überprüfe nochmals. Ich denke, 'match' ist viel schöner/einfacher/schneller für dein Problem. Sie sollten sich die Antwort von Theodore überlegen. – Arun

+1

@Arun 'as.data.table' ist schneller als' data.table'. Normalerweise werden wir nur einmal konvertieren, da sogar 'as.data.table' das gesamte Objekt kopiert. Die Klasse kann durch Referenz ohne eine Kopie mit 'Setattr 'bei Bedarf aufgeladen werden. –

4

Mit LEFT join sql mit sqldf

library(sqldf) 
sqldf('SELECT df2.id , df1.value 
     FROM df2 
     LEFT JOIN df1 
     ON df2.id = df1.id') 

id  value 
1 1 1.000000 
2 2 6.210526 
3 3 11.421053 
4 4 16.631579 
5 5 21.842105 
6 21  NA 
7 22  NA 
8 23  NA 

EDIT hinzufügen einige benchamrking:

Die übereinstimmen wie erwartet ist sehr schnell hier. sqldf ist wirklich langsam!

-Test an den OP-Daten

library(microbenchmark) 
microbenchmark(ag(),ar.dt(),ar.me(),tl()) 

Unit: microseconds 
    expr  min   lq  median  uq  max 
1 ag() 23071.953 23536.1680 24053.8590 26889.023 34256.354 
2 ar.dt() 3123.972 3284.5890 3348.1155 3523.333 7740.335 
3 ar.me() 950.807 1015.2815 1095.1160 1128.112 6330.243 
4 tl() 41.340 45.8915 68.0785 71.112 187.735 

-Test mit großen Daten 1E6 Datenzeilen.

hier, wie erzeuge ich meine Daten:

N <- 1e6 
df1 <- data.frame(id=as.character(1:N), 
        value=seq(1, 100), 
        stringsAsFactors=F) 
n2 <- 1000 
df2 <- data.frame(id=sample(df1$id,n2), 
        v2=NA, 
        stringsAsFactors=F) 

Überraschung !! Merge ist 16 mal schneller als sqldf und data.table Lösung ist die langsamste!

Unit: milliseconds 
    expr  min  lq median  uq  max 
1 ag() 5678.0580 5865.3063 6034.9151 6214.3664 8084.6294 
2 ar.dt() 8373.6083 8612.9496 8867.6164 9104.7913 10423.5247 
3 ar.me() 387.4665 451.0071 506.8269 648.3958 1014.3099 
4 tl() 174.0375 186.8335 214.0468 252.9383 667.6246 

Wo die Funktion ag, ar.dt, ar.me, tl durch definiert sind:

ag <- function(){ 
require(sqldf) 
sqldf('SELECT df2.id , df1.value 
     FROM df2 
     LEFT JOIN df1 
     ON df2.id = df1.id') 
} 


ar.dt <- function(){ 
    require(data.table) 
    dt1 <- data.table(df1, key="id") 
    dt2 <- data.table(df2) 
    dt1[dt2$id, value] 
} 

ar.me <- function(){ 
merge(df2, df1, by="id", all.x=T, sort=F) 
} 

tl <- function(){ 
    df2Needed <- df2 
df2Needed$v2 <- df1$value[match(df2$id, df1$id)] 
} 

EDIT 2

Es scheint, dass die data.table Schöpfung einschließlich im Benchmarking ist es ein bisschen unfair. Um Verwirrung zu vermeiden, füge ich eine neue Funktion hinzu, wo ich vermute, dass ich bereits data.table-Strukturen habe.

ar.dtLight <- function(){ 
    dt1[dt2$id, value] 
} 

library(microbenchmark) 
microbenchmark(ag(),ar.dt(),ar.me(),tl(),ar.dtLight,times=1) 

Unit: microseconds 
     expr   min   lq  median   uq   max 
1  ag() 7247593.591 7247593.591 7247593.591 7247593.591 7247593.591 
2 ar.dt() 8543556.967 8543556.967 8543556.967 8543556.967 8543556.967 
3 ar.dtLight  1.139  1.139  1.139  1.139  1.139 
4 ar.me() 462235.106 462235.106 462235.106 462235.106 462235.106 
5  tl() 201988.996 201988.996 201988.996 201988.996 201988.996 

Es scheint, dass die Erstellung der Schlüssel (Indizes) der Zeitaufwand ist. Aber sobald die Indizes erstellt werden data.table Methode ist unschlagbar.

+0

Ich bin mir nicht sicher, ob 'dt1' und' dt2' erstellt werden sollen. – Arun

+0

@Arun Ich nahm an, dass data.table Erstellung schnell ist. Der Op gibt ein data.frame als Eingabe, also ist der Zwang zu einer data.table ein Teil der Lösung. – agstudy

+0

Die interessantesten Benchmarks! –

3

Eine Möglichkeit, dies mit merge() zu tun:

df2Needed <- merge(df2,df1,by="id",all.x=TRUE, sort=FALSE) 
df2Needed <- df2Needed[,c("id","value")] 
colNames(df2Needed) <- c("id","v2") 

und einem anderen (elegantere, glaube ich) mit match():

df2Needed <- df2 
df2Needed$v2 <- df1$value[match(df2$id, df1$id)] 
+0

(+1) für die Match-Lösung auswerten. – Arun

Verwandte Themen