mutate_at 在某些情况下不创建变量后缀?

mutate_at does not create variable suffixes in some cases?

我一直在尝试 dplyr::mutate_at 通过对某些列应用相同的函数来创建新变量。当我在 .funs 参数中 命名我的函数 时,mutate 调用会创建带有后缀的新列而不是替换现有列,这是我在 .

df = data.frame(var1=1:2, var2=4:5, other=9)
df %>% mutate_at(vars(contains("var")), .funs=funs('sqrt'=sqrt))
####   var1 var2 other var1_sqrt var2_sqrt
#### 1    1    4     9  1.000000  2.000000
#### 2    2    5     9  1.414214  2.236068

但是,我注意到当 vars 参数用于指向我的列 return 时只有一列而不是几列时,生成的新列会删除初始名称:它被命名为 sqrt 而不是 other_sqrt 这里:

df %>% mutate_at(vars(contains("other")), .funs=funs('sqrt'=sqrt))
####   var1 var2 other sqrt
#### 1    1    4     9    3
#### 2    2    5     9    3

我想了解为什么会发生这种行为,以及如何避免这种情况,因为我事先不知道 contains() 将 return.

有多少列

编辑: 新建的列必须继承原有列的名称,在末尾加上后缀'sqrt'

谢谢

我只是想出了一个(不太干净)的方法; 我向数据集添加了一个额外的虚拟变量,其名称确保它会被选中并且我们不会陷入 1 变量的情况,并且在计算之后我删除了 2 个虚拟变量,如下所示:

df %>% mutate(other_fake=NA) %>% 
  mutate_at(vars(contains("other")), .funs=funs('sqrt'=sqrt)) %>% 
  select(-contains("other_fake"))
####   var1 var2 other other_sqrt
#### 1    1    4     9          3
#### 2    2    5     9          3

这是另一个想法。我们可以在 mutate_at 调用之后添加 setNames(sub("^sqrt$", "other_sqrt", names(.)))。这个想法是用 other_sqrt 替换列名 sqrt。如果只有一个名为 other 的列,则模式 ^sqrt$ 应仅匹配派生列 sqrt,如示例 1 所示。如果有多个列 other,如例2,setNames不会改变列名。

library(dplyr)

# Example 1
df <- data.frame(var1 = 1:2, var2 = 4:5, other = 9)

df %>% 
  mutate_at(vars(contains("other")), funs("sqrt" = sqrt(.))) %>%
  setNames(sub("^sqrt$", "other_sqrt", names(.)))
#   var1 var2 other other_sqrt
# 1    1    4     9          3
# 2    2    5     9          3

# Example 2
df2 <- data.frame(var1 = 1:2, var2 = 4:5, other1 = 9, other2 = 16)

df2 %>% 
  mutate_at(vars(contains("other")), funs("sqrt" = sqrt(.))) %>%
  setNames(sub("^sqrt$", "other_sqrt", names(.)))
#   var1 var2 other1 other2 other1_sqrt other2_sqrt
# 1    1    4      9     16           3           4
# 2    2    5      9     16           3           4

或者我们可以设计一个函数,在操作数据框之前检查有多少列包含字符串other

mutate_sqrt <- function(df, string){
  string_col <- grep(string, names(df), value = TRUE)
  df2 <- df %>% mutate_at(vars(contains(string)), funs("sqrt" = sqrt(.)))
  if (length(string_col) == 1){
    df2 <- df2 %>%  setNames(sub("^sqrt$", paste(string_col, "sqrt", sep = "_"), names(.)))
  }
  return(df2)
}

mutate_sqrt(df, "other")
#   var1 var2 other other_sqrt
# 1    1    4     9          3
# 2    2    5     9          3

mutate_sqrt(df2, "other")
#   var1 var2 other1 other2 other1_sqrt other2_sqrt
# 1    1    4      9     16           3           4
# 2    2    5      9     16           3           4