使用dplyr在列对之间进行变异

4

我正在尝试在dplyr中对列对应的函数进行应用。在下面的简单示例中,有列ab,以及a_expb_exp。我想创建一个名为a_mul = a*a_exp的新列,以及另一个用于b(和c、d、e...)。我知道可以使用循环和函数、purrrpivot_longer来解决这个问题,但我想知道是否可以使用mutate(across())来解决它。这是一个最小化的示例。实际问题更加复杂,因此我无法一次性创建两个列,因为在实际情况下,a_exp不是a的函数。我正在尝试以下代码,并且我得到了

"promise already under evaluation: recursive default argument reference or earlier problems?"

我不明白为什么会出现这种情况,也不知道如何解决它。

test <- tibble(a = runif(10), b = runif(10)) %>% 
    mutate(across(c(a,b), exp, .names = '{.col}_exp')) 

test <- test %>% 
    mutate(across(c(a,b), 
                  ~ .x * .data[[paste0(cur_column(), '_exp')]], 
                  .names = '{.col}_mult'))
4个回答

1

如果它们是成对的,你可以直接将across(a:b)乘以across(a_exp:b_exp)

test %>%
  mutate(across(a:b, .names = '{.col}_mul') * across(a_exp:b_exp))

# # A tibble: 10 × 6
#         a      b a_exp b_exp  a_mul  b_mul
#     <dbl>  <dbl> <dbl> <dbl>  <dbl>  <dbl>
#  1 0.288  0.957   1.33  2.60 0.383  2.49
#  2 0.788  0.453   2.20  1.57 1.73   0.713
#  3 0.409  0.678   1.51  1.97 0.616  1.33
#  4 0.883  0.573   2.42  1.77 2.14   1.02  
#  5 0.940  0.103   2.56  1.11 2.41   0.114
#  6 0.0456 0.900   1.05  2.46 0.0477 2.21
#  7 0.528  0.246   1.70  1.28 0.896  0.315
#  8 0.892  0.0421  2.44  1.04 2.18   0.0439
#  9 0.551  0.328   1.74  1.39 0.957  0.455
# 10 0.457  0.955   1.58  2.60 0.721  2.48

数据
library(dplyr)

set.seed(123)

test <- tibble(a = runif(10), b = runif(10)) %>% 
    mutate(across(c(a,b), exp, .names = '{.col}_exp')) 

1
好的回答。这意味着OP必须知道变量的名称?! - TarJae
1
@TarJae 如果 a:ba_exp:b_exp 成对出现,那么这个可以运行。 - Darren Tsai
这对我似乎有效,但它创建了一个带有实际想要的列的列表列,恢复名称似乎很困难。 - David
@David 我使用您提供的数据,但没有得到列表列。如果没有您的真实数据,很难知道您的情况。 - Darren Tsai

1

您可以获取列中的值。

library(tidyverse)

set.seed(12)

test <- tibble(a = runif(10), b = runif(10)) %>% 
  mutate(across(c(a, b), exp, .names = "{.col}_exp"))

test %>% mutate(across(c(a, b), ~.x * get(paste0(cur_column(), "_exp")), .names = "{.col}_mult"))

如果你可以决定先进行哪种算术运算,那么先做乘法再做exp可能会更容易。

test <- tibble(a = runif(10), b = runif(10)) %>% 
  mutate(across(c(a, b), ~.x * exp(.x), .names = "{.col}_mult"),
         across(c(a, b), exp, .names = "{.col}_exp"))

输出

# A tibble: 10 × 6
         a     b a_exp b_exp  a_mult b_mult
     <dbl> <dbl> <dbl> <dbl>   <dbl>  <dbl>
 1 0.0694  0.393  1.07  1.48 0.0743   0.582
 2 0.818   0.814  2.27  2.26 1.85     1.84 
 3 0.943   0.376  2.57  1.46 2.42     0.548
 4 0.269   0.381  1.31  1.46 0.353    0.557
 5 0.169   0.265  1.18  1.30 0.201    0.345
 6 0.0339  0.439  1.03  1.55 0.0351   0.682
 7 0.179   0.458  1.20  1.58 0.214    0.723
 8 0.642   0.541  1.90  1.72 1.22     0.929
 9 0.0229  0.666  1.02  1.95 0.0234   1.30 
10 0.00832 0.113  1.01  1.12 0.00839  0.126

1

我们至少有两种方法来实现这个:

1.使用purrr包中的reduce方法:

library(dplyr)
library(purrr)
library(stringr)

set.seed(123)

test %>% 
  split.default(str_remove(names(.), "_.*")) %>% 
  map_dfr(reduce, `*`) %>% 
  rename_with(~ paste0(., "_mult"), everything()) %>% 
  bind_cols(test)

   a_mult b_mult      a      b a_exp b_exp
    <dbl>  <dbl>  <dbl>  <dbl> <dbl> <dbl>
 1 1.86   1.14   0.819  0.616   2.27  1.85
 2 2.53   0.861  0.964  0.515   2.62  1.67
 3 2.33   2.31   0.924  0.920   2.52  2.51
 4 0.194  0.718  0.164  0.455   1.18  1.58
 5 0.500  1.98   0.351  0.847   1.42  2.33
 6 1.14   2.08   0.617  0.871   1.85  2.39
 7 1.04   1.35   0.580  0.683   1.79  1.98
 8 2.42   1.71   0.942  0.781   2.56  2.18
 9 0.259  0.323  0.210  0.251   1.23  1.29
10 0.0404 0.0487 0.0388 0.0465  1.04  1.05

2. 使用 across 时,我们需要使用 rename_with 修改名称以获得足够的名称对:

library(dplyr)
library(stringr)

test %>%
    rename_with(., ~ifelse(!str_detect(., "\\_"), paste0(., "_start"), .)) %>% 
    mutate(across(ends_with('_exp'), ~ . *
                    get(str_replace(cur_column(), "exp$", "start")), .names = "mult_{.col}")) %>%
    rename_at(vars(starts_with('mult')), ~ str_remove(., "\\_exp"))

  a_start b_start a_exp b_exp mult_a mult_b
     <dbl>   <dbl> <dbl> <dbl>  <dbl>  <dbl>
 1  0.288   0.957   1.33  2.60 0.383  2.49  
 2  0.788   0.453   2.20  1.57 1.73   0.713 
 3  0.409   0.678   1.51  1.97 0.616  1.33  
 4  0.883   0.573   2.42  1.77 2.14   1.02  
 5  0.940   0.103   2.56  1.11 2.41   0.114 
 6  0.0456  0.900   1.05  2.46 0.0477 2.21  
 7  0.528   0.246   1.70  1.28 0.896  0.315 
 8  0.892   0.0421  2.44  1.04 2.18   0.0439
 9  0.551   0.328   1.74  1.39 0.957  0.455 
10  0.457   0.955   1.58  2.60 0.721  2.48 

0
如果数据的组织方式与您的示例数据相同,那么这应该可以做到:
test[, 1:2] * test[, 3:4]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接