您可以使用 dplyr across() 来迭代成对的列吗?

问题描述 投票:0回答:5

我有 18 对变量,我想对它们进行成对数学运算以计算 18 个新变量。将公式应用于一列时,dplyr 中的 across() 函数非常方便。有没有办法将 across() 应用于成对的列?

简单除以 2 个变量的小示例(我的实际代码会更复杂,有些 ifelse,...):

library(tidyverse)
library(glue)

# filler data
df <- data.frame("label" = c('a','b','c','d'),
                 "A" = c(4, 3, 8, 9),
                 "B" = c(10, 0, 4, 1),
                 "error_A" = c(0.4, 0.3, 0.2, 0.1),
                 "error_B" = c(0.3, 0, 0.4, 0.1))

# what I want to have in the end 
# instead of just 2 (A, B), I have 18
df1 <- df %>% mutate(
  'R_A' = A/error_A,
  'R_B' = B/error_B
)

# what I'm thinking about doing to use both variables A and error_A to calculate the new column
df2 <- df %>% mutate(
  across(c('A','B'),
         ~.x/{HOW DO I USE THE COLUMN WHOSE NAME IS glue('error_',.x)}
         .names = 'R_{.col}'
)
r dplyr tidyverse across
5个回答
8
投票

一个选项是

map/reduce
。 指定感兴趣的列 ('nm1'),在
map
select
数据集中的那些列、
reduce
通过划分、
rename
列绑定后的列 (
_dfc
) 中循环,然后绑定那些具有原始数据集的

library(dplyr)
library(purrr)
library(stringr)
nm1 <- c('A', 'B')
map_dfc(nm1, ~ df %>% 
                select(ends_with(.x)) %>% 
                reduce(., `/`) ) %>%
    rename_all(~ str_c('R_', nm1)) %>%
    bind_cols(df, .)

-输出

#  label A  B error_A error_B R_A      R_B
#1     a 4 10     0.4     0.3  10 33.33333
#2     b 3  0     0.3     0.0  10      NaN
#3     c 8  4     0.2     0.4  40 10.00000
#4     d 9  1     0.1     0.1  90 10.00000

或其他选项

across

df %>% 
    mutate(across(c(A, B), ~ 
     ./get(str_c('error_', cur_column() )), .names = 'R_{.col}' ))
#  label A  B error_A error_B R_A      R_B
#1     a 4 10     0.4     0.3  10 33.33333
#2     b 3  0     0.3     0.0  10      NaN
#3     c 8  4     0.2     0.4  40 10.00000
#4     d 9  1     0.1     0.1  90 10.00000    

6
投票

一种选择可能是:

df %>%
 mutate(across(c(A, B), .names = "R_{col}")/across(starts_with("error")))

  label A  B error_A error_B R_A      R_B
1     a 4 10     0.4     0.3  10 33.33333
2     b 3  0     0.3     0.0  10      NaN
3     c 8  4     0.2     0.4  40 10.00000
4     d 9  1     0.1     0.1  90 10.00000

3
投票

我喜欢上面 akruns 的回答,尤其是

cur_column()
的方法。有趣的是,
cur_column()
不能与{rlang}的评估(
!! sym(paste0("error_", cur_column()))
)一起使用,但
get
是一个很好的解决方法。

只是添加一种方法,该方法也可以在 dpylr < 1.0.0. I usually use a

mutate
自定义函数下与
purrr::reduce()
一起使用。在此函数中,
x
是您的字符串主干,您可以使用
!! sym(paste0(...))
构造要访问的所有变量。在左侧,您可以只使用 {rlang} 的粘合语法。

您可以通过在字符串向量上调用

reduce()
来应用此自定义函数,并且您的
data.frame
会进入
.init =  .
参数。

library(tidyverse)
library(glue)


# filler data
df <- data.frame("label" = c('a','b','c','d'),
                 "A" = c(4, 3, 8, 9),
                 "B" = c(10, 0, 4, 1),
                 "error_A" = c(0.4, 0.3, 0.2, 0.1),
                 "error_B" = c(0.3, 0, 0.4, 0.1))

gen_vars1 <- function(df, x) {
  
  mutate(df,
         "R_{x}" := !! sym(x) / !! sym(paste0("error_", x)))
}

df %>% 
  reduce(c("A", "B"), gen_vars1, .init = .)
#>   label A  B error_A error_B R_A      R_B
#> 1     a 4 10     0.4     0.3  10 33.33333
#> 2     b 3  0     0.3     0.0  10      NaN
#> 3     c 8  4     0.2     0.4  40 10.00000
#> 4     d 9  1     0.1     0.1  90 10.00000

reprex 包于 2021 年 1 月 2 日创建(v0.3.0)

我曾经针对此类问题提出过功能请求,但显然对于 {dplyr} 来说这太特殊了。当您点击链接时,您还可以找到另一个选项来执行此类操作。


2
投票

对于这种情况,我发现基本 R 解决方案也直接且高效。它不需要循环列或唯一值。您定义两组列并直接划分它们。

对于您共享的示例,我们可以通过查找仅包含一个字符的列名称来识别

"A"
"B"
列。

cols <- grep('^.$', names(df), value = TRUE)
error_cols <- grep('error', names(df), value = TRUE)

df[paste0('R_', cols)] <- df[cols]/df[error_cols]
df

#  label A  B error_A error_B R_A  R_B
#1     a 4 10     0.4     0.3  10 33.3
#2     b 3  0     0.3     0.0  10  NaN
#3     c 8  4     0.2     0.4  40 10.0
#4     d 9  1     0.1     0.1  90 10.0

0
投票

github上的dplyover包有一个方便的

across2
可以在这里使用:

library(dplyr)
library(dplyover)

# library(remotes)
# install_github("TimTeaFan/dplyover")

df %>%
  mutate(across2(A:B, starts_with("error"), "/", .names = "R_{xcol}"))

给予

  label A  B error_A error_B R_A      R_B
1     a 4 10     0.4     0.3  10 33.33333
2     b 3  0     0.3     0.0  10      NaN
3     c 8  4     0.2     0.4  40 10.00000
4     d 9  1     0.1     0.1  90 10.00000
© www.soinside.com 2019 - 2024. All rights reserved.