在R条件下左外连接

时间:2016-07-26 13:28:24

标签: sql r dataframe merge

有没有办法通过多列合并(左外连接)数据帧,但是有OR条件?

示例:列df1有两个数据框df2x, y, num。我希望有一个包含df1所有行的数据框,但只有df2中符合条件的行df1$x == df2$xdf2$y == df2y

以下是示例数据:

df1 <- data.frame(x = LETTERS[1:5],
                  y = 1:5,
                  num = rnorm(5), stringsAsFactors = F)
df1
  x y       num
1 A 1 0.4209480
2 B 2 0.4687401
3 C 3 0.3018787
4 D 4 0.0669793
5 E 5 0.9231559

df2 <- data.frame(x = LETTERS[3:7],
                  y = 3:7,
                  num = rnorm(5), stringsAsFactors = F)
df2$x[4] <- NA
df2$y[3] <- NA
df2
     x  y        num
1    C NA -0.7160824
2 <NA>  4 -0.3283618
3    E  5 -1.8775298
4    F  6 -0.9821082
5    G  7  1.8726288

然后,结果预计是:

  x y       num    x  y        num
1 A 1 0.4209480 <NA> NA         NA
2 B 2 0.4687401 <NA> NA         NA
3 C 3 0.3018787    C NA -0.7160824
4 D 4 0.0669793 <NA>  4 -0.3283618
5 E 5 0.9231559    E  5 -1.8775298

最明显的解决方案是使用sqldf包:

mergedData <- sqldf::sqldf("SELECT * FROM df1
                           LEFT OUTER JOIN df2
                           ON df1.x = df2.x
                           OR df1.y = df2.y")

不幸的是,这个简单的解决方案非常慢,并且合并每个行数超过100k的数据帧需要很长时间。

另一个选择是分割正确的数据框并按部分合并,但它更优雅甚至是开箱即用的#34;溶液

1 个答案:

答案 0 :(得分:1)

以下是使用data.table的一种方法。对于每一列,我们执行连接,但只提取索引(而不是实现整个连接)。然后,我们可以组合所有列中的这些索引(如果可以有多个匹配,这部分需要进行一些更改)

require(data.table)
setDT(df1)
setDT(df2)

foo <- function(dx, dy, cols) {
    ix = lapply(cols, function(col) {
        dy[dx, on=col, which=TRUE] # for each row in dx, get matching indices of dy
                                   # by matching on column specified in "col"
    })
    ix = do.call(function(...) pmax(..., na.rm=TRUE), ix)
}
ix = foo(df1, df2, c("x", "y")) # obtain matching indices of df2 for each row in df1
df1[, paste0("col", 1:3) := df2[ix]] # update df1 by reference
df1
#    x y         num col1 col2       col3
# 1: A 1  2.09611034   NA   NA         NA
# 2: B 2 -1.06795571   NA   NA         NA
# 3: C 3  1.38254433    C    3  1.0173476
# 4: D 4 -0.09367922    D    4 -0.6379496
# 5: E 5  0.47552072    E   NA -0.1962038

如果需要,您可以使用setDF(df1)将其转换回data.frame。