【问题标题】:Join only those columns that are non-NA仅加入那些非 NA 的列
【发布时间】:2018-04-17 09:00:14
【问题描述】:

我有一个数据集,在某些行的某些列中包含 NA:

DT <- data.table(ID=c(1, 2, 1:3), A=c(NA, NA, 1, NA, 3), B=c(4, 5, NA, 5, 6), C=c(7, 8, NA, NA, 9))
DT
#    ID  A  B  C
# 1:  1 NA  4  7
# 2:  2 NA  5  8
# 3:  1  1 NA NA
# 4:  2 NA  5 NA
# 5:  3  3  6  9

还有一个参考表

ref <- data.table(ID=c(1, 1:3), A=c(1, 1:3), B=c(1, 4:6), C=c(1, 7, NA, 9), VAL=c(111, 101:103), VAL2=c(112, 104:106))
ref
#    ID A B  C VAL VAL2
# 1:  1 1 1  1 111  112
# 2:  1 1 4  7 101  104
# 3:  2 2 5 NA 102  105
# 4:  3 3 6  9 103  106

Qn:我如何将DTref 连接起来,每行使用非NA 列?

所需的输出(为强调分组而添加了换行符):

   ID  A  B  C VAL VAL2
1:  1 NA  4  7 101  104

2:  2 NA  5  8  NA   NA

3:  1  1 NA NA 111  112
4:  1  1 NA NA 101  104

5:  2 NA  5 NA 102  105

6:  3  3  6  9 103  106

我尝试按如下方式逐行进行:

newcols <- c("VAL", "VAL2")
resLs <- lapply(split(DT, by="ID"), function(x) {
    #find those non-NA columns
    nonNACols <- names(x)[sapply(x, Negate(is.na))]

    #left join with ref table after subsetting the columns of ref table
    ref[, c(nonNACols, newcols), with=FALSE][x, on=nonNACols]
})

#combine the list of row results
ans <- rbindlist(resLs, use.names=TRUE, fill=TRUE)
setcolorder(ans, names(ref))
ans

如果解决方案可以按某种组而不是逐行完成,那会更好。有什么建议吗?


编辑:经过这么多小时终于搞定了。通过分组使用data.table:

cols <- c("ID","A", "B", "C")
newcols <- c("VAL", "VAL2")
DT[, grp := paste(names(.SD)[sapply(.SD, Negate(is.na))], collapse=""), by=seq_len(nrow(DT)), .SDcols=cols]

rbindlist(
    DT[, {
        vec <- names(.SD)[sapply(.SD, function(x) !all(is.na(x)))]
        list(list(ref[.SD, on=vec,
            c(vec, newcols), with=FALSE]))
    }, by=.(grp)]$V1,
    use.names=TRUE, fill=TRUE)

编辑:另一种编码方式

cols <- c("ID","A", "B", "C")
newcols <- c("VAL", "VAL2")
DT[, grp := paste(names(.SD)[sapply(.SD, Negate(is.na))], collapse="_"),
    by=seq_len(nrow(DT)),
    .SDcols=cols]

setnames(DT[,
    ref[.SD, on=strsplit(.BY$grp, split="_")[[1L]], 
        c(paste0("i.", cols), paste0("x.",newcols)), with=FALSE], 
    by=.(grp)][,-1L], 
    c(cols, newcols))[]

【问题讨论】:

  • 我有点不清楚,因为它看起来像列绑定,即cbind(DT, ref[, .(VAL, VAL2)])
  • 谢谢,@docendo。我在数据集中又添加了一行。希望现在更清楚
  • 仍不清楚。你为什么不直接加入ID? DT[ref, on = "ID", `:=`(VAL = i.VAL, VAL2 = i.VAL2)]其他列似乎无关紧要。
  • 啊,我知道你们都来自哪里。让我改进示例数据
  • 如果你使用sqldf 像:library(sqldf); sqldf("SELECT l.*, r.VAL, r.VAL2 FROM DT as l LEFT JOIN ref as r ON l.ID = r.ID AND (l.A = r.A OR l.A IS NULL) AND (l.B = r.B OR l.B IS NULL) AND (l.C = r.C OR l.C IS NULL)")

标签: r data.table


【解决方案1】:

一种选择是匹配A = A OR is.na(A) 等内容。但是,我认为您不能使用OR 条件来合并data.tables。对于此类复杂的合并情况,我喜欢使用 sqldf 代替:

library(sqldf)
sqldf("SELECT l.*, r.VAL, r.VAL2
       FROM       DT as l
       LEFT JOIN  ref as r
       ON         l.ID = r.ID AND (l.A = r.A OR l.A IS NULL)
                  AND (l.B = r.B OR l.B IS NULL)
                  AND (l.C = r.C OR l.C IS NULL)
                  AND (l.A IS NOT NULL OR l.B IS NOT NULL OR l.C IS NOT NULL)")

#  ID  A  B  C VAL VAL2
#1  1 NA  4  7 101  104
#2  2 NA  5  8  NA   NA
#3  1  1 NA NA 111  112
#4  1  1 NA NA 101  104
#5  2 NA  5 NA 102  105
#6  3  3  6  9 103  106

请注意,最后一个条件确保如果您的所有A, B, C 都是NA,那么它不会匹配任何行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-15
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2021-02-15
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多