如何使用tidyverse中的相​​邻列修改另一列中命名的列?

How to modify a column named in another column using adjacent column in tidyverse?

我有以下数据框,

df <- tibble(x = c(2, 3, 4)) %>% 
mutate(`1` = 99, `2` = 88, `3` = 77, `4` = 66, `5` = 55)

x 包含需要操作的列名,该列中的值必须替换为列 x-1xx+1。例如,对于 x 为 2 的第一行,列 2 中的值必须替换为 (99+88+77) = 264.

我尝试使用双大括号 ({{}}) 和 :=,如下所示,

df %>% 
  mutate("{{x}}" := {{x-1}} + {{x}} + {{x+1}})

但我收到以下错误, Error in local_error_context(dots = dots, .index = i, mask = mask) : promise already under evaluation: recursive default argument reference or earlier problems?

然后我尝试使用 across() 内的 cur_column() 访问该列,如下所示,

df %>% 
  mutate(across(-x, ~if_else(x == cur_column(), {{cur_column()}}, .x)))

我遇到了与上述相同的错误,我想我可能错误地使用了 curly 运算符,有人可以帮忙吗?

{{}} 语法适用于将未计算的表达式传递给 dplyr 命令时,它不适用于捕获列值。

大多数情况下,在每一行的不同列上执行不同的选项并不容易。一种替代方法是重塑数据,以便您可以使用 lead/lag 函数。然后你就可以回头了。

library(dplyr)
library(tidyr)
df %>% 
  mutate(row = row_number()) %>% 
  pivot_longer(!c(x, row)) %>% 
  mutate(name = as.integer(name)) %>% 
  group_by(row) %>% 
  mutate(value=if_else(x==name, value + lead(value) + lag(value), value)) %>% 
  pivot_wider(c(row, x)) %>% 
  select(-row)

#     row     x   `1`   `2`   `3`   `4`   `5`
#   <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     1     2    99   264    77    66    55
# 2     2     3    99    88   231    66    55
# 3     3     4    99    88    77   198    55

另一种方法是创建一个可以使用 rowwise() 访问 cur_column()cur_data() 的辅助函数,以便为每一行创建不同的转换。


colclump <- function(target) {
  prevc <- as.character(as.integer(target)-1)
  nextc <- as.character(as.integer(target)+1)
  function(x) {
    if (cur_column()==target) {
      x + cur_data()[[prevc]] + cur_data()[[nextc]]
    } else {
      x
    }
  }
  
}
df %>% 
  rowwise() %>% 
  mutate(across(-x, ~colclump(x)(.x)))

也许这个也符合你的目的:

for(k in 2:4) {
  df[k-1,k+1] <- df[k-1,as.character(df$x[k-1])] + 
              df[k-1,as.character(df$x[k-1]-1)] + 
              df[k-1,as.character(df$x[k-1]+1)]
}
df
#  A tibble: 3 x 6
# x   `1`   `2`   `3`   `4`   `5`
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     2    99   264    77    66    55
# 2     3    99    88   231    66    55
# 3     4    99    88    77   198    55