2013-04-30 57 views
4

我一直在尋找解決方案並一直在嘗試,但似乎無法執行我應該做的簡單任務。通過至少兩列中的一個匹配合並數據幀

我有兩個數據幀格式化類似於下面玩具實例

DF1 = data.frame(A=c("cats","dogs",NA,"dogs"), B=c("kittens","puppies","kittens",NA), C=c(88,99,101,110)) 

    A  B   C 
1 cats kittens  88 
2 dogs puppies  99 
3 NA  kittens  101 
4 dogs NA   110 


DF2 = data.frame(D=c(1,2), A=c("cats","dogs"), B=c("kittens","puppies")) 

    D A  B 
1 1 cats kittens 
2 2 dogs puppies 

我希望合併這兩個數據集,使得輸出是:

 A  B   C  D 
1 cats kittens 88 1 
2 dogs puppies 99 2 
3 dogs NA  110 2 
4  NA kittens 101 1 

換句話說,任何行帶有標籤A ==「貓」或B ==「小貓」將被映射到列D中的1,任何具有A ==「狗」或B ==「小狗」的行將被映射到2.

我已經使用命令

merge(DF1, DF2, by=c("A","B"), all.x=TRUE) 

然而,這並不正確匹配的行3和第4,僅行1和2。我得到的輸出

 A  B   C  D 
1 cats kittens 88 1 
2 dogs puppies 99 2 
3 dogs NA  110 NA 
4  NA kittens 101 NA 

請注意我的工作實際數據集是很長。事實上DF1超過1,000,000行,DF2超過300,000行每行數千行,所以可以縮放的解決方案就是我真正需要的。

+0

你已經取得相當大的你的榜樣。小心使它更容易複製/粘貼到R?以下是有關如何執行此操作的一些提示:http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example – 2013-04-30 06:25:19

+1

您的示例輸出與您的說明不符。不應該列「D」是'c(1,2,2,1)'? – A5C1D2H2I1M1N2O1R2T1 2013-04-30 06:27:49

+0

你完全正確,我已經對此進行了修改以避免混淆。謝謝。 – Starcalibre 2013-04-30 06:40:29

回答

2

這裏有一個不同的方法:

library(functional) 

partial.merge <- function(DF1, DF2) { 
    common.cols <- intersect(names(DF1), names(DF2)) 
    result.col <- names(DF2)[!(names(DF2) %in% common.cols)] 

    # This can only handle one result column: 
    stopifnot(length(result.col) == 1) 

    # Merge in each common column, one at a time. 
    # The identical operation is done for each common column, so Reduce is useful: 
    r <- Reduce(function(D, C) merge(D, DF2[c(C, result.col)], by=c(C), all.x=TRUE), x=common.cols, init=DF1) 

    # The merge created cols like c('D.x', 'D.y'). These are the columns: 
    merge.cols <- paste(result.col, c('x', 'y'), sep='.') 

    # The .x and .y columns are partial, put them together: 
    r[[result.col]] <- rowMeans(r[merge.cols], na.rm=TRUE) 

    # Remove the temporaries: 
    for (i in merge.cols) { 
    r[[i]] <- NULL 
    } 
    return(r) 
} 

partial.merge(DF1, DF2) 
##   B A C D 
## 1 kittens cats 88 1 
## 2 kittens <NA> 101 1 
## 3 puppies dogs 99 2 
## 4 <NA> dogs 110 2 
+0

雖然我不介意你選擇我的答案是否正確,但我建議你實際嘗試一下你的真實數據,並用'system.time()'檢查運行時間。這幾乎肯定是三者中最慢的! – 2013-04-30 13:50:16

3

也許你可以嘗試的東西沿着這些路線:

temp <- merge(DF1, DF2, by=c("A","B"), all.x=TRUE) 

within(temp, { 
    M1 <- c("cats", "kittens") 
    D <- ifelse(A %in% M1 | B %in% M1, 1, 2) 
    rm(M1) 
}) 
#  A  B C D 
# 1 cats kittens 88 1 
# 2 dogs puppies 99 2 
# 3 dogs <NA> 110 2 
# 4 <NA> kittens 101 1 

可以嵌套如果ifelse語句您需要的不僅僅是這兩個選項的更多。

+0

謝謝,這適用於玩具示例,但我的原始數據集非常長(有數千個不同的對)。是否有解決方案可以用於真正的大型數據框? – Starcalibre 2013-04-30 07:07:21

+0

爲什麼它不適用於更大的數據集? – 2013-04-30 07:11:22

2
DF1[which(DF1$A=="cats"|DF1$B=="kittens"), "D"] <- DF2[which(DF2$A=="cats"|DF2$B=="kittens"), "D"] 
DF1[which(DF1$A=="dogs"|DF1$B=="puppies"), "D"] <- DF2[which(DF2$A=="dogs"|DF2$B=="puppies"), "D"] 
DF1 
#------- 
    A  B C D 
1 cats kittens 88 1 
2 dogs puppies 99 2 
3 <NA> kittens 101 1 
4 dogs <NA> 110 2 

功能化:

idxpick <- function(a,b) DF1[which(DF1$A==a|DF1$B==b), "D"] <<- # Yes, I feel guilty. 
            DF2[which(DF2$A==a|DF2$B==b), "D"] 
DF1 = data.frame(A=c("cats","dogs",NA,"dogs"), 
       B=c("kittens","puppies","kittens",NA), 
       C=c(88,99,101,110)) 
DF2 = data.frame(D=c(1,2), A=c("cats","dogs"), B=c("kittens","puppies")) 
apply(DF2, 1, function(rr) idxpick(rr["A"], rr["B"])) 
#------------ 
[1] 1 2 

DF1 
    A  B C D 
1 cats kittens 88 1 
2 dogs puppies 99 2 
3 <NA> kittens 101 1 
4 dogs <NA> 110 2 
+0

嘿,謝謝你的解決方案。對不起,但我應該已經更清楚,我正在使用的數據集非常大,所以爲每個可能的對編寫命令並不是真的可行。大約有300,000種不同類型的配對。 – Starcalibre 2013-04-30 07:20:30

+0

可以將它變成可應用於DF2的函數。這將需要很長時間。可能需要在一夜之間離開。 – 2013-04-30 07:30:54

+0

在野外發現了一種罕見的「<< - 」,整潔。 – Ben 2013-04-30 07:42:05

相關問題