首页 > 解决方案 > 替换 data.table 中的嵌套 ifelse

问题描述

我想在我的列中创建一个列,它从一组给定的列data.table中获取第一个非NA值:

library(data.table)
o <- data.table(a1 = c(1, NA, NA, NA), 
                a2 = c(NA, 2, NA, NA), 
                a3 = c(NA, NA, 3, NA),
                default = 11:14)
res <- copy(o)
res[, nc := ifelse(!is.na(a1), 
                   a1, 
                   ifelse(!is.na(a2), 
                          a2, 
                          ifelse(!is.na(a3),
                                 a3, 
                                 default)))][]
#    a1 a2 a3 default nc
# 1:  1 NA NA      11  1
# 2: NA  2 NA      12  2
# 3: NA NA  3      13  3
# 4: NA NA NA      14 14

由于有很多列,这变得相当乏味,我想用某种循环来替换它。我已经阅读了Shorten nested ifelse并且至少可以通过以下方式获得第一个非NA列的位置max.col

(col_ind <- o[, max.col(!sapply(.SD, is.na), "first"), .SDcol = patterns("^a|^default")])
# [1] 1 2 3 4

我现在如何将列位置“映射”到列?使用矩阵子集,我可以做类似的事情, o[cbind(1:NROW(o), col_ind)]但这由于明显的原因不起作用?max.col 任何人都知道如何解决这个问题(如果有一个惯用的解决方案,就不需要坚持这种方法data.table

标签: rnesteddata.table

解决方案


一些原始的想法:

1)使用fcase(), (目前在开发版本中可用):

o[, nc := fcase(
      !is.na(a1), a1,
      !is.na(a2), a2,
      !is.na(a3), a3,
      rep(TRUE, nrow(o)), as.double(default)
)]

2)使用apply()

o[, 
  nc := apply(.SD, 1L, function(x) x[!is.na(x)][1L]), 
  .SDcol = patterns("^a|^default")]

3)使用melt()

o[, row := .I]
o[, nc := o[, melt(.SD, id.vars = "row"), .SDcol = patterns("^a|^default|^row")
            ][!is.na(value), value[1L], by = row]$V1]
o[, row := NULL]

4)set()与您的col_ind矢量一起使用:

for (i in seq_len(nrow(o))) set(o, i, "nc", value = o[[col_ind[i]]][i])

5) 使用max.col()矩阵数字索引:

o[, nc := {
    m <- as.matrix(.SD)
    m[cbind(seq.int(.N), max.col(!is.na(m), "first"))]
}]

6)使用fcoalesce()

o[, names(o) := lapply(.SD, as.integer)][, 
    nc := fcoalesce(.SD)]
  • 将 5 和 6 归功于 chinsoon12。

推荐阅读