通过data.table或dplyr中的分组列选择每个数字列的绝对值的最大值

ale*_*ing 4 r dplyr data.table

以下是我的data.frame的示例:

opts <- seq(-0.5, 0.5, 0.05)
df <- data.frame(combo1=sample(opts, 6),
                 combo2=sample(opts, 6),
                 combo3=sample(opts, 6),
                 gene=rep(c("g1", "g2", "g3"), each=2), stringsAsFactors=F)

df
   combo1 combo2 combo3 gene
1   0.40   0.50  -0.10   g1
2   0.10  -0.20  -0.35   g1
3  -0.35  -0.35   0.40   g2
4   0.00   0.10  -0.30   g2
5  -0.45  -0.10   0.05   g3
6  -0.40  -0.40  -0.05   g3
Run Code Online (Sandbox Code Playgroud)

对于每个组合,我想按基因分组,然后选择最大绝对值.我可以使用dplyr完成此任务:

library(dplyr)
df_final <- data.frame(row.names=unique(df$gene))

for (combo in colnames(df)[1:3]) {

    combo_preds <- df[, c(combo, "gene")]
    colnames(combo_preds) <- c("pred", "gene")

    combo_preds %>%
        group_by(gene) %>%
        arrange(desc(abs(pred))) %>%
        slice(1) %>%
        ungroup() ->
        combo_preds

    #add to df_final
    class(combo_preds) <- "data.frame"
    df_final[combo_preds$gene, combo] <- combo_preds$pred
}
#names rows based on gene
row.names(df_final) <- unique(df$gene)

df_final
    combo1 combo2 combo3
g1   0.40   0.50  -0.35
g2  -0.35  -0.35   0.40
g3  -0.45  -0.40   0.05
Run Code Online (Sandbox Code Playgroud)

有没有办法可以使用data.table或其他更高效的实现来完成上述操作?实际上,我有~1300 dfs,每个有~14000个基因和~650个组合.目前的实施需要每分钟2.6分钟,因此需要2天.

mat*_*fee 9

你当然可以做到data.table.(我没有对你的版本做基准测试).

library(data.table)
dt <- data.table(df)
dt[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']
Run Code Online (Sandbox Code Playgroud)

它基本上按基因对表进行分组,并在每个表的用途lapply上循环遍历每一列以查找具有最大绝对值的值.

但是,我认为你可能最好将表重新整理为长格式,但我认为这取决于你的具体数据(你必须试试看).

opts <- seq(-0.5, 0.5, 0.05)
n.combos <- 600
n.genes <- 10000
n.rows.per.gene <- 5

# columns are called X1 X2 instead of combo1 combo2 but no matter.
df.wide <- data.frame(replicate(n.combos, sample(opts, n.rows.per.gene, replace=T)),
                      gene=rep(paste0("g", 1:n.genes), each=n.rows.per.gene))
Run Code Online (Sandbox Code Playgroud)

df.wide看起来像您的数据帧,每个组合一列,每个基因的每个重复一行.

这是最初的data.table答案:

# data.table option
library(data.table)
dt <- data.table(df.wide)
system.time({
out <- dt[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']
})
#    user  system elapsed 
#  10.757   0.364  12.612
Run Code Online (Sandbox Code Playgroud)

现在我们重塑为长格式,它只有一个'组合'列和一个'值'列:

# reshape to long
dt.long <- melt(dt, id.vars='gene', variable.name='combo')
# > head(dt.long)
#    gene combo value
# 1:   g1    X1  0.20
# 2:   g1    X1  0.30
# 3:   g1    X1  0.10
# 4:   g1    X1  0.05
# 5:   g1    X1  0.30
# 6:   g2    X1  0.20

system.time({out.long <- dt.long[, value[which.max(value)], by='gene,combo']})

   user  system elapsed 
  8.000   0.472   9.525 
Run Code Online (Sandbox Code Playgroud)

这为您提供了一个数据框,其中包含最大绝对值的基因,组合和值.如果你愿意的话,你可以重新塑造它.

所以它似乎没有那么快 - 我想你将不得不尝试在你的数据上看到.我想即使第二种方法更快,你仍然需要考虑将数据表转换为long所花费的时间(这看起来并不多).

比较例如dplyr(使用非常优雅,但速度较慢)

system.time({
out.dplyr <- df.wide %>% group_by(gene) %>%
  summarise_each(funs(.[which.max(abs(.))]))
})
#   user  system elapsed 
# 163.106   7.989 189.788
Run Code Online (Sandbox Code Playgroud)