如何系统地改变多列作为多列的功能?

How to mutate multiple columns as function of multiple columns systematically?

我对随着时间的推移收集到的一些变量有一点小意见。一个非常简化的 tibble 版本看起来像这样。

df = tribble(
~id, ~varA.t1, ~varA.t2, ~varB.t1, ~varB.t2,
'row_1', 5, 10, 2, 4,
'row_2', 20, 50, 4, 6
)

我想系统地创建一组新变量 varC 以便 varC.t# = varA.t# / varB.t# 其中 # 是 1, 2, 3 等(类似于上面小标题中设置列名的方式)。

我该如何使用类似于 mutateacross 的方法来做到这一点?

对于这种情况,我发现使用 base R 既简单又高效。

varAcols <- sort(grep('varA', names(df), value = TRUE))
varBcols <- sort(grep('varB', names(df), value = TRUE))
df[sub('A', 'C', varAcols)] <- df[varAcols]/df[varBcols]

#    id    varA.t1 varA.t2 varB.t1 varB.t2 varC.t1 varC.t2
#  <chr>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
#1 row_1       5      10       2       4     2.5    2.5 
#2 row_2      20      50       4       6     5      8.33

另一种进行自定义的方法是

初始设置

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

df = tribble(
  ~id, ~varA.t1, ~varA.t2, ~varB.t1, ~varB.t2,
  'row_1', 5, 10, 2, 4,
  'row_2', 20, 50, 4, 6
)

# A function take in a formula then parse it and correct the column name
operation_function <- function(df, formula) {
  # Extract the column name from the formula
  new_column_name <- str_extract(formula, "^.+=")
  new_column_name <- trimws(gsub("=", "", new_column_name))
  
  # Process the df
  df %>%
    # parse the formula - this reuslt in new column name  as value formula
    mutate(!!rlang::parse_expr(formula)) %>%
    # rename the new created column with the correct column name
    rename(!!new_column_name := last_col())
}

注意:我认为应该有更有效的方法来实现具有专有名称的公式。虽然我现在想不通。欢迎其他人的想法

准备要被数据处理的公式。在这种情况下很简单

对于更复杂的公式,您可能需要稍微改变一下

# Prepare the formula
base_formula <- c("varC.t# = varA.t# / varB.t#")
replacement_list <- c(1, 2)
list_formula <- map(replacement_list, .f = gsub,
  pattern = "#", x = base_formula)
list_formula
#> [[1]]
#> [1] "varC.t1 = varA.t1 / varB.t1"
#> 
#> [[2]]
#> [1] "varC.t2 = varA.t2 / varB.t2"

最后用公式列表处理数据

# process with the function and then reduce them with left_join
reduce(map(.x = list_formula, .f = operation_function, df = df),
  left_join)
#> Joining, by = c("id", "varA.t1", "varA.t2", "varB.t1", "varB.t2")
#> # A tibble: 2 x 7
#>   id    varA.t1 varA.t2 varB.t1 varB.t2 varC.t1 varC.t2
#>   <chr>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
#> 1 row_1       5      10       2       4     2.5    2.5 
#> 2 row_2      20      50       4       6     5      8.33

reprex package (v1.0.0)

于 2021-04-10 创建

您可以使用 mutate(across... 执行类似的操作,但是,要重命名列,必须有一个快捷方式。

df %>% 
  mutate(across(.cols = c(varA.t1, varA.t2),
                .fns = ~ .x / get(glue::glue(str_replace(cur_column(), "varA", "varB"))),
                .names = "V_{.col}")) %>%
  rename_with(~str_replace(., "V_varA", "varC"), starts_with("V_"))

# A tibble: 2 x 7
  id    varA.t1 varA.t2 varB.t1 varB.t2 varC.t1 varC.t2
  <chr>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
1 row_1       5      10       2       4     2.5    2.5 
2 row_2      20      50       4       6     5      8.33

如果有很长的时间序列,您还可以预先为 .cols 创建一个向量。

我在 GitHub 上有一个名为 {dplyover} 的软件包,旨在以类似于 dplyr::across.

的方式解决此类问题

函数调用 across2。它允许您定义两组可以应用一个或多个函数的列。 .names 参数支持两个粘合规范:{pre}{suf}。他们提取变量名称的共享前缀和后缀。这使得在我们的输出变量上取好听的名字变得容易。

该函数有一个警告。当应用于高度分组的数据时,它的性能不佳(有一个带有基准的小插图)。

library(dplyr)
library(dplyover) # https://github.com/TimTeaFan/dplyover

df = tribble(
  ~id, ~varA.t1, ~varA.t2, ~varB.t1, ~varB.t2,
  'row_1', 5, 10, 2, 4,
  'row_2', 20, 50, 4, 6
)

df %>% 
  mutate(across2(starts_with("varA"),
                 starts_with("varB"),
                 ~ .x / .y,
                 .names = "{pre}C.{suf}"))

#> # A tibble: 2 x 7
#>   id    varA.t1 varA.t2 varB.t1 varB.t2 varC.t1 varC.t2
#>   <chr>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
#> 1 row_1       5      10       2       4     2.5    2.5 
#> 2 row_2      20      50       4       6     5      8.33

reprex package (v0.3.0)

于 2021-04-10 创建