2013-04-30 41 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

相关问题