从第一列返回值以匹配条件

时间:2018-06-27 14:53:15

标签: r data.table

这似乎应该是微不足道的,但我很沮丧。

下面是问题的简单说明;真正的问题是> 1M行,> 100列。出于性能原因,我正在使用data.table,但可以接受其他建议。

“所需”列应等于x,y和z列中的第一个非NA值(按顺序排列)。

x <- c(NA,NA,"a")
y <- c(NA,"b","c")
z <- c("d","e","f")
desired <- c("d","b","a")
dt <- data.table(x,y,z,desired)

查找列索引很好:

dt[,desired.col.ind := apply(dt,1,function(x) which(!is.na(x))[1]),]

从索引中返回列名是可以的:

dt[,desired.col.name := names(dt)[desired.col.ind],]

但是我所有尝试将列索引或名称转换为其值的尝试都失败了,以下是我进行的两次更清晰的尝试。

dt[,desired.val.1 := get(desired.col.name),] # fail (returns value from column 'z' for all)
dt[,desired.val.2 := apply(desired.col.name,1,function(x) get(x)),] # error

2 个答案:

答案 0 :(得分:3)

非data.table选项。不知道这会不会更慢。

ind <- cbind(1:nrow(dt), max.col(!is.na(dt[, c('x', 'y', 'z')]), 'first'))
setDF(dt) #necessary to support array indexing
dt$desired <- dt[ind]   
dt
#      x    y z desired
# 1 <NA> <NA> d       d
# 2 <NA>    b e       b
# 3    a    c f       a

基准

dt <- data.table(x,y,z)

dt <- rbindlist(replicate(1e4, dt, simplify = F))
df <- as.data.frame(dt)

microbenchmark(
  dt = {dt[, desired := na.omit(unlist(.SD))[1], 1:nrow(dt)]},
  df1 = {ind <- cbind(1:nrow(df), apply(!is.na(df[, c('x', 'y', 'z')]), 1, which.max))
          df$desired <- df[ind] },
  df2 = {ind <- cbind(1:nrow(df), max.col(!is.na(df[, c('x', 'y', 'z')]), 'first'))
          df$desired <- df[ind] }, # akrun's imporvement to df1
  times = 10
)

# Unit: milliseconds
#  expr        min         lq       mean     median         uq        max neval
#    dt 345.570477 384.211345 403.661789 408.844811 418.096925 452.655327    10
#   df1 108.865365 116.901067 133.166031 120.619020 130.211443 186.128229    10
#   df2   1.915489   1.953233   2.987614   2.082464   2.470157   8.281857    10

答案 1 :(得分:2)

一个选项是

dt[, desired := na.omit(unlist(.SD))[1], 1:nrow(dt)]
dt
#       x    y z desired
#1: <NA> <NA> d       d
#2: <NA>    b e       b
#3:    a    c f       a

数据

dt <- data.table(x,y,z)