【问题标题】:Use data.table to select non-unique rows使用 data.table 选择非唯一行
【发布时间】:2013-07-04 21:03:49
【问题描述】:

我有一个大表,其中包含几个具有相关值的基因 (newID)。有些基因 (newID) 是唯一的,有些有多个实例(出现在多行中)。如何从表中排除仅出现一次(行)的那些?在下面的示例中,只有最后一行会被删除,因为它是唯一的。

head(exons.s, 10)
                       Row.names exonID    pvalue log2fold.5_t.GFP_t.              newID
1  ENSMUSG00000000001_Gnai3:E001   E001 0.3597070         0.029731989 ENSMUSG00000000001
2  ENSMUSG00000000001_Gnai3:E002   E002 0.6515167         0.028984837 ENSMUSG00000000001
3  ENSMUSG00000000001_Gnai3:E003   E003 0.8957798         0.009665072 ENSMUSG00000000001
4  ENSMUSG00000000001_Gnai3:E004   E004 0.5308266        -0.059273822 ENSMUSG00000000001
5  ENSMUSG00000000001_Gnai3:E005   E005 0.4507640        -0.061276835 ENSMUSG00000000001
6  ENSMUSG00000000001_Gnai3:E006   E006 0.5147357        -0.068357886 ENSMUSG00000000001
7  ENSMUSG00000000001_Gnai3:E007   E007 0.5190718        -0.063959853 ENSMUSG00000000001
8  ENSMUSG00000000001_Gnai3:E008   E008 0.8999434         0.032186993 ENSMUSG00000000001
9  ENSMUSG00000000001_Gnai3:E009   E009 0.5039369         0.133313175 ENSMUSG00000000001
10  ENSMUSG00000000003_Pbsn:E001   E001        NA                  NA ENSMUSG00000000003
> dim(exons.s)
[1] 234385      5

使用 plyr 我会这样做:

## remove single exon genes:
multEx <- function(df){
   if (nrow(df) > 1){return(df)}
}

genes.mult.ex <- ddply(exons.s , .(newID), multEx, .parallel=TRUE)

但这很慢。我认为使用 data.table 会很容易,但我想不通:

exons.s <- data.table(exons.s, key="newID")
x.dt.out <- exons.s[, lapply(.SD, multEx), by=newID]

我是 data.table 的新手,因此欢迎任何指向正确方向的指针。

【问题讨论】:

    标签: r unique data.table subset


    【解决方案1】:

    创建一个列,给出每个组中的行数,然后是子集:

    exons.s[,n:=.N,by=newID]
    exons.s[n>1]
    

    【讨论】:

    • 或者,如果您不想分配新列:exons.s[,c(.SD,n=.N),by=newID][n&gt;1]
    • 就语法简洁而言,我认为这是最自然的版本:exons.s[, .SD[.N&gt;1], by=newID]
    【解决方案2】:

    使用 duplicated() 函数而不是计算组大小,有一种更简单、更有效的方法。

    首先我们需要生成一个测试数据集:

    # Generate test datasets
    smallNumberSampled <- 1e3
    largeNumberSampled <- 1e6
    
    smallDataset <- data.table(id=paste('id', 1:smallNumberSampled, sep='_'), value1=sample(x = 1:26, size = smallNumberSampled, replace = T), value2=letters[sample(x = 1:26, size = smallNumberSampled, replace = T)])
    largeDataset <- data.table(id=paste('id', 1:largeNumberSampled, sep='_'), value1=sample(x = 1:26, size = largeNumberSampled, replace = T), value2=letters[sample(x = 1:26, size = largeNumberSampled, replace = T)])
    
    # add 2 % duplicated rows:
    smallDataset <- rbind(smallDataset, smallDataset[sample(x = 1:nrow(smallDataset), size = nrow(smallDataset)* 0.02)])
    largeDataset <- rbind(largeDataset, largeDataset[sample(x = 1:nrow(largeDataset), size = nrow(largeDataset)* 0.02)])
    

    然后我们将这三种解决方案实现为函数:

    # Original suggestion
    getDuplicatedRows_Count <- function(dt, columnName) {
        dt[,n:=.N,by=columnName]
        return( dt[n>1] )
    }
    
    # Duplicated using subsetting
    getDuplicatedRows_duplicated_subset <- function(dt, columnName) {
        # .. means "look up one level"
        return( dt[which( duplicated(dt[, ..columnName]) | duplicated(dt[, ..columnName], fromLast = T)  ),] )
    }
    
    # Duplicated using the "by" argument to avoid copying
    getDuplicatedRows_duplicated_by <- function(dt, columnName) {
        return( dt[which( duplicated(dt[,by=columnName]) | duplicated(dt[,by=columnName], fromLast = T)  ),] )
    }
    

    然后我们测试它们是否给出相同的结果

    results1 <- getDuplicatedRows_Count     (smallDataset, 'id')
    results2 <- getDuplicatedRows_duplicated_subset(smallDataset, 'id')
    results3 <- getDuplicatedRows_duplicated_by(smallDataset, 'id')
    
    > identical(results1, results2)
    [1] TRUE
    > identical(results2, results3)
    [1] TRUE
    

    我们对 3 个解决方案的平均性能进行计时:

    # Small dataset
    > system.time( temp <- replicate(n = 100, expr =  getDuplicatedRows_Count            (smallDataset, 'id')) ) / 100
    user    system  elapsed 
    0.00176 0.00007 0.00186 
    > system.time( temp <- replicate(n = 100, expr =  getDuplicatedRows_duplicated_subset(smallDataset, 'id')) ) / 100
    user    system  elapsed 
    0.00206 0.00005 0.00221 
    > system.time( temp <- replicate(n = 100, expr =  getDuplicatedRows_duplicated_by    (smallDataset, 'id')) ) / 100
    user    system  elapsed 
    0.00141 0.00003 0.00147 
    
    #Large dataset
    > system.time( temp <- replicate(n = 100, expr =  getDuplicatedRows_Count            (largeDataset, 'id')) ) / 100
    user    system  elapsed 
    0.28571 0.01980 0.31022 
    > system.time( temp <- replicate(n = 100, expr =  getDuplicatedRows_duplicated_subset(largeDataset, 'id')) ) / 100
    user    system  elapsed 
    0.24386 0.03596 0.28243 
    > system.time( temp <- replicate(n = 100, expr =  getDuplicatedRows_duplicated_by    (largeDataset, 'id')) ) / 100
    user    system   elapsed 
    0.22080 0.03918  0.26203  
    

    这表明 duplicated() 方法的扩展性更好,尤其是在使用“by=”选项的情况下。

    更新:2014 年 11 月 21 日。相同输出的测试(正如 Arun 建议的那样 - 谢谢)发现我使用 data.table v 1.9.2 存在问题,其中重复的 fromLast 不起作用。我更新到 v 1.9.4 并重新进行了分析,现在差异要小得多。

    更新:2014 年 11 月 26 日。包含并测试了从 data.table 中提取列的“by=”方法(正如 Arun 所建议的那样,功劳就在那里)。此外,运行时测试平均超过 100 次测试,以确保结果的正确性。

    【讨论】:

    • 答案是否相同?
    • 谢谢。 duplicated 用于 data.tables 也有一个 by= 参数用于指定列名,而不是必须子集(复制 data.table)。
    • 再次(多么好的习惯)好建议 Arun - 我已经相应地更新了答案,并且 by= 参数效果更好。
    猜你喜欢
    • 1970-01-01
    • 2017-03-11
    • 2012-08-01
    • 2017-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多