当尝试查找拆分列表的最大值时,我遇到了严重的性能问题。
有没有一种方法可以优化以下代码:
# Generate data for this MWE
x <- matrix(runif(900 * 9000), nrow = 900, ncol = 9000)
y <- rep(1:100, each = 9)
my_data <- cbind(y, x)
my_data <- data.frame(my_data)
# This is the critical part I would like to optimize
my_data_split <- split(my_data, y)
max_values <- lapply(my_data_split, function(x) x[which.max(x[ , 50]), ])
我想获得给定列达到给定组最大值的行(应该更容易从代码中理解)。
我知道拆分成列表可能是导致性能下降的原因,但是我不知道如何规避它。
答案 0 :(得分:3)
这可能不会立即告诉您。
有一个内部函数max.col
进行类似的操作,除了它沿矩阵行(而不是列)找到最大值的位置索引。因此,如果转置原始矩阵x
,则可以使用此功能。
要按组执行max.col
时,会增加复杂性。需要split
-lapply
约定。但是,如果在转置之后将矩阵转换为数据帧,则可以执行split.default
。 (请注意,它不是split
或split.data.frame
。此处数据帧被视为列表(向量),因此拆分发生在数据帧列之间。)我们执行sapply
来按组应用max.col
,并将结果cbind
应用于矩阵。
tx <- data.frame(t(x))
tx.group <- split.default(tx, y) ## note the `split.default`, not `split`
pos <- sapply(tx.group, max.col)
生成的pos
类似于查找表。它具有9000行和100列(组)。 pos[i, j]
为(原始非转置矩阵的)第i
列和第j
组的所需索引提供索引。因此,您对第50列和所有组的最终提取是
max_values <- Map("[[", tx.group, pos[50, ])
您只需生成一次查询表,然后随时进行任意提取。
此方法的缺点:
分割后,每个组中的数据都存储在数据帧中,而不是矩阵中。也就是说,例如,tx.group[[1]]
是9000 x 9数据帧。但是max.col
需要一个矩阵,因此它将内部将该数据帧转换为矩阵。
因此,主要的性能/内存开销包括:
我不确定我们是否使用MatrixStats
软件包中的某些功能来消除以上所有内容。我期待看到这样的解决方案。
但是无论如何,这个答案已经比OP最初的速度快得多。
答案 1 :(得分:1)
使用{dplyr}的解决方案:
# Generate data for this MWE
x <- matrix(runif(900 * 9000), nrow = 900, ncol = 9000)
y <- rep(1:100, each = 9)
my_data <- cbind.data.frame(y, x)
# This is the critical part I would like to optimize
system.time({
my_data_split <- split(my_data, y)
max_values <- lapply(my_data_split, function(x) x[which.max(x[ , 50]), ])
})
# Using {dplyr} is 9 times faster, but you get results in a slightly different format
library(dplyr)
system.time({
max_values2 <- my_data %>%
group_by(y) %>%
do(max_values = .[which.max(.[[50]]), ])
})
all.equal(max_values[[1]], max_values2$max_values[[1]], check.attributes = FALSE)