【问题标题】:Using dplyr's rename() including variable names not in data set使用 dplyr 的 rename() 包括不在数据集中的变量名
【发布时间】:2023-03-10 15:04:01
【问题描述】:

我正在尝试将一些 plyr 代码转换为 dplyr,并且在 dplyr 中遇到了 rename() 的新功能。我希望能够为一组具有重叠但不完全相同的原始名称的数据集重用一个 rename() 表达式。例如,

sample1 <- data.frame(A=1:10, B=letters[1:10])

sample2 <- data.frame(B=11:20, C=letters[11:20])

然后,

 rename(sample1, var1 = A, var2 = B, var3 = C)

我希望结果是变量 A 重命名为 var1,B 重命名为 var2,在这种情况下不添加 var3。相反,我得到了

错误:未知变量:C。

相比之下,plyr 语法可以让我使用

rename(sample1, c("A" = "var1", "B" = "var2", "C" = "var3"))
rename(sample2, c("A" = "var1", "B" = "var2", "C" = "var3"))

并且不抛出错误。有没有办法在不出现未知变量错误的情况下在 dplyr 中获得相同的结果?

【问题讨论】:

  • 您可以专门从 plyr 引用重命名函数:plyr::rename(sample1, c("A" = "var1", "B" = "var2", "C" = "var3"))

标签: r plyr dplyr


【解决方案1】:

完全无视您对如何使用 dplyr 执行此操作的实际要求,我想建议使用查找表的不同方法:

sample1 <- data.frame(A=1:10, B=letters[1:10])
sample2 <- data.frame(B=11:20, C=letters[11:20])

rename_map <- c("A"="var1",
                "B"="var2",
                "C"="var3")

names(sample1) <- rename_map[names(sample1)]
str(sample1)

names(sample2) <- rename_map[names(sample2)]
str(sample2)

从根本上说,算法很简单:

  1. 构建当前变量名到所需名称的查找表
  2. 使用 names() 函数,使用映射索引查找映射并将这些映射变量分配给适当的列。

编辑:根据 Hadley 的建议,我使用命名向量而不是列表,让生活更轻松。我总是忘记命名向量:(

【讨论】:

  • 您可以通过使用命名字符向量而不是命名列表来简化此操作
  • 如果您只想命名列的子集,这会将所有其他现有列名称设置为 NA。
【解决方案2】:
    #no need to use rename 

    oldnames<-unique(c(names(sample1),names(sample2)))
    newnames<-c("var1","var2","var3")
    name_df<-data.frame(oldnames,newnames)
    mydata<-list(sample1,sample2) # combined two datasets as a list
#one liner
    finaldata <- lapply(mydata, function(i) {colnames(i)<-name_df[name_df[,1] %in%  colnames(i),2]
return(i)})
> finaldata
[[1]]
   var1 var2
1     1    a
2     2    b
3     3    c
4     4    d
5     5    e
6     6    f
7     7    g
8     8    h
9     9    i
10   10    j

[[2]]
   var2 var3
1    11    k
2    12    l
3    13    m
4    14    n
5    15    o
6    16    p
7    17    q
8    18    r
9    19    s
10   20    t

【讨论】:

    【解决方案3】:

    使用dplyr,我们可以使用旧名称作为值,新名称作为名称的命名向量,然后仅取消引用name_vec 中与数据集中名称匹配的值。 rename 支持不带引号的字符,因此无需事先将其转换为sym

    library(dplyr)
    
    name_vec <- c(var1 = "A", var2 = "B", var3 = "C")
    
    sample1 %>%
      rename(!!name_vec[name_vec %in% names(.)])
    
    sample2 %>%
      rename(!!name_vec[name_vec %in% names(.)])
    

    另外,setNames:

    name_vec <- c(A = "var1", B = "var2", C = "var3")
    
    sample1 %>%
      setNames(name_vec[names(.)])
    
    sample2 %>%
      setNames(name_vec[names(.)])
    

    输出:

       var1 var2
    1     1    a
    2     2    b
    3     3    c
    4     4    d
    5     5    e
    6     6    f
    7     7    g
    8     8    h
    9     9    i
    10   10    j
    
       var2 var3
    1    11    k
    2    12    l
    3    13    m
    4    14    n
    5    15    o
    6    16    p
    7    17    q
    8    18    r
    9    19    s
    10   20    t
    

    【讨论】:

      【解决方案4】:

      我以前用过@earino的答案 我自己,但发现它可能不安全。如果数据的列名 命名向量(的名称)中缺少框架,这些列名称会被NA 默默替换,这肯定不是您想要的。

      d1 <- data.frame(A = 1:10, B = letters[1:10], stringsAsFactors = FALSE)
      
      rename_vec <- c("B" = "var2", "C" = "var3")
      
      names(d1) <- rename_vec[names(d1)]
      str(d1)
      #> 'data.frame':    10 obs. of  2 variables:
      #>  $ NA  : int  1 2 3 4 5 6 7 8 9 10
      #>  $ var2: chr  "a" "b" "c" "d" ...
      

      如果您运行names(d1) &lt;- rename_vec[names(d1)],也会发生同样的情况 意外两次,因为当你第二次运行它时,没有 colnames(d1)names(rename_vec)

      names(d1) <- rename_vec[names(d1)]
      str(d1)
      #> 'data.frame':    10 obs. of  2 variables:
      #>  $ NA: int  1 2 3 4 5 6 7 8 9 10
      #>  $ NA: chr  "a" "b" "c" "d" ...
      

      我们只需要在重命名向量中选择数据框中的那些列。

      d2 <- data.frame(B1 = 1:10, B = letters[1:10], stringsAsFactors = FALSE)
      
      sel <- is.element(colnames(d2), names(rename_vec))
      names(d2)[sel] <- rename_vec[names(d2)][sel]
      str(d2)
      #> 'data.frame':    10 obs. of  2 variables:
      #>  $ B1  : int  1 2 3 4 5 6 7 8 9 10
      #>  $ var2: chr  "a" "b" "c" "d" ...
      

      更新:我最初在这里有一个涉及字符串替换的解决方案,结果证明这也是不安全的,因为它允许部分匹配。我觉得这个更好。

      【讨论】:

      • 您也可以坚持使用原始解决方案,但为了安全起见,请添加以下内容:stopifnot(all(names(sample1) %in% names(rename_vec )))
      • @snoram 我认为我找到了一个比我原来的解决方案更好的解决方案,这实际上只是对 earino 的一个小调整。
      猜你喜欢
      • 2018-07-04
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 2014-08-25
      • 2021-01-19
      • 1970-01-01
      • 2020-10-16
      • 1970-01-01
      相关资源
      最近更新 更多