在 mutate 语句中动态引用列名 - dplyr

标签 r dplyr across

我为这个冗长的问题表示歉意,但过了很长一段时间后,我自己无法找到解决方案。
我有这个玩具数据框

set.seed(23)
df <- tibble::tibble(
  id = paste0("00", 1:6),
  cond = c(1, 1, 2, 2, 3, 3),
  A_1 = sample(0:9, 6, replace = TRUE), A_2 = sample(0:9, 6, replace = TRUE), A_3 = sample(0:9, 6, replace = TRUE),
  B_1 = sample(0:9, 6, replace = TRUE), B_2 = sample(0:9, 6, replace = TRUE), B_3 = sample(0:9, 6, replace = TRUE),
  C_1 = sample(0:9, 6, replace = TRUE), C_2 = sample(0:9, 6, replace = TRUE), C_3 = sample(0:9, 6, replace = TRUE)
)

# A tibble: 6 x 11
#   id     cond   A_1   A_2   A_3   B_1   B_2   B_3   C_1   C_2   C_3
#   <chr> <dbl> <int> <int> <int> <int> <int> <int> <int> <int> <int>
# 1 001       1     6     3     9     5     0     5     6     0     6
# 2 002       1     4     5     0     8     5     0     1     6     6
# 3 003       2     4     2     8     8     8     6     5     2     5
# 4 004       2     4     4     0     7     2     6     7     5     7
# 5 005       3     1     7     0     9     9     0     5     7     8
# 6 006       3     3     8     7     0     2     5     0     9     4
我想创建三个变量 A_def , B_def , C_def根据它们的后缀等于变量 cond 的条件,只取对应变量 之一的值。 .
例如,对于 cond == 1 的行, A_def应该有来自 A_1 的值, B_def应该有来自 B_1 的值, C_def应该有来自 C_1 的值.同样,如果 cond == 2 , *_def列应该具有来自各自 *_2 的值变量。
我设法通过两种方式实现了我想要的输出:一种是硬编码的(如果 cond 包含许多值,可能会避免),另一种是使用 tidyr的旋转功能。
硬编码解决方案:
df %>% 
  mutate(
    A_def = ifelse(cond == 1, A_1, ifelse(cond == 2, A_2, A_3)),
    B_def = ifelse(cond == 1, B_1, ifelse(cond == 2, B_2, B_3)),
    C_def = ifelse(cond == 1, C_1, ifelse(cond == 2, C_2, C_3))
  ) %>% 
  select(id, cond, contains("_def"))
tidyr的解决办法:
df %>% 
  pivot_longer(cols = contains("_")) %>% 
  mutate(
    number = gsub("[A-Za-z_]", "", name),
    name = gsub("[^A-Za-z]", "", name)
  ) %>% 
  filter(cond == number) %>% 
  pivot_wider(id_cols = c(id, cond), names_from = name, values_from = value, names_glue = "{name}_def")
两种情况下的输出
# A tibble: 6 x 5
#   id     cond A_def B_def C_def
#   <chr> <dbl> <int> <int> <int>
# 1 001       1     6     5     6
# 2 002       1     4     8     1
# 3 003       2     2     8     2
# 4 004       2     4     2     5
# 5 005       3     0     0     8
# 6 006       3     7     5     4

现在,我想知道是否可以使用 mutate 获得相同的输出。和/或 across以动态方式(可能在 ifelse 中使用 mutate 语句?)。我尝试了以下代码片段,但结果并不如预期。在其中之一中,我试图将变量名称作为 ifelse 中的符号。声明,但我得到了一个错误。
df %>% 
  mutate(across(paste0(c("A", "B", "C"), "_1"),
                ~ifelse(cond == 1, cur_column(), 
                        ifelse(cond == 2, cur_column(), paste0(gsub("[^A-Za-z]", "", cur_column()), "_3"))))) %>% 
  select(id, cond, contains("_1"))

df %>% 
  mutate_at(paste0(c("A", "B", "C"), "_1"),
            ~ifelse(cond == 1, ., ifelse(cond == 2, ., paste0(., "_2")))) %>% 
  select(id, cond, contains("_1"))

df %>% 
  mutate_at(paste0(c("A", "B", "C"), "_1"),
            ~ifelse(cond == 1, !!!rlang::syms(paste0(c("A", "B", "C"), "_1")),
                    ifelse(cond == 2, !!!rlang::syms(paste0(c("A", "B", "C"), "_2")),
                           !!!rlang::syms(paste0(c("A", "B", "C"), "_3")))))
问题:有没有办法使用 dplyr 获得与上述相同的期望输出的声明如 mutate (或其被取代的范围变体)和/或 across ?

最佳答案

正如罗纳克所说,您的 tidyr解决方案似乎很好。
不过,您可以稍微简化一下:

df %>% 
  pivot_longer(cols = contains("_"), names_to = c("name", "number"), names_sep = "_") %>% 
  filter(cond == number) %>% 
  pivot_wider(id_cols = c(id, cond), names_glue = "{name}_def")


## A tibble: 6 x 5
#  id     cond A_def B_def C_def
#  <chr> <dbl> <int> <int> <int>
#1 001       1     7     8     1
#2 002       1     2     5     2
#3 003       2     4     2     3
#4 004       2     0     3     1
#5 005       3     9     0     7
#6 006       3     9     7     0

关于在 mutate 语句中动态引用列名 - dplyr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62556564/

相关文章:

r - 根据对应于特定日期的有效值将日期转换为 NA

r - 当目录包含大量无关文件时,如何提高 R CMD 构建的速度?

R data.table "by"为 "i"

R 命令行最佳实践 : exit, 打印 stdout、打印 stderr、避免警告

r - 在 R 中跨不同类型的列应用 ifelse() 时保留列类型

r - Dplyr cross + mutate + condition 选择列

javascript - 如何在没有注释的情况下在 dygraphs 中显示工具提示

r - 数据框中的条件计数

r - 如何整齐地组合稀疏列