在 dplyr 中使用动态生成的列名

Use dynamically generated column names in dplyr

我有一个包含多列的数据框,用户提供了一个包含列名的向量,我想计算一个元素出现的最大次数

set.seed(42)
df <- tibble(
  var1 = sample(c(1:3),10,replace=T),
  var2 = sample(c(1:3),10,replace=T),
  var3 = sample(c(1:3),10,replace=T)
)
select_vars <- c("var1", "var3")

df %>% 
    rowwise() %>% 
    mutate(consensus=max(table(unlist(c(var1,var3)))))

# A tibble: 10 x 4
# Rowwise: 
    var1  var2  var3 consensus
   <int> <int> <int>     <int>
 1     1     1     1         2
 2     1     1     3         1
 3     1     2     1         2
 4     1     2     1         2
 5     2     2     2         2
 6     2     3     3         1
 7     2     3     2         2
 8     1     1     1         2
 9     3     1     2         1
10     3     3     2         1

这正是我想要的,但是当我尝试使用变量向量时我无法让它工作

df %>% 
  rowwise() %>% 
  mutate(consensus=max(unlist(table(select_vars)) )))

在OP的代码中,我们需要select

library(dplyr)
df %>% 
  rowwise() %>% 
  mutate(consensus=max(table(unlist(select(cur_data(), select_vars))) ))

-输出

# A tibble: 10 x 4
# Rowwise: 
    var1  var2  var3 consensus
   <int> <int> <int>     <int>
 1     1     1     1         2
 2     1     1     3         1
 3     1     2     1         2
 4     1     2     1         2
 5     2     2     2         2
 6     2     3     3         1
 7     2     3     2         2
 8     1     1     1         2
 9     3     1     2         1
10     3     3     2         1

或者只是 cur_data() 的子集,这只会 return 保留组属性的数据

df %>%
     rowwise %>% 
     mutate(consensus = max(table(unlist(cur_data()[select_vars]))))
# A tibble: 10 x 4
# Rowwise: 
    var1  var2  var3 consensus
   <int> <int> <int>     <int>
 1     1     1     1         2
 2     1     1     3         1
 3     1     2     1         2
 4     1     2     1         2
 5     2     2     2         2
 6     2     3     3         1
 7     2     3     2         2
 8     1     1     1         2
 9     3     1     2         1
10     3     3     2         1

或使用pmap

library(purrr)
df %>%
     mutate(consensus = pmap_dbl(cur_data()[select_vars], ~ max(table(c(...)))))
# A tibble: 10 x 4
    var1  var2  var3 consensus
   <int> <int> <int>     <dbl>
 1     1     1     1         2
 2     1     1     3         1
 3     1     2     1         2
 4     1     2     1         2
 5     2     2     2         2
 6     2     3     3         1
 7     2     3     2         2
 8     1     1     1         2
 9     3     1     2         1
10     3     3     2         1

由于这些是按行操作,如果我们使用 collapse 函数可以获得一些效率

library(collapse)
tfm(df, consensus = dapply(slt(df, select_vars), MARGIN = 1,
       FUN = function(x) fmax(tabulate(x))))
# A tibble: 10 x 4
    var1  var2  var3 consensus
 * <int> <int> <int>     <int>
 1     1     1     1         2
 2     1     1     3         1
 3     1     2     1         2
 4     1     2     1         2
 5     2     2     2         2
 6     2     3     3         1
 7     2     3     2         2
 8     1     1     1         2
 9     3     1     2         1
10     3     3     2         1

基准

如上所述,collapse 更快(运行 在稍大的数据集上)

df1 <- df[rep(seq_len(nrow(df)), 1e5), ]

system.time({
tfm(df1, consensus = dapply(slt(df1, select_vars), MARGIN = 1,
       FUN = function(x) fmax(tabulate(x))))

})
#user  system elapsed 
#  5.257   0.123   5.323 

system.time({
df1 %>%
     mutate(consensus = pmap_dbl(cur_data()[select_vars], ~ max(table(c(...)))))

})
#user  system elapsed 
# 54.813   0.517  55.246 

rowwise 操作花费太多时间,因此停止执行

df1 %>% 
   rowwise() %>% 
   mutate(consensus=max(table(unlist(select(cur_data(), select_vars))) ))
 })
Timing stopped at: 575.5 3.342 581.3

您可以将其包装在 c(!!! syms()) 中以使其正常工作,显然您不需要 unlist。但老实说,我不确定您要做什么,以及为什么这里需要 table。您是否只想检查 var2var3 是否相同,如果相同则 2 如果不相同则 1?

library(dplyr)

df <- tibble(
  var1 = sample(c(1:3),10,replace=T),
  var2 = sample(c(1:3),10,replace=T),
  var3 = sample(c(1:3),10,replace=T)
)

select_vars <- c("var2", "var3")

df %>% 
  rowwise() %>% 
  mutate(consensus=max(table(c(!!!syms(select_vars)))))

#> # A tibble: 10 x 4
#> # Rowwise: 
#>     var1  var2  var3 consensus
#>    <int> <int> <int>     <int>
#>  1     2     3     2         1
#>  2     3     1     3         1
#>  3     3     1     1         2
#>  4     3     3     3         2
#>  5     1     1     2         1
#>  6     2     1     3         1
#>  7     3     2     3         1
#>  8     1     2     3         1
#>  9     2     1     2         1
#> 10     2     1     1         2

reprex package (v0.3.0)

于 2021 年 7 月 22 日创建

你需要的是使用动词all_of

df %>% 
  rowwise() %>% 
  mutate(consensus=max(table(unlist(all_of(select_vars)))))
# A tibble: 10 x 4
# Rowwise: 
    var1  var2  var3 consensus
   <int> <int> <int>     <int>
 1     2     3     3         1
 2     2     2     2         1
 3     1     2     2         1
 4     2     3     3         1
 5     1     2     1         1
 6     2     1     2         1
 7     2     2     2         1
 8     3     1     2         1
 9     2     1     3         1
10     3     2     1         1