【问题标题】:R: Performance issue when finding maximum of splitted listR:查找最大拆分列表时的性能问题
【发布时间】:2018-07-15 19:47:10
【问题描述】:

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

有没有办法优化下面的代码:

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

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

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

【问题讨论】:

    标签: r performance list split lapply


    【解决方案1】:

    这对你来说可能不是很清楚。

    有一个内部函数max.col 做类似的事情,除了它沿着矩阵行(而不是列)找到最大值的位置索引。所以如果你转置你原来的矩阵x,你就可以使用这个函数了。

    当您想按组执行max.col 时,复杂性会增加。需要split-lapply 约定。但是,如果在转置之后,我们将矩阵转换为数据帧,我们可以做split.default。 (注意它不是splitsplit.data.frame。这里的数据框被视为一个列表(向量),因此拆分发生在数据框列之间。)最后,我们做一个@ 987654329@ 按组应用max.colcbind 将结果应用到矩阵中。

    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-th 列(原始非转置矩阵)和j-th 组提供了您想要的索引。因此,您对第 50 列和所有组的最终提取是

    max_values <- Map("[[", tx.group, pos[50, ])
    

    查找表生成一次,随时任意抽取。


    这种方法的缺点:

    拆分后,每个组中的数据都存储在数据框而不是矩阵中。也就是说,例如,tx.group[[1]] 是一个 9000 x 9 的数据帧。但是max.col 需要一个矩阵,因此它会在内部将此数据帧转换为矩阵。

    因此,主要的性能/内存开销包括:

    • 初始矩阵转置;
    • 矩阵到数据帧的转换;
    • 数据帧到矩阵的转换(每组)。

    我不确定我们是否通过 MatrixStats 包中的某些功能消除了上述所有内容。我期待看到解决方案。

    但无论如何,这个答案已经比 OP 最初的答案快得多了。

    【讨论】:

    • 非常感谢您的全面回答。这对我帮助很大。
    【解决方案2】:

    使用 {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)
    

    【讨论】:

    • 非常感谢您的回答。我认为我应该开始使用 dplyr 包,因为它被广泛使用并且具有非常清晰的合成器。
    猜你喜欢
    • 2019-05-21
    • 1970-01-01
    • 2020-03-01
    • 2021-07-28
    • 2014-01-07
    • 2023-04-03
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    相关资源
    最近更新 更多