【问题标题】:How to match and replace a subset of column names如何匹配和替换列名的子集
【发布时间】:2019-03-28 22:02:22
【问题描述】:

我正在尝试用更具描述性的标签替换数据框中的一些(但不是全部)列名。 我有一个带有长名称的向量,需要匹配和替换当前相关的列名。

更详细的:

我有一个包含文本和数字列的数据框。 例如

df<-data.frame(text1=c("nnnn","uuuu","ooo"),
               text2=c("b","t","eee"),
               a1=c(1,2,3),
               a2=c(45,43,23),
               b1=c(43,6,2),
               text3=c("gg","ll","jj"))

所以它看起来像这样:

df
  text1 text2 a1 a2 b1 text3
1  nnnn     b  1 45 43    gg
2  uuuu     t  2 43  6    ll
3   ooo   eee  3 23  2    jj

对于某些列标签,我还有一个较长标签的向量:

longnames=c("a1 age","a2 gender","b1 postcode")

如果有匹配的长名称,我想完全替换df中对应的短名称。所以我想要的输出是:

  text1 text2 a1 age a2 gender b1 postcode text3
1  nnnn     b      1        45          43    gg
2  uuuu     t      2        43           6    ll
3   ooo   eee      3        23           2    jj

所有需要替换的短标签都与相关长标签的开头唯一匹配。也就是说,短标签“a2”需要替换成长标签“a2 gender”,而这个长标签是唯一以“a2"。

【问题讨论】:

    标签: r


    【解决方案1】:

    dplyr::rename 可以一次性重命名列的子集,但新名称需要一个命名向量。

    library("tidyverse")
    
    df <- data.frame(
      text1 = c("nnnn", "uuuu", "ooo"),
      text2 = c("b", "t", "eee"),
      a1 = c(1, 2, 3),
      a2 = c(45, 43, 23),
      b1 = c(43, 6, 2),
      text3 = c("gg", "ll", "jj")
    )
    
    longnames <- c("a1 age", "a2 gender", "b1 postcode")
    shortnames <- str_extract(longnames, "^(\\w+)")
    
    # named vector specifying how to rename
    names(shortnames) <- longnames
    shortnames
    #>      a1 age   a2 gender b1 postcode 
    #>        "a1"        "a2"        "b1"
    
    df %>%
      rename(!!shortnames)
    #>   text1 text2 a1 age a2 gender b1 postcode text3
    #> 1  nnnn     b      1        45          43    gg
    #> 2  uuuu     t      2        43           6    ll
    #> 3   ooo   eee      3        23           2    jj
    
    # In this case `!!shortnames` achieves this:
    
    df %>%
      rename("a1 age" = "a1",
             "a2 gender" = "a2",
             "b1 postcode" = "b1")
    #>   text1 text2 a1 age a2 gender b1 postcode text3
    #> 1  nnnn     b      1        45          43    gg
    #> 2  uuuu     t      2        43           6    ll
    #> 3   ooo   eee      3        23           2    jj
    

    reprex package (v0.2.1) 于 2019 年 3 月 28 日创建

    以编程方式指定新名称很有用,因为我们可以更轻松、更清晰地更改列名称规范。但是为了提高可读性,您可以从显式规范开始,它只是更多的写作。

    【讨论】:

    • 你能解释一下'!!​​'是什么吗?是什么意思?
    • 这是一个“整洁评估”的例子。一个重要的话题,你可以在这里阅读更多:dplyr.tidyverse.org/articles/programming.html
    • 在这种特殊情况下,!! 有助于将列名向量(指定旧名称和新名称对)传递给rename 函数。
    • 感谢您的解释!要实际更改 df 中的名称,最后一行需要是: df % rename (!!shortnames)
    【解决方案2】:
    m1 = sapply(names(df), function(snm) sapply(longnames, function(lnm) grepl(snm, lnm)))
    df1 = setNames(df, replace(names(df), colSums(m1) == 1, longnames[rowSums(m1) == 1]))
    df1
    #  text1 text2 a1 age a2 gender b1 postcode text3
    #1  nnnn     b      1        45          43    gg
    #2  uuuu     t      2        43           6    ll
    #3   ooo   eee      3        23           2    jj
    

    m1 是一个矩阵,显示了dflongnames 的列名之间的匹配。 colSums(m1) == 1 标识匹配的列名。 rowSums(m1) == 1 标识各自匹配的longnames

    或使用部分匹配

    inds = pmatch(colnames(df), longnames)
    df1 = setNames(df, replace(longnames[inds], is.na(inds), colnames(df)[is.na(inds)]))
    

    【讨论】:

    • 这两个示例都在控制台中给出了正确的输出,但 setNames 行似乎并没有真正改变 df。如果我做 names(df)
    【解决方案3】:

    您可以使用已经矢量化的adist

    a = which(!attr(adist(names(df),longnames,counts = T),'counts')[,,'sub'],T)
    
    names(df)[a[,'row']] = longnames    #longnames[a[,'col']]
    
    df
      text1 text2 a1 age a2 gender b1 postcode text3
    1  nnnn     b      1        45          43    gg
    2  uuuu     t      2        43           6    ll
    3   ooo   eee      3        23           2    jj
    

    【讨论】:

      【解决方案4】:

      使用sapply 的一种方法。这可以使用for 循环以及几乎精确的代码来完成。 seq.int(colnames(df)) 产生一个序列 1:ncol(df)。当df 中的相应列名匹配时,greplongnames 中找到索引。然后if 条件检查索引向量的长度是否> 0(如果有列匹配,则应该是)。然后它进行替换。

      ## sapply (can be replaced with lapply)
      sapply(seq.int(colnames(df)), function(x) {
        index <- grep(colnames(df)[x], longnames)
        if (length(index) > 0) colnames(df)[x] <<- longnames[index]
      })
      

      ## for loop (note the difference in <<-)
      for (x in seq.int(colnames(df))) {
        index <- grep(colnames(df)[x], longnames)
        if (length(index) > 0) colnames(df)[x] <- longnames[index]
      }
      

      【讨论】:

      • 这很接近但不太有效。它产生一个包含 6 个元素的列表,不匹配任何长名称的条目为 NULL。我对这段代码中发生的事情很感兴趣,你能说一下每一行在做什么吗?
      • 添加了说明。 if 语句行的后半部分有错误。现在已修复,它应该可以工作。它仍然可能在控制台上输出一些带有 NULL 的输出,但这仅适用于不匹配的列,df 列同时更新。
      猜你喜欢
      • 2019-07-22
      • 1970-01-01
      • 1970-01-01
      • 2019-10-11
      • 2018-02-26
      • 2017-03-04
      • 2019-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多