6

我有一个随时间收集的一些变量的小标题。一个非常简化的 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做到这一点?

4

4 回答 4

4

您可以使用 执行类似的操作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预先创建一个向量。

于 2021-04-10T05:55:34.793 回答
4

我在 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 包(v0.3.0)于 2021-04-10 创建

于 2021-04-10T08:14:09.937 回答
3

对于这种情况,我发现使用 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
于 2021-04-10T03:41:30.733 回答
2

另一种通过一些定制来做到这一点的方法是

最初设定

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 包于 2021-04-10 创建(v1.0.0)

于 2021-04-10T05:01:21.443 回答