使用data.table选择非唯一的行

3

我有一个由多个基因(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还不熟悉,所以欢迎任何指导。

2个回答

5
创建一个列,用于给出每个组中的行数,然后进行子集操作:
exons.s[,n:=.N,by=newID]
exons.s[n>1]

或者,如果您不想分配新列:exons.s[,c(.SD,n=.N),by=newID][n>1] - Ricardo Saporta
1
就语法简洁性而言,我认为这是最自然的版本:exons.s[, .SD[.N>1], by=newID] - eddi

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 v1.9.2中duplicated的fromLast无法工作。我更新到v1.9.4并重新进行了分析,现在差异小得多。
更新:2014年11月26日。包括并测试了从data.table中提取列的“by=”方法(由Arun建议,功劳归于他)。此外,运行时的测试平均超过100个测试以确保结果正确。

答案是否相同? - Arun
data.tables 中的 duplicated 函数也有一个 by= 参数,用于指定列名,而不必对数据表进行子集操作(这会复制数据表)。 - Arun
1
再次感谢Arun的好建议 - 我已经相应地更新了答案,而且by=参数的效果更好。 - Kristoffer Vitting-Seerup

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接