R:查找最大拆分列表时的性能问题

时间:2018-07-15 19:47:10

标签: r performance list split lapply

当尝试查找拆分列表的最大值时,我遇到了严重的性能问题。

有没有一种方法可以优化以下代码:

# 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]), ])

我想获得给定列达到给定组最大值的行(应该更容易从代码中理解)。

我知道拆分成列表可能是导致性能下降的原因,但是我不知道如何规避它。

2 个答案:

答案 0 :(得分:3)

这可能不会立即告诉您。

有一个内部函数max.col进行类似的操作,除了它沿矩阵行(而不是列)找到最大值的位置索引。因此,如果转置原始矩阵x,则可以使用此功能。

要按组执行max.col时,会增加复杂性。需要split-lapply约定。但是,如果在转置之后将矩阵转换为数据帧,则可以执行split.default。 (请注意,它不是splitsplit.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)