【问题标题】:Rename variables via lookup table in R通过 R 中的查找表重命名变量
【发布时间】:2017-05-02 15:58:04
【问题描述】:

我有一个特定顺序的数据框:

df <- 
  data.frame(
    foo = 1:3,
    bar = LETTERS[1:3],
    baz = rnorm(3)
  )

df

  foo bar         baz
1   1   A  0.41474174
2   2   B -0.08416768
3   3   C -0.27931232

在另一个数据框中,我将旧变量名称与一些新名称匹配,但顺序不同

variable_match <- 
  data.frame(
    old = names(df)[c(2, 3, 1)], 
    new = LETTERS[1:3]
  )

variable_match
  old new
1 bar   A
2 baz   B
3 foo   C

我的问题是:如何通过在第二个数据框中查找相应的值来重命名原始数据框中的变量。理想情况下,我正在寻找tidyverse 解决方案。我尝试了以下变体:

library(tidyverse)

df %>% rename_at(variable_match$old, funs(variable_match$new))

假设 rename_at 是正确的方法,但这不起作用。我想知道purrr::map_* 是否是正确的方法,但不知道如何。非常感谢您的建议。

【问题讨论】:

  • 如何使用plyr::mapvalues
  • 也许这行得通,但我无法测试它df %&gt;% rename_at(variable_match$old, function(x) variable_match$new[variable_match$old == x])。我认为@Gregor 的回答很好
  • 您可以使用 dplyr 0.6 轻松移植 Gregor 的答案:df %&gt;% rename_all(funs(variable_match$new[match(., variable_match$old)]))。你也可以使用df %&gt;% rename(!!!setNames(rlang::syms(as.character(variable_match$old)), variable_match$new)),尽管我很确定:= 有一个更优雅的版本,我不知道该怎么写。
  • @alistaire 谢谢,这基本上就是我想要的。看起来我接下来需要了解rlang
  • 还有rename_(df, .dots = setNames(as.character(variable_match$old), variable_match$new))

标签: r dplyr tidyverse purrr


【解决方案1】:

这是一个单行base解决方案:

names(df2) = variable_match$new[match(names(df), variable_match$old)]

它对你来说可能不是“理想的”(它不需要tidyverse 来工作),但它很简单,不需要加载任何额外的包,而是依赖于常见的内置函数。


如 cmets 中所述,如果您更喜欢带有管道的嵌套语句(管道不是旨在提高可读性并防止嵌套吗?)上面的简单行相当于

library(purrr)
library(dplyr)
library(magrittr)
df = df %>%
    set_names(
        var_match %>%
        pull(new) %>%
        extract(
            names(df) %>% 
            match(var_match$old)
        )
    )

我是管道的忠实拥护者,dplyr - 当让事情变得更简单、更易读时,我会一直使用它们。在这种情况下,他们将一条简单的线变成了一个编程难题,包括如何编写和如何阅读。

整体上更好的界面是data.table::setnames 函数。如果转换为数据表,则代码为setnames(df, old = var_match$old, new = var_match$new)。这在并非所有名称都被更改的情况下非常可靠(参见下面的 cmets)。

【讨论】:

  • 谢谢 - 这可以通过上面 alistaire 的评论直接移植到 tidyverse,或 df %&gt;% set_names( var_match %&gt;% pull(new) %&gt;% magrittr::extract(names(df) %&gt;% match(var_match$old)) )
  • 这行得通,但是如果有几列不匹配怎么办。例如,您的查找表仅引用原始数据框中 80% 的字段。如果在我的查找表中找不到该名称,那么我不想重命名它,但保持名称不变。此映射为不在查找表中的字段提供 NA 名称
  • @runningbirds 是的,OP 的问题是关于重新排序,而不是关于部分替换。您可以进行小的修改,以便仅替换匹配的名称:(调用查找表vm:names(df2)[names(df2) %in% vm$old] = vm$new[match(names(df2)[names(df2) %in% vm$old], vm$old)]
【解决方案2】:

我对 Gregor 的答案进行了功能化,以便可以在管道中轻松使用它:

lookup_rename <- function(df, column_lookup) {
  df2 <- df
  # using Gregor's answer (https://stackoverflow.com/a/43742442/3927208)
  names(df2) = column_lookup$new[match(names(df), column_lookup$old)]
  df2
}

我希望这能说明base 解决方案如何在tidyverse 理念中轻松使用。也可能值得在函数中添加一些错误处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多