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

通过data.table或dplyr中的分组列为每个数值列选择绝对值的最大值,r,data.table,dplyr,R,Data.table,Dplyr,下面是my 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), stringsAsFact

下面是my 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
选择
普雷德斯组合酒店
#添加到df_最终版本

类(combo_preds)基类R中的一个方法可以工作(还有许多其他方法)


这里有一个更简单、更快的
dplyr
方法:

df %>% group_by(gene) %>%
  summarise_each(funs(.[which.max(abs(.))]))
在可复制的数据上尝试:

set.seed(495)
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
请注意,在上述情况下,
combo2
gene=g1
的绝对值是相等的。如果这很重要,你需要决定如何打破关系

我的
dplyr
方法和@mathematic.coffee的
data.table
方法的稍快版本的计时(使用更大的样本数据框):

因此,
data.table
版本的运行时间大约是
dplyr
版本的一半

更新:为了回应@Arun的评论,这里有一个更大的样本数据框,包含更多的列和更多的
基因
类别

# Large sample of fake data
set.seed(194)
genes=apply(expand.grid(letters,letters), 1, paste, collapse="")
df = data.frame(replicate(50, rnorm(26*26*1e3)), gene=genes)
object.size(df)
# 273 MB

microbenchmark::microbenchmark(
  dplyr=setDF(df) %>% group_by(gene) %>%
    summarise_each(funs(.[which.max(abs(.))])),
  data.table={setDT(df)[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']},
  times=10
)
更新2:与上述相同,但组数更大(26^3而不是26^2)。正如@Arun所讨论的,
data.table
速度优势随着组数的增加而增加

   # Large sample of fake data
   set.seed(194)
   genes=apply(expand.grid(letters,letters,letters), 1, paste, collapse="")
   df = data.frame(replicate(50, rnorm(26*26*26*50)), gene=genes)
   object.size(df)
   # 356 MB

   microbenchmark::microbenchmark(
     dplyr=setDF(df) %>% group_by(gene) %>%
       summarise_each(funs(.[which.max(abs(.))])),
     data.table={setDT(df)[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']},
     times=1
   )       

您当然可以使用
data.table
来完成此操作。(我没有针对您的版本进行基准测试)

这里的
df.wide
看起来像您的数据框,每个组合一列,每个基因的每个复制一行

这是原始的
数据。表
答案:

# 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

@Mathematic.coffee的答案对于lapply来说很好,但是有一种更具数据表的方法来实现相同的目标:

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

dt

很晚了,但希望这对其他人有所帮助:(

在创建随机示例数据集之前,您可能希望使用
set.seed
。不,这是正确的,除非OP希望最大绝对值而不是最大值。您的基准数据大小为2.7MB(甚至完全适合缓存),而Mathematic.coffee的容量约为230MB。如果没有合理大小的数据,很难了解工具的性能/伸缩性。我认为这也是因为他/她的数据集有很多列。并且,
dplyr
分别计算每一列(以便您可以在其中直接参考上一列),只要函数针对混合求值进行了优化,这就可以了,但在其他方面效率很低。与原始方法相比,可以看到更好的代码,但由于某些原因,需要花费大约一分钟的时间(2.62分钟vs 3.53分钟).相比之下,@mathematic.coffee的data.table方法的计时时间为42秒。@eipi10,感谢您的跟进。虽然不是全部,但要好得多。这有助于更好地理解性能差异的原因。谢谢。
df %>% group_by(gene) %>%
  summarise_each(funs(.[which.max(abs(.))]))
   gene combo1 combo2 combo3
1    g1  -0.45   0.50  -0.25
2    g2   0.35  -0.40  -0.30
3    g3   0.15  -0.35  -0.40
set.seed(495)
opts <- seq(-0.5, 0.5, 0.05)
df <- data.frame(combo1=sample(opts, 9e4, replace=TRUE),
                 combo2=sample(opts, 9e4, replace=TRUE),
                 combo3=sample(opts, 9e4, replace=TRUE),
                 gene=rep(c("g1", "g2", "g3"), each=3e4), stringsAsFactors=F)

microbenchmark::microbenchmark(
  dplyr=setDF(df) %>% group_by(gene) %>%
    summarise_each(funs(.[which.max(abs(.))])),
  data.table={setDT(df)[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']}
)
Unit: milliseconds
      expr       min        lq      mean    median        uq      max neval cld
     dplyr 10.013623 11.839132 14.156735 12.284574 12.675220 32.35739   100   b
data.table  4.434841  6.008701  6.947104  6.222775  6.415083 29.52652   100  a
# Large sample of fake data
set.seed(194)
genes=apply(expand.grid(letters,letters), 1, paste, collapse="")
df = data.frame(replicate(50, rnorm(26*26*1e3)), gene=genes)
object.size(df)
# 273 MB

microbenchmark::microbenchmark(
  dplyr=setDF(df) %>% group_by(gene) %>%
    summarise_each(funs(.[which.max(abs(.))])),
  data.table={setDT(df)[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']},
  times=10
)
Unit: milliseconds
      expr       min        lq      mean    median        uq       max neval cld
     dplyr 1240.1695 1299.0425 1375.8298 1318.5343 1385.5854 1748.8112    10   b
data.table  464.5597  493.8959  527.7097  519.3607  585.1482  603.3916    10  a
   # Large sample of fake data
   set.seed(194)
   genes=apply(expand.grid(letters,letters,letters), 1, paste, collapse="")
   df = data.frame(replicate(50, rnorm(26*26*26*50)), gene=genes)
   object.size(df)
   # 356 MB

   microbenchmark::microbenchmark(
     dplyr=setDF(df) %>% group_by(gene) %>%
       summarise_each(funs(.[which.max(abs(.))])),
     data.table={setDT(df)[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']},
     times=1
   )       
   Unit: seconds
             expr       min        lq      mean    median        uq       max neval
            dplyr 27.567790 27.567790 27.567790 27.567790 27.567790 27.567790     1        
       data.table  2.765047  2.765047  2.765047  2.765047  2.765047  2.765047     1
library(data.table)
dt <- data.table(df)
dt[, lapply(.SD, function (col) col[which.max(abs(col))]), by='gene']
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))
# 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
# 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 
system.time({
out.dplyr <- df.wide %>% group_by(gene) %>%
  summarise_each(funs(.[which.max(abs(.))]))
})
#   user  system elapsed 
# 163.106   7.989 189.788
opts <- seq(-0.5, 0.5, 0.05)
dt <- data.table(combo1=sample(opts, 6),
                 combo2=sample(opts, 6),
                 combo3=sample(opts, 6),
                 gene=rep(c("g1", "g2", "g3"), each=2))

dt
   combo1 combo2 combo3 gene 
1:  -0.20  -0.40  -0.10   g1 
2:   0.15   0.15   0.40   g1 
3:   0.35   0.10  -0.05   g2 
4:   0.45  -0.15  -0.25   g2 
5:   0.00  -0.25   0.50   g3 
6:   0.10   0.20   0.25   g3
dt2 <- dt[, .(combo1=max(combo1), combo2=max(combo2), combo3=max(combo3)), 
            keyby=gene]

dt2
   gene combo1 combo2 combo3
1:   g1   0.15   0.15   0.40
2:   g2   0.45   0.10  -0.05
3:   g3   0.10   0.20   0.50