【问题标题】:R data.table subsetting by column valueR data.table 按列值设置子集
【发布时间】:2019-08-06 14:34:17
【问题描述】:

我想知道data.table 根据某些列值的出现选择行的最有效或最干净的方法是什么。 例如,在一个 7 列数据表中,每个值为 1 或 0,我希望所有行恰好有 2 个 1 值和 5 个 0 值(1 表示“存在”和 0“不存在”)。

到目前为止,这就是我正在做的事情,假设以下data.table(更大,这里只是一个示例)

                                    name D2A1.var D2B3.var D3A1.var D4A3.var D5B3.var H2A3.var H4A4.var MA_ancestor.var
Chrom_1;10000034;G;A Chrom_1;10000034;G;A        1        1        1        1        1        1        1               1
Chrom_1;10000035;G;A Chrom_1;10000035;G;A        1        1        1        1        1        1        1               1
Chrom_1;10000042;C;A Chrom_1;10000042;C;A        1        1        1        1        1        1        1               1
Chrom_1;10000051;A;G Chrom_1;10000051;A;G        1        1        1        1        1        1        1               1
Chrom_1;10000070;G;A Chrom_1;10000070;G;A        1        1        1        1        1        1        1               1
Chrom_1;10000084;C;T Chrom_1;10000084;C;T        1        1        1        1        1        1        1               1
Chrom_6;9997224;AT;A               Chrom_6;9997224;AT;A        0        0        0        0        0        1        0               1
Chrom_6;9998654;GTGTGTGTT;G Chrom_6;9998654;GTGTGTGTT;G        0        0        0        0        0        0        0               1
Chrom_6;9999553;TTTC;T           Chrom_6;9999553;TTTC;T        0        0        0        0        0        0        0               1

如果我想要我有 7 1 的所有行,假设 D2A1.var 和 D3A1.var 中只有 1,我正在执行以下操作

ALL = DT[DT$MA_ancestor.var == 1 & DT$D2A1.var == 1 &DT$D2B3.var == 1 & DT$D3A1.var == 1 & DT$D4A3.var == 1 &DT$D5B3.var == 1 & DT$H2A3.var == 1 & DT$H4A4.var == 1,]
TWO = DT[DT$MA_ancestor.var == 0 & DT$D2A1.var == 1 &DT$D2B3.var == 0 & DT$D3A1.var == 1 & DT$D4A3.var == 0 &DT$D5B3.var == 0 & DT$H2A3.var == 0 & DT$H4A4.var == 0,]
DFlist=list(TWO, ALL)
DFlong = rbindlist(DFlist, use.names = TRUE, idcol = FALSE)

这会返回预期的结果并且速度足够快。但是,当有多个条件时,需要大量输入和大量data.table 创作。有没有更快、更干净、更紧凑的方法来实现这一目标?

【问题讨论】:

  • “例如,在一个 7 列数据表中,每个值为 1 或 0,我希望所有行中恰好有 2 个值 1 和 5 个值 0。”我只是做Reduce("+", .SD) == 2 并将其用于子集。

标签: r data.table rows data-manipulation multiple-conditions


【解决方案1】:

我们可以通过指定感兴趣的列来使用.SDcols。循环遍历 Data.table (.SD) 的子集,创建逻辑 vector 和 Reduce 的 list 到单个逻辑 vector 和 &

ALL <- DT[, Reduce(`&`, lapply(.SD, `==`, 1), .SDcols = nm1]
TWO <- DT[, Reduce(`&`, lapply(.SD, `==`, 0), .SDcols = nm1]

在哪里

nm1 <- names(DT)[-1] #or change the names accordingly

【讨论】:

    【解决方案2】:

    另一个使用setkey的选项:

    setkeyv(DT, names(DT))
    
    #create desired filtering conditions as lists
    cond1 <- setNames(as.list(rep(1, ncol(DT))), names(DT))
    cond2 <- list(MA_ancestor.var=0, D2A1.var=1, D2B3.var=0, D3A1.var=1, D4A3.var=0, D5B3.var=0, H2A3.var=0, H4A4.var=0)
    
    #get list of conditions so that one does not have to type it one by one
    scond <- grep("^cond", ls(), value=TRUE)
    DT[rbindlist(mget(scond, envir=.GlobalEnv), use.names=TRUE)]
    

    如果您担心捡起以cond 开头的虚假变量,您可以使用list2env 将它们分配给环境并将envir 传递给mget。

    数据:

    DT <- fread("D2A1.var D2B3.var D3A1.var D4A3.var D5B3.var H2A3.var H4A4.var MA_ancestor.var
    1        1        1        1        1        1        1               1
    1        1        1        1        1        1        1               1
    1        1        1        1        1        1        1               1
    1        1        1        1        1        1        1               1
    1        1        1        1        1        1        1               1
    1        1        1        1        1        1        1               1
    0        0        0        0        0        1        0               1
    0        0        0        0        0        0        0               1
    0        0        0        0        0        0        0               1")
    

    【讨论】:

      【解决方案3】:

      有没有更快、更干净、更紧凑的方法来实现这一点?

      像您一样进行单独的查询和 rbinding 可能是最简单的。

      您可以使用replace 和连接语法来简化每个查询:

      # make a list of columns initially set to value 0
      vec0 = lapply(DT[, .SD, .SDcols=D2A1.var:MA_ancestor.var], function(x) 0)
      
      # helper function for semi join
      subit = function(x, d = DT) d[x, on=names(x), nomatch=0]
      
      rbind(
        subit(replace(vec0, names(vec0), 1)),
        subit(replace(vec0, c("D2A1.var", "D3A1.var"), 1))
      )
      

      (此代码未经测试,因为 OP 的数据不易重现。)

      您可能可以进一步简化...

      subitall = function(..., d = DT, v0 = vec0) 
        rbindlist(lapply(..., function(x) subit( replace(v0, names(v0), 1), d = d )))
      
      subitall( names(vec0), c("D2A1.var", "D3A1.var") )
      

      关于子集/半连接的函数subit,您可以根据Perform a semi-join with data.table中的答案修改它以满足您的需求


      编辑:哦,对了,按照@chinsoon 的回答,你也可以先 rbind:

      subit(rbindlist(list(
        replace(vec0, names(vec0), 1),
        replace(vec0, c("D2A1.var", "D3A1.var"), 1)      
      )))
      

      这意味着只加入一次,这样更简单。

      【讨论】:

        猜你喜欢
        • 2014-04-20
        • 2013-05-21
        • 1970-01-01
        • 2013-01-08
        • 2021-12-05
        • 2013-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多