【问题标题】:Return value from first column to match condition从第一列返回值以匹配条件
【发布时间】:2018-06-27 14:53:14
【问题描述】:

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

以下是问题的简单说明;真正的问题是 > 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

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    非 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
    

    【讨论】:

    • 我认为max.col 会更快,即cbind(seq_len(nrow(dt)), max.col(!is.na(dt), 'first'))
    • 谢谢@akrun,这样更快。我不知道这个功能。
    【解决方案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)
    

    【讨论】:

    • 如何推广到仅在列的子集中查找非 NA?
    • @peachy,只需在.SDcols 中指定感兴趣的列,即dt[, desired := na.omit(unlist(.SD))[1], 1:nrow(dt), .SDcols = x:y]
    猜你喜欢
    • 2012-12-31
    • 1970-01-01
    • 2018-01-21
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 2020-08-26
    • 2015-11-30
    • 1970-01-01
    相关资源
    最近更新 更多