【问题标题】:How to dplyr rename a column, by column index?如何按列索引重命名列?
【发布时间】:2017-08-03 19:47:03
【问题描述】:

以下代码重命名数据集中的第一列:

require(dplyr)    
mtcars %>%
        setNames(c("RenamedColumn", names(.)[2:length(names(.))]))

期望的结果:

                    RenamedColumn cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4                    21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag                21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
Datsun 710                   22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1

是否有可能使用 rename 和列索引得到相同的结果?

这个:

mtcars %>%
    rename(1 = "ChangedNameAgain")

会失败:

Error in source("~/.active-rstudio-document", echo = TRUE) : 
  ~/.active-rstudio-document:7:14: unexpected '='
6: mtcars %>%
7:     rename(1 =
                ^

类似地尝试使用rename_.[[1]] 作为列引用将返回错误。

【问题讨论】:

    标签: r dataframe dplyr rename nse


    【解决方案1】:

    截至dplyr0.7.5rlang0.2.1tidyselect0.2.4,这很有效:

    library(dplyr)
    
    rename(mtcars, ChangedNameAgain = 1)
    
    #                     ChangedNameAgain cyl  disp  hp drat    wt  qsec vs am gear carb
    # Mazda RX4                       21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
    # Mazda RX4 Wag                   21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
    # Datsun 710                      22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
    # Hornet 4 Drive                  21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
    # Hornet Sportabout               18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
    # ...
    

    原始答案和编辑现已过时:

    rename() 的逻辑是new_name = old_name,所以ChangedNameAgain = 11 = ChangedNameAgain 更有意义。

    我建议:

    mtcars %>% rename_(ChangedNameAgain = names(.)[1])
    #                     ChangedNameAgain cyl  disp  hp drat    wt  qsec vs am gear carb
    # Mazda RX4                       21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
    # Mazda RX4 Wag                   21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
    # Datsun 710                      22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
    # Hornet 4 Drive                  21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
    # Hornet Sportabout               18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
    # Valiant                         18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
    

    编辑

    自从dplyr 的0.6/0.7 版本以来,我还没有完全了解基于rlang 的新dplyr 编程系统。

    我最初的答案中使用的rename 的下划线后缀版本现在已被弃用,并且根据@jzadra 的评论,它无论如何都不适用于像"foo bar" 这样有语法问题的名称。

    这是我对基于rlang 的新非标准评估系统的尝试。不要犹豫,在 cmets 中告诉我我做错了什么:

    df <- tibble("foo" = 1:2, "bar baz" = letters[1:2])
    
    # # A tibble: 2 x 2
    #     foo `bar baz`
    #   <int>     <chr>
    # 1     1         a
    # 2     2         b
    

    首先我直接尝试使用rename(),但不幸的是我遇到了错误。它似乎是源代码中的FIXME(或者这与FIXME无关?)(我使用的是dplyr 0.7.4),所以它可以在未来工作:

    df %>% rename(qux = !! quo(names(.)[[2]]))
    
    # Error: Expressions are currently not supported in `rename()`
    

    (编辑:现在的错误消息 (dplyr 0.7.5) 为 Error in UseMethod("rename_") : no applicable method for 'rename_' applied to an object of class "function"

    (2018 年 6 月 14 日更新:df %&gt;% rename(qux = !! quo(names(.)[[2]])) 现在似乎可以正常工作,仍然使用 dplyr 0.7.5,不确定底层包是否已更改)。

    这是一个有效的select 解决方法。它不会像rename 那样保留列顺序:

    df %>% select(qux = !! quo(names(.)[[2]]), everything())
    
    # # A tibble: 2 x 2
    #     qux   foo
    #   <chr> <int>
    # 1     a     1
    # 2     b     2
    

    如果我们想把它放在一个函数中,我们必须用:=稍微修改它以允许在左侧取消引用。如果我们想对字符串和裸变量名等输入具有鲁棒性,我们必须使用enquo()quo_name() 的“黑魔法”(或者说vignette)(老实说,我不完全理解它确实):

    rename_col_by_position <- function(df, position, new_name) {
      new_name <- enquo(new_name)
      new_name <- quo_name(new_name)
      select(df, !! new_name := !! quo(names(df)[[position]]), everything())
    }
    

    这适用于新名称作为字符串:

    rename_col_by_position(df, 2, "qux")
    
    # # A tibble: 2 x 2
    #     qux   foo
    #   <chr> <int>
    # 1     a     1
    # 2     b     2
    

    这适用于新名称作为 quosure:

    rename_col_by_position(df, 2, quo(qux))
    
    # # A tibble: 2 x 2
    #     qux   foo
    #   <chr> <int>
    # 1     a     1
    # 2     b     2
    

    这适用于新名称作为裸名:

    rename_col_by_position(df, 2, qux)
    
    # # A tibble: 2 x 2
    #     qux   foo
    #   <chr> <int>
    # 1     a     1
    # 2     b     2
    

    即使这样也有效:

    rename_col_by_position(df, 2, `qux quux`)
    
    # # A tibble: 2 x 2
    #   `qux quux`   foo
    #        <chr> <int>
    # 1          a     1
    # 2          b     2
    

    【讨论】:

    • 如果现有名称需要反引号,则此方法不起作用。
    • 谢谢。我必须同意新的 rlang 已经使 tidyverse 远离了可理解的语法,这对我来说似乎是一个巨大的倒退。
    【解决方案2】:

    这里有几个可以说更容易阅读的替代解决方案,因为它们不关注. 参考。 select 理解列索引,所以如果你要重命名第一列,你可以简单地做

    mtcars %>% select( RenamedColumn = 1, everything() )
    

    但是,使用select 的问题是,如果您要重命名中间的列,它会重新排列列。要解决此问题,您必须预先选择要重命名的列左侧的列:

    ## This will rename the 7th column without changing column order
    mtcars %>% select( 1:6, RenamedColumn = 7, everything() )
    

    另一种选择是使用新的rename_at,它也可以理解列索引:

    ## This will also rename the 7th column without changing the order
    ## Credit for simplifying the second argument: Moody_Mudskipper
    mtcars %>% rename_at( 7, ~"RenamedColumn" )
    

    ~ 是必需的,因为rename_at 非常灵活,可以接受函数作为其第二个参数。例如,mtcars %&gt;% rename_at( c(2,4), toupper ) 将使第二列和第四列的名称变为大写。

    【讨论】:

    • rename_at 似乎是最好的,也是最新的解决方案。谢谢,@artem-solokov!
    • @Artem 你可以写mtcars %&gt;% rename_at(1,~"RenamedColumn")
    • 很好的建议,@Moody_Mudskipper。我编辑了我的答案以合并它。
    【解决方案3】:

    dplyr 已用 rename_with() 取代 rename_at()。您可以像这样按索引重命名列:

    library(tidyverse)
    
    mtcars %>% 
      rename_with(.cols = 1, ~"renamed_column")
    
    #>                     renamed_column cyl  disp  hp drat    wt  qsec vs am gear
    #> Mazda RX4                    21.0   6 160.0 110 3.90 2.620 16.46  0  1    4
    #> Mazda RX4 Wag                21.0   6 160.0 110 3.90 2.875 17.02  0  1    4
    #> Datsun 710                   22.8   4 108.0  93 3.85 2.320 18.61  1  1    4
    #> Hornet 4 Drive               21.4   6 258.0 110 3.08 3.215 19.44  1  0    3
    #> Hornet Sportabout            18.7   8 360.0 175 3.15 3.440 17.02  0  0    3
    #> ...
    

    确保在新列名之前包含波浪号 (~)*。

    还要注意,如果你引入glue包,你可以像这样修改现有的列名:

    library(glue)
    
    mtcars %>% 
      rename_with(.cols = 1, ~glue::glue("renamed_{.}"))
    #>                     renamed_mpg cyl  disp  hp drat    wt  qsec vs am gear carb
    #> Mazda RX4                  21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
    #> Mazda RX4 Wag              21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
    #> Datsun 710                 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
    #> Hornet 4 Drive             21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
    #> Hornet Sportabout          18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
    #> ...
    

    将上述方法应用于多列只需使用冒号 (:) 传入列索引号范围或使用 c() 在向量中传递多个索引;这是两者的结合:

    mtcars %>% 
      rename_with(.cols = c(1:3, 5), ~glue::glue("renamed_{.}"))
    #>                     renamed_mpg renamed_cyl renamed_disp  hp renamed_drat    wt
    #> Mazda RX4                  21.0           6        160.0 110         3.90 2.620
    #> Mazda RX4 Wag              21.0           6        160.0 110         3.90 2.875
    #> Datsun 710                 22.8           4        108.0  93         3.85 2.320
    #> Hornet 4 Drive             21.4           6        258.0 110         3.08 3.215
    #> Hornet Sportabout          18.7           8        360.0 175         3.15 3.440
    #> ...
    

    请记住,由于. 代表当前列名,您可以像这样对其应用字符串修改函数:

    mtcars %>% 
      rename_with(.cols = c(1:3), 
                  ~glue::glue("renamed_{str_replace(.,'mpg','miles_per_gallon')}"))
    #>                     renamed_miles_per_gallon renamed_cyl renamed_disp  hp
    #> Mazda RX4                               21.0           6        160.0 110
    #> Mazda RX4 Wag                           21.0           6        160.0 110
    #> Datsun 710                              22.8           4        108.0  93
    #> Hornet 4 Drive                          21.4           6        258.0 110
    #> Hornet Sportabout                       18.7           8        360.0 175
    #> ...
    

    *您可以详细了解~. NSE 函数简写here

    【讨论】:

      【解决方案4】:

      @Aurele 建议的恕我直言 rlang 在这里太多了。

      解决方案1:使用大括号管道管道上下文:

      bcMatrix %>% {colnames(.)[1] = "foo"; .}
      

      解决方案 2:或者 (ab) 使用来自 magrittr 软件包的 tee 运算符 %&gt;%(如果使用 dplyr,则无论如何都要安装)来执行重命名作为副作用:

      bcMatrix %T>% {colnames(.)[1] = "foo"}
      

      解决方案 3:使用简单的辅助函数:

      rename_by_pos = function(df, index, new_name){ 
          colnames(df)[index] = new_name 
          df 
      }
      iris %>% rename_by_pos(2,"foo")
      

      【讨论】:

        猜你喜欢
        • 2016-11-12
        • 1970-01-01
        • 2019-05-15
        • 2019-11-01
        • 2015-10-02
        • 2018-01-25
        • 2018-08-02
        • 2022-11-21
        • 2021-05-12
        相关资源
        最近更新 更多