这里有一个更简单更快的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
combo1 combo2 combo3 gene
1 -0.15 0.50 -0.25 g1
2 -0.45 -0.50 0.15 g1
3 -0.25 0.10 -0.30 g2
4 0.35 -0.40 -0.15 g2
5 -0.05 -0.35 -0.40 g3
6 0.15 -0.05 -0.10 g3
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
请注意,在上述情况下,combo2 和 gene=g1 的绝对值是相同的。如果这很重要,您将需要决定如何打破关系。
我的dplyr 方法和@mathematical.coffee 的data.table 方法的稍快版本的时间安排(使用更大的样本数据框):
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
所以data.table 版本的运行时间大约是dplyr 版本的一半。
更新:为了解决@Arun 的评论,这里有一个更大的示例数据框,其中包含更多列和更多gene 类别。
# 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
更新 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
)
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