r - 为多组列动态创建行的替代(更快)方法

标签 r dplyr purrr rowwise across

我正在尝试为多组列自动计算每行的平均分数。 例如。一组列可以代表不同比例的项目。 这些列也被系统地命名 (scale_itemnumber)。

例如,下面的虚拟数据框包含来自三个不同比例的项目。 (可能会出现并非每个量表的所有项目都包括在内的情况,此处表示为缺失的 VAR_3)。

#library(tidyverse)
set.seed(123)
df <- tibble(  G_1 =  sample(1:5, size = 10000, replace = TRUE),
               G_2 =  sample(1:5, size = 10000, replace = TRUE),
               G_3 =  sample(1:5, size = 10000, replace = TRUE),
             MOT_1 =  sample(1:5, size = 10000, replace = TRUE),
             MOT_2 =  sample(1:5, size = 10000, replace = TRUE),
             MOT_3 =  sample(1:5, size = 10000, replace = TRUE),
             VAR_1 =  sample(1:5, size = 10000, replace = TRUE),
             VAR_2 =  sample(1:5, size = 10000, replace = TRUE),
             VAR_4 =  sample(1:5, size = 10000, replace = TRUE))

我想做的是为每个构造创建一个额外的列(具有动态名称,例如 mean_G、mean_MOT、mean_VAR),代表各自列集的行平均值。

# A tibble: 6 x 12
    G_1   G_2   G_3 MOT_1 MOT_2 MOT_3 VAR_1 VAR_2 VAR_4 mean_G mean_MOT mean_VAR
  <int> <int> <int> <int> <int> <int> <int> <int> <int>  <dbl>    <dbl>    <dbl>
1     3     3     1     1     1     1     1     5     4   2.33     1        3.33
2     3     5     3     3     2     1     4     3     5   3.67     2        4   
3     2     5     4     5     3     2     4     1     1   3.67     3.33     2   
4     2     5     4     4     4     1     2     5     4   3.67     3        3.67
5     3     4     2     1     4     5     2     2     3   3        3.33     2.33
6     5     3     4     4     3     4     1     1     4   4        3.67     2   

我实际上有一个使用 rowwise() 和 c_across() 结合 purrr 的工作方法,但与手动执行(mutate + rowMeans 组合)相比,它的执行速度太慢了。 然而,真正的 df 有更多的尺度和更多的项目,所以我宁愿不必硬编码每个平均列并插入每个项目(特别是因为包含的确切选择也可能因数据帧而异)。

#functional but slow approach

#get list of variable prefixes
var_names <- str_extract(names(df), "^.*(?=(_))") %>% 
  unique()

#use map and c_across to calculate the means rowwise per variable group

df_functional <-
      df %>% 
      bind_cols(
        map_dfc(.x = var_names, 
                .f = ~ .y %>% 
                  rowwise() %>% 
                  transmute(!!str_c("mean_", .x) := mean(c_across(starts_with(.x)))),
                .y = .))



#manual approach
df_manual <- df %>% mutate(mean_G   = rowMeans(select(., G_1,   G_2,   G_3)),
                             mean_MOT = rowMeans(select(., MOT_1,   MOT_2,   MOT_3)),
                             mean_VAR = rowMeans(select(., VAR_1,   VAR_2,   VAR_4)))

结果是相同的,但动态/功能方法要慢得多!不确定对于具有更多列/组的 dfs 会是什么样子。我如何才能在保持动态方法的灵 active 的同时加快速度?

> identical(df_manual, df_functional)
[1] TRUE

#Benchmark (using the microbenchmark package)
benchmark
Unit: milliseconds
       expr        min         lq        mean     median         uq        max neval
 functional 37198.3569 38592.6855 48313.00156 52936.3254 55349.0561 59831.0141   100
     manual    16.0662    18.0139    27.53403    19.9085    22.9384   138.5401   100

最佳答案

这应该更快:

library(dplyr, warn.conflicts = FALSE)
library(purrr)
df <- tibble(  G_1 =  sample(1:5, size = 10000, replace = TRUE),
               G_2 =  sample(1:5, size = 10000, replace = TRUE),
               G_3 =  sample(1:5, size = 10000, replace = TRUE),
               MOT_1 =  sample(1:5, size = 10000, replace = TRUE),
               MOT_2 =  sample(1:5, size = 10000, replace = TRUE),
               MOT_3 =  sample(1:5, size = 10000, replace = TRUE),
               VAR_1 =  sample(1:5, size = 10000, replace = TRUE),
               VAR_2 =  sample(1:5, size = 10000, replace = TRUE),
               VAR_4 =  sample(1:5, size = 10000, replace = TRUE))
f <- function(df){
    row_means <- split.default(df, stringr::str_remove(names(df), '_[0-9]')) %>% 
        map(rowMeans) %>% 
        setNames(paste0("mean_", names(.)))
    df %>% 
        mutate(
            !!!row_means
        )
}
    manual <- function(df) {
        df %>% mutate(
            mean_G = rowMeans(select(., G_1, G_2, G_3)),
            mean_MOT = rowMeans(select(., MOT_1, MOT_2, MOT_3)),
            mean_VAR = rowMeans(select(., VAR_1, VAR_2, VAR_4))
        )
    }
    microbenchmark::microbenchmark(prog = f(df), man = manual(df))
#> Unit: milliseconds
#>  expr    min      lq     mean   median       uq     max neval cld
#>  prog 2.6982 2.91245  3.30497  3.09260  3.30435  7.5209   100  a 
#>   man 9.1948 9.85690 10.79482 10.13105 10.81000 19.4007   100   b

reprex package 创建于 2022-07-01 (v2.0.1)

关于r - 为多组列动态创建行的替代(更快)方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72832675/

相关文章:

r - 将显着性级别添加到相关热图

r - 将一组列与另一组列相乘的整洁方法

引用 dplyr 中的一系列列

r - 如何在 R 中的多个时间序列上应用 dtw 算法?

删除数据框中变量的镜像组合

r - cex.axis 不会改变森林图中的刻度大小

r - R 中的数据帧 "expand"程序?

r - 使用 dplyr 查找前一组分组数据的平均值

r - tidyverse r 中的虚拟代码分类/序数变量

r - 在整行上按行使用 purrr 而不是 apply()