这似乎应该是微不足道的,但我很沮丧。
下面是问题的简单说明;真正的问题是> 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
答案 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)