将自定义函数应用于data.table by row会返回不正确的值

时间:2017-01-10 05:12:29

标签: r data.table genomicranges

我是data.tables的新手,我有一个包含DNA基因组坐标的表格,如下所示:

       chrom   pause strand coverage
    1:     1 3025794      +        1
    2:     1 3102057      +        2
    3:     1 3102058      +        2
    4:     1 3102078      +        1
    5:     1 3108840      -        1
    6:     1 3133041      +        1

我编写了一个自定义函数,我希望将其应用于我的大约200万行表的每一行,它使用GenomicFeatures的mapToTranscripts以字符串和新坐标的形式检索两个相关值。我想在两个新列中将它们添加到我的表中,如下所示:

       chrom   pause strand coverage       transcriptID CDS
    1:     1 3025794      +        1 ENSMUST00000116652 196
    2:     1 3102057      +        2 ENSMUST00000116652  35
    3:     1 3102058      +        2 ENSMUST00000156816 888
    4:     1 3102078      +        1 ENSMUST00000156816 883
    5:     1 3108840      -        1 ENSMUST00000156816 882
    6:     1 3133041      +        1 ENSMUST00000156816 880

该功能如下:

    get_feature <- function(dt){

      coordinate <- GRanges(dt$chrom, IRanges(dt$pause, width = 1), dt$strand) 
      hit <- mapToTranscripts(coordinate, cds_canonical, ignore.strand = FALSE) 
      tx_id <- tx_names[as.character(seqnames(hit))] 
      cds_coordinate <- sapply(ranges(hit), '[[', 1)

      if(length(tx_id) == 0 || length(cds_coordinate) == 0) {  
        out <- list('NaN', 0)
      } else {
        out <- list(tx_id, cds_coordinate)
      }

      return(out)
    } 

然后,我这样做:

    counts[, c("transcriptID", "CDS"):=get_feature(.SD), by = .I] 

我收到此错误,表示该函数返回两个长度比原始表短的列表,而不是每行一个新元素:

Warning messages:
    1: In `[.data.table`(counts, , `:=`(c("transcriptID", "CDS"),  ... :
      Supplied 1112452 items to be assigned to 1886614 items of column 'transcriptID' (recycled leaving remainder of 774162 items).
    2: In `[.data.table`(counts, , `:=`(c("transcriptID", "CDS"),  ... :
      Supplied 1112452 items to be assigned to 1886614 items of column 'CDS' (recycled leaving remainder of 774162 items).

我假设使用 .I 运算符会逐行应用该函数并返回每行一个值。我还确保函数没有使用 if 语句返回空值。

然后我尝试了这个函数的模拟版本:

    get_feature <- function(dt) {

      return('I should be returned once for each row')

    }

并称之为:

    new.table <- counts[, get_feature(.SD), by = .I] 

它创建一行数据表,而不是一个原始长度。所以我得出结论,我的函数,或者可能是我调用它的方式,是以某种方式折叠生成的向量的元素。我做错了什么?

更新(使用解决方案):正如@StatLearner所指出的那样,this answer中对此进行了解释,如?data.table中所述,.I仅用于用于j(如DT[i,j,by=])。因此,by=.I等效于by=NULL,正确的语法为by=1:nrow(dt),以便按行编号分组并逐行应用函数。

不幸的是,对于我的特殊情况,这是非常低效的,我计算了100行的执行时间为20秒。对于需要3个月才能完成的3600万行数据集。

在我的情况下,我不得不放弃并使用整个表格上的mapToTranscripts函数,这需要几秒钟,显然是预期用途。

    get_features <- function(dt){
      coordinate <- GRanges(dt$chrom, IRanges(dt$pause, width = 1), dt$strand) # define coordinate
      hits <- mapToTranscripts(coordinate, cds_canonical, ignore.strand = FALSE) # map it to a transcript
      tx_hit <- as.character(seqnames(hits)) # get transcript number
      tx_id <- tx_names[tx_hit] # get transcript name from translation table

      return(data.table('transcriptID'= tx_id, 
                       'CDS_coordinate' =  start(hits))
    }

     density <- counts[, get_features(.SD)]

然后使用mapFromTranscripts包中的GenomicFeatures映射回基因组,这样我就可以使用data.tables联接从原始表中检索信息,这是我的目的试图做。

1 个答案:

答案 0 :(得分:4)

当我需要为data.table中的每一行应用函数时,我这样做的方法是按行号对其进行分组:

counts[, get_feature(.SD), by = 1:nrow(counts)]

this answer中所述,.I不适合在by中使用,因为它应返回通过分组生成的行索引序列。 by = .I不会抛出错误的原因是data.table在data.table命名空间中创建对象.I等于NULL,因此by = .I等同于{{1 }}

请注意,按行号使用by = NULL组,并允许您的函数只访问data.table中的一行:

by=1:nrow(dt)

将生成一个与require(data.table) counts <- data.table(chrom = sample.int(10, size = 100, replace = TRUE), pause = sample((3 * 10^6):(3.2 * 10^6), size = 100), strand = sample(c('-','+'), size = 100, replace = TRUE), coverage = sample.int(3, size = 100, replace = TRUE)) get_feature <- function(dt){ coordinate <- data.frame(dt$chrom, dt$pause, dt$strand) rowNum <- nrow(coordinate) return(list(text = 'Number of rows in dt', rowNum = rowNum)) } counts[, get_feature(.SD), by = 1:nrow(counts)] 中行数相同的data.table,但counts只包含coordinate中的一行

counts

nrow text rowNum 1: 1 Number of rows in dt 1 2: 2 Number of rows in dt 1 3: 3 Number of rows in dt 1 4: 4 Number of rows in dt 1 5: 5 Number of rows in dt 1 将为函数提供整个data.table:

by = NULL

这是counts[, get_feature(.SD), by = NULL] text rowNum 1: Number of rows in dt 100 工作的预期方式。