【问题标题】:Create new variables with mutate_at while keeping the original ones使用 mutate_at 创建新变量,同时保留原始变量
【发布时间】:2018-02-07 10:08:42
【问题描述】:

考虑这个简单的例子:

library(dplyr)

dataframe <- data_frame(helloo = c(1,2,3,4,5,6),
                        ooooHH = c(1,1,1,2,2,2),
                        ahaaa = c(200,400,120,300,100,100))

# A tibble: 6 x 3
  helloo ooooHH ahaaa
   <dbl>  <dbl> <dbl>
1      1      1   200
2      2      1   400
3      3      1   120
4      4      2   300
5      5      2   100
6      6      2   100

这里我想将函数ntile 应用于所有包含oo 的列,但我希望将这些新列称为cat + 相应的列。

我知道我能做到

dataframe %>% mutate_at(vars(contains('oo')), .funs = funs(ntile(., 2)))
# A tibble: 6 x 3
  helloo ooooHH ahaaa
   <int>  <int> <dbl>
1      1      1   200
2      1      1   400
3      1      1   120
4      2      2   300
5      2      2   100
6      2      2   100

但我需要的是这个

# A tibble: 8 x 5
  helloo   ooooHH   ahaaa cat_helloo cat_ooooHH
     <dbl>    <dbl> <dbl>    <int>    <int>
1        1        1   200        1        1
2        2        1   400        1        1
3        3        1   120        1        1
4        4        2   300        2        2
5        5        2   100        2        2
6        5        2   100        2        2
7        6        2   100        2        2
8        6        2   100        2        2

是否有不需要存储中间数据并合并回原始数据帧的解决方案?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    dplyr 1.0.0 更新 2020-06

    dplyr 1.0.0 开始,across() 函数取代了诸如 mutate_at() 之类的函数的“作用域变体”。代码在across() 中应该看起来很熟悉,它嵌套在mutate() 中。

    为您在列表中提供的函数添加名称会将函数名称添加为后缀。

    dataframe %>%
         mutate( across(contains('oo'), 
                        .fns = list(cat = ~ntile(., 2))) )
    
    # A tibble: 6 x 5
      helloo ooooHH ahaaa helloo_cat ooooHH_cat
       <dbl>  <dbl> <dbl>      <int>      <int>
    1      1      1   200          1          1
    2      2      1   400          1          1
    3      3      1   120          1          1
    4      4      2   300          2          2
    5      5      2   100          2          2
    6      6      2   100          2          2
    

    在 1.0.0 中使用 across() 中的 .names 参数更改新列名称更容易一些。这是将函数名称添加为前缀而不是后缀的示例。这使用 glue 语法。

    dataframe %>%
         mutate( across(contains('oo'), 
                        .fns = list(cat = ~ntile(., 2)),
                        .names = "{fn}_{col}" ) )
    
    # A tibble: 6 x 5
      helloo ooooHH ahaaa cat_helloo cat_ooooHH
       <dbl>  <dbl> <dbl>      <int>      <int>
    1      1      1   200          1          1
    2      2      1   400          1          1
    3      3      1   120          1          1
    4      4      2   300          2          2
    5      5      2   100          2          2
    6      6      2   100          2          2
    

    mutate_at() 的原始答案

    已编辑以反映 dplyr 中的更改。从 dplyr 0.8.0 开始,funs() 已弃用,而应使用 list()~

    您可以为传递给.funs 的列表中的函数指定名称,以创建带有名称作为后缀的新变量。

    dataframe %>% mutate_at(vars(contains('oo')), .funs = list(cat = ~ntile(., 2)))
    
    # A tibble: 6 x 5
      helloo ooooHH ahaaa helloo_cat ooooHH_cat
       <dbl>  <dbl> <dbl>      <int>      <int>
    1      1      1   200          1          1
    2      2      1   400          1          1
    3      3      1   120          1          1
    4      4      2   300          2          2
    5      5      2   100          2          2
    6      6      2   100          2          2
    

    如果您想将其用作前缀,则可以使用 rename_at 更改名称。

    dataframe %>% 
         mutate_at(vars(contains('oo')), .funs = list(cat = ~ntile(., 2))) %>%
         rename_at( vars( contains( "_cat") ), list( ~paste("cat", gsub("_cat", "", .), sep = "_") ) )
    
    # A tibble: 6 x 5
      helloo ooooHH ahaaa cat_helloo cat_ooooHH
       <dbl>  <dbl> <dbl>      <int>      <int>
    1      1      1   200          1          1
    2      2      1   400          1          1
    3      3      1   120          1          1
    4      4      2   300          2          2
    5      5      2   100          2          2
    6      6      2   100          2          2
    

    dplyr 早期版本中带有 funs() 的先前代码:

    dataframe %>% 
         mutate_at(vars(contains('oo')), .funs = funs(cat = ntile(., 2))) %>%
         rename_at( vars( contains( "_cat") ), funs( paste("cat", gsub("_cat", "", .), sep = "_") ) )
    

    【讨论】:

    • 我想总是可以写一些正则表达式的东西来更改col_cat 变量的名称?
    • @ℕʘʘḆḽḘ 是的。为方便起见,可能在rename_at;在编辑中添加示例。
    • 重命名似乎仅在有多个包含匹配项的列时才会追加。有没有办法让它也附加到单个匹配中?示例:dataframe %&gt;% mutate_at(vars(contains('ah')), .funs = funs(cat = ntile(., 2)))
    • @bheavner 我不知道,但您可能会问一个新问题。对于单个变量,您可以使用 mutate 编写函数并根据函数输入设置变量名称。请参阅Programming with dplyr 的“设置变量名”部分
    • 如果您想知道 ~ntile(., 2)this 函数将最小输入映射到最小输出到底是什么,那么在这种情况下,col ooooHH 前三个值 1,2,3 排名为 1,@ 987654346@ 排名为 2。(例如,如果我的排名为 ~ntile(., 5)),则将 hello 列与 cat_hello 进行比较,如果您只想根据行值的乘积进行变异,请使用 .fns = list(cat = ~(.)*10)