使用`across()`创建新列,函数包含多个其他列

s4n0splo  于 2023-10-13  发布在  其他
关注(0)|答案(1)|浏览(97)

是否可以基于列名模式和多列值函数创建多个新列?
例如

set.seed(123)
tibble(x = seq(1:10), bm_a = runif(10), val_a = runif(10), bm_b = runif(10), val_b = runif(10)) |>
  mutate(rel_a = val_a - bm_a, rel_b = val_b - bm_b)
# A tibble: 10 × 7
       x   bm_a  val_a  bm_b  val_b  rel_a   rel_b
   <int>  <dbl>  <dbl> <dbl>  <dbl>  <dbl>   <dbl>
 1     1 0.288  0.957  0.890 0.963   0.669  0.0735
 2     2 0.788  0.453  0.693 0.902  -0.335  0.209 
 3     3 0.409  0.678  0.641 0.691   0.269  0.0502
 4     4 0.883  0.573  0.994 0.795  -0.310 -0.199 
 5     5 0.940  0.103  0.656 0.0246 -0.838 -0.631 
 6     6 0.0456 0.900  0.709 0.478   0.854 -0.231 
 7     7 0.528  0.246  0.544 0.758  -0.282  0.214 
 8     8 0.892  0.0421 0.594 0.216  -0.850 -0.378 
 9     9 0.551  0.328  0.289 0.318  -0.224  0.0290
10    10 0.457  0.955  0.147 0.232   0.498  0.0845

但是想象一下,我有很多不同的动态命名列,都是以bm_*val_*开头的。有没有可能使用mutate()across()pick()starts_with()等的组合来让它对所有可能的列名都起作用?

nfeuvbwi

nfeuvbwi1#

下面是你如何使用cross和pick:

library(tidyverse)
df |> mutate(across(starts_with("val"), ~ .x - pick(str_replace(cur_column(), "val", "bm")), .names = "{str_replace(.col, 'val', 'rel')}"))

但使用pivot_longer()似乎是一种更快、更健壮的方法:

df |> 
  pivot_longer(-x, names_to = c(".value", "group"), names_pattern = "(bm|val)_(.+)") |>
  mutate(rel = val - bm)

更多详情:

  1. cur_column()获取当前列名。所以str_replace(cur_column(), "val", "bm")获取当前列名,瓦尔替换为bm。
    1..names设置输出列的名称。如果我们不这样做,他们会覆盖瓦尔列!
    1.使用花括号的原因是因为胶水语法。across需要字符串作为名称。查看glue包以了解更多关于它如何工作的信息。

相关问题