【问题标题】:R dplyr: rename variables using string functionsR dplyr:使用字符串函数重命名变量
【发布时间】:2015-08-03 15:53:59
【问题描述】:

(有点相关的问题:Enter new column names as string in dplyr's rename function)

在dplyr 链(%>%)的中间,我想用旧名称的函数替换多个列名称(使用tolower 或gsub 等)

library(tidyr); library(dplyr)
data(iris)
# This is what I want to do, but I'd like to use dplyr syntax
names(iris) <- tolower( gsub("\\.", "_", names(iris) ) )
glimpse(iris, 60)
# Observations: 150
# Variables:
#   $ sepal_length (dbl) 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6,...
#   $ sepal_width  (dbl) 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4,...
#   $ petal_length (dbl) 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4,...
#   $ petal_width  (dbl) 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3,...
#   $ species      (fctr) setosa, setosa, setosa, setosa, s...

# the rest of the chain:
iris %>% gather(measurement, value, -species) %>%
  group_by(species,measurement) %>%
  summarise(avg_value = mean(value)) 

我看到?rename 将参数replace 当作named character vector, with new names as values, and old names as names.

所以我尝试了:

iris %>% rename(replace=c(names(iris)=tolower( gsub("\\.", "_", names(iris) ) )  ))

但是这 (a) 返回 Error: unexpected '=' in iris %&gt;% ... 并且 (b) 需要通过名称引用链中上一个操作中的数据帧,这在我的实际用例中我无法做到。

iris %>% 
  rename(replace=c(    )) %>% # ideally the fix would go here
  gather(measurement, value, -species) %>%
  group_by(species,measurement) %>%
  summarise(avg_value = mean(value)) # I realize I could mutate down here 
                                     #  instead, once the column names turn into values, 
                                     #  but that's not the point
# ---- Desired output looks like: -------
# Source: local data frame [12 x 3]
# Groups: species
# 
#       species  measurement avg_value
# 1      setosa sepal_length     5.006
# 2      setosa  sepal_width     3.428
# 3      setosa petal_length     1.462
# 4      setosa  petal_width     0.246
# 5  versicolor sepal_length     5.936
# 6  versicolor  sepal_width     2.770
# ... etc ....  

【问题讨论】:

  • 优雅的做法是:iris %&gt;% `names&lt;-`(.,tolower( gsub("\\.", "_", names(.) ) ))(我只是在开玩笑。)
  • 以下答案中使用的某些功能已被弃用。 rename_with 是最新的 dplyr 动词,用于使用函数以编程方式重命名变量。请参阅下面的答案。

标签: regex r rename dplyr


【解决方案1】:

这是一个很晚的答案,2017 年 5 月

从 dplyr 0.5.0.9004 开始,即将推出 0.6.0,许多新的列重命名方法,符合 maggritr 管道运算符 %&gt;%,已添加到包中。

这些功能是:

  • rename_all
  • rename_if
  • rename_at

使用这些功能有很多不同的方法,但与您的问题相关的一种方法是使用stringr 包:

df <- df %>%
  rename_all(
      funs(
        stringr::str_to_lower(.) %>%
        stringr::str_replace_all(., '\\.', '_')
      )
  )

所以,继续安装管道:)(不是双关语)。

【讨论】:

  • 很高兴知道,谢谢。另外值得注意的是,您可以将df %&lt;&gt;% foo() 用作df &lt;- df %&gt;% foo() 的简写
  • 由于新的 dplyr 更新,他们改变了 funs() 的工作方式(真希望他们没有),你需要用 list 替换 funs 并在函数前放置一个波浪号 ~例如list(~str_replace(., to_replace, replacement))
【解决方案2】:

我认为您正在查看 plyr::rename 的文档,而不是 dplyr::rename。你会用dplyr::rename做这样的事情:

iris %>% rename_(.dots=setNames(names(.), tolower(gsub("\\.", "_", names(.)))))

【讨论】:

  • 你可以用. 代替iris 在后面的出现。
  • 这很有用,为什么你必须使用rename_而不是rename?
  • 习惯,因为我主要以编程方式使用 dplyr
  • @Konrad 实际上,我面前没有文档,但我认为非安全版本没有 .dots 参数
  • 仅供参考:rename_慢慢地变成deprecated。虽然@Frank 对setNames 的使用似乎是最直接的(如果dplyr 没有提供的话),但我还没有找到明显的替代品。
【解决方案3】:

这里有一种方法可以绕过有点尴尬的rename 语法:

myris <- iris %>% setNames(tolower(gsub("\\.","_",names(.))))

【讨论】:

  • 解决方法的另一个依赖项?这越来越深奥了。
  • 您可以将setnames 替换为setNames 并将呼叫挂断至data.table。
  • @MatthewPlourde 你知道有什么理由更喜欢更长的rename 而不是更简单的路线吗?你的答案看起来像rename_(.dots=this_answer),对吧? rename 的帮助页面不会像 data.table 中的 setnames 那样通过引用来宣传修改。
  • @Anton 说得对,但这就是变通办法的本质。 (感谢 Mathhew 的评论,依赖关系又消失了。)我觉得应该扩展 dplyr 语法以支持 OP 的期望(基于 plyr),例如rename(replace_all=...)。如果在这里需要构造一个命名列表并知道将其传递给奇怪的参数.dots,这似乎是有缺陷的。
  • @Frank 我最终使用了你的答案 (+1),因为它是一种更简单的方法来做我想做的事——并教会了我关于 setNames 的知识——但@MatthewPlourde 更从字面上回答了书面的问题(即使用rename)。感谢您的宝贵时间!
【解决方案4】:

自 2020 年起,rename_if、rename_at 和 rename_all 被标记为已被取代。用 dplyr 解决这个问题的最新方法是 rename_with():

iris %>% rename_with(tolower)

或更复杂的版本:

iris %>% 
  rename_with(stringr::str_replace, 
              pattern = "Length", replacement = "len", 
              matches("Length"))

(编辑 2021-09-08)
正如@a_leemo 在评论中提到的那样,手册中没有逐字提到这个符号。相反,人们会从手册中推断出以下内容:

iris %>% 
  rename_with(~ stringr::str_replace(.x, 
                                     pattern = "Length", 
                                     replacement = "len"), 
              matches("Length")) 

两者都做同样的事情,但是,我发现第一个解决方案更具可读性。在第一个示例中,pattern = ... 和 replacement = ... 作为 ... 点实现的一部分被转发到函数。有关详细信息,请参阅 ?rename_with 和 ?dots。

【讨论】:

  • 谢谢!我一直在努力弄清楚如何使用 rename_with 对其进行编码,这成功了。
  • 如何为自定义函数 @loki 做到这一点?如果我在 rename_with 语句中编写函数,它可以自动传递名称,如果我在其他地方定义函数,它不会argument is not an atomic vector
  • 刚刚发现:根本不给函数任何参数,而是将其指定为函数mydataframe %&gt;% rename_with(myawesomefunction)
  • 这解决了我遇到的一个问题,谢谢!但是为什么str_replace() 函数内部的参数被拉到它之外呢?我无法从帮助文档中找出这种语法。
  • @a_leemo 更类似于手册的版本是:iris %&gt;% rename_with(~ stringr::str_replace(.x, pattern = "Length", replacement = "len"), matches("Length")),带有~ 和.x 符号。但是,我觉得这相当复杂。但是,正如您正确指出的那样,我提出的解决方案偏离了手册。感谢您的批评。我会相应地编辑我的答案。
【解决方案5】:

对于这个特殊的 [但相当常见的] 案例,该函数已经写在 janitor 包中:

library(janitor)

iris %>% clean_names()

##   sepal_length sepal_width petal_length petal_width species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
## .          ...         ...          ...         ...     ...

大家一起,

iris %>% 
    clean_names() %>%
    gather(measurement, value, -species) %>%
    group_by(species,measurement) %>%
    summarise(avg_value = mean(value))

## Source: local data frame [12 x 3]
## Groups: species [?]
## 
##       species  measurement avg_value
##        <fctr>        <chr>     <dbl>
## 1      setosa petal_length     1.462
## 2      setosa  petal_width     0.246
## 3      setosa sepal_length     5.006
## 4      setosa  sepal_width     3.428
## 5  versicolor petal_length     4.260
## 6  versicolor  petal_width     1.326
## 7  versicolor sepal_length     5.936
## 8  versicolor  sepal_width     2.770
## 9   virginica petal_length     5.552
## 10  virginica  petal_width     2.026
## 11  virginica sepal_length     6.588
## 12  virginica  sepal_width     2.974

【讨论】:

    【解决方案6】:

    我使用 base、stringr 和 dplyr 的雄辩尝试:

    编辑:library(tidyverse) 现在包括所有三个库。

    library(tidyverse)
    library(maggritr) # Though in tidyverse to use %>% pipe you need to call it 
    # library(dplyr)
    # library(stringr)
    # library(maggritr)
    
    names(iris) %<>% # pipes so that changes are apply the changes back
        tolower() %>%
        str_replace_all(".", "_")
    

    我这样做是为了使用管道构建函数。

    my_read_fun <- function(x) {
        df <- read.csv(x) %>%
        names(df) %<>%
            tolower() %>%
            str_replace_all("_", ".")
        tempdf %<>%
            select(a, b, c, g)
    }
    

    【讨论】:

    • str_replace_all 不在这些软件包中。仅供参考,无需在答案文本中包含“编辑”符号;让它成为可能的最佳答案。人们可以通过单击答案下方的链接来查看编辑历史记录。
    • 第一个 str_replace_all 函数中的句点应转义 \\. - 否则所有内容都将替换为下划线
    【解决方案7】:

    select() 和 select_all() 都可用于重命名列。

    如果您只想重命名特定列,您可以使用select:

    iris %>% 
      select(sepal_length = Sepal.Length, sepal_width = Sepal.Width, everything()) %>% 
      head(2)
    
      sepal_length sepal_width Petal.Length Petal.Width Species
    1          5.1         3.5          1.4         0.2  setosa
    2          4.9         3.0          1.4         0.2  setosa
    

    rename 做同样的事情,只是不必包含everything():

    iris %>% 
      rename(sepal_length = Sepal.Length, sepal_width = Sepal.Width) %>% 
      head(2)
    
      sepal_length sepal_width Petal.Length Petal.Width Species
    1          5.1         3.5          1.4         0.2  setosa
    2          4.9         3.0          1.4         0.2  setosa
    

    select_all() 适用于所有列,并且可以将函数作为参数:

    iris %>% 
      select_all(tolower)
    
    iris %>% 
      select_all(~gsub("\\.", "_", .)) 
    

    或将两者结合起来:

    iris %>% 
      select_all(~gsub("\\.", "_", tolower(.))) %>% 
      head(2)
    
      sepal_length sepal_width petal_length petal_width species
    1          5.1         3.5          1.4         0.2  setosa
    2          4.9         3.0          1.4         0.2  setosa
    

    【讨论】:

    • 这比rename 家族中的任何东西都更有效,也更直接......奇怪的是,使用select_all 和~gsub 比使用rename_at 或rename_if 更容易带有某种变量声明的谓词......似乎这就是rename_*的用途
    【解决方案8】:

    如果你不想自己写正则表达式,你可以使用

    • snakecase-pkg 非常灵活,
    • janitor::make_clean_names() 有一些不错的默认值或
    • janitor::clean_names() 与 make_clean_names() 的作用相同,但直接作用于数据帧。

    在管道内调用它们应该很简单。

    library(magrittr)
    library(snakecase)
    
    iris %>% setNames(to_snake_case(names(.)))
    iris %>% tibble::as_tibble(.name_repair = to_snake_case)
    iris %>% purrr::set_names(to_snake_case)
    iris %>% dplyr::rename_all(to_snake_case)
    iris %>% janitor::clean_names()
    
    

    【讨论】:

      猜你喜欢
      • 2016-07-30
      • 2021-05-19
      • 1970-01-01
      • 2021-10-22
      • 1970-01-01
      • 2017-06-10
      • 2019-02-17
      • 1970-01-01
      • 2019-05-10
      相关资源
      最近更新 更多