提取部分列名进行重命名

Extract parts of column names for renaming

我有一个数据框,其中一些列被命名为日期。例如,像这样:

df_1 <- data_frame("id" = c('a','b','c','d'),
                 "gender" = c('m','f','f','m'),
                 "05/16/2017" = c(1,2,3,4),
                 "11/08/2016" = c(1,2,3,4),
                 "08/15/2016" = c(1,2,3,4))

df_1
# A tibble: 4 x 5
  id    gender `05/16/2017` `11/08/2016` `08/15/2016`
  <chr> <chr>         <dbl>        <dbl>        <dbl>
1 a     m                 1            1            1
2 b     f                 2            2            2
3 c     f                 3            3            3
4 d     m                 4            4            4

对于当前日期的列,格式为 mm/dd/yyyy,我想提取 mmyyyy 组件并使用它们将列重命名为 election_yyyy_mm。 IE。我最终会得到如下所示的 df:

df_2 <- data_frame("id" = c('a','b','c','d'),
                 "gender" = c('m','f','f','m'),
                 "election_2017_05" = c(1,2,3,4),
                 "election_2016_11" = c(1,2,3,4),
                 "election_2016_08" = c(1,2,3,4))

df_2
# A tibble: 4 x 5
  id    gender election_2017_05 election_2016_11 election_2016_08
  <chr> <chr>             <dbl>            <dbl>            <dbl>
1 a     m                     1                1                1
2 b     f                     2                2                2
3 c     f                     3                3                3
4 d     m                     4                4                4

我想我有一个涉及 stringr 的部分解决方案,但目前我必须 运行 str_extract 两次才能获得 mmyyyy分别组成。我也不确定如何将矢量传递给 rename().

这是我目前的两个片段:

stringr::str_extract(c("05/16/2017", "11/08/2016", "08/15/2016"), "^[^/]+")
[1] "05" "11" "08"

stringr::str_extract(c("05/16/2017", "11/08/2016", "08/15/2016"), "[0-9]{4}")
[1] "2017" "2016" "2016"

谁能帮我 a) 在一次调用 str_extract(或其他函数)中提取两个元素(yyyymm 位),然后 b) 传递rename?

的结果向量

使用 tidyverse(dplyr 和 stringr),我们可以像这样重命名列:

library(dplyr)

df_1 %>% 
  rename_with(
    .cols = contains("/"), # selects only the date columns
    ~ paste0(
      "election_",  
      stringr::str_sub(.x, -4, -1), # last 4 digits/letters
      "_",
      stringr::str_sub(.x, 1, 2) # first 2 digits/letters
    )
  )

结果:

# A tibble: 4 x 5
  id    gender election_2017_05 election_2016_11 election_2016_08
  <chr> <chr>             <dbl>            <dbl>            <dbl>
1 a     m                     1                1                1
2 b     f                     2                2                2
3 c     f                     3                3                3
4 d     m                     4                4                4

我们可以使用rename_with函数重命名。 在重命名函数中,我们可以先将字符解析为带有 mdy() 的日期,然后提取 month()year()。最后,glue() 元素重新组合在一起。

library(dplyr)
library(glue)
library(lubridate)

df_1 %>% rename_with( ~glue('election_{year(mdy(.x))}_{month(mdy(.x))}'),
                      matches("\d{2}/\d{2}/\d{4}"))

输出

# A tibble: 4 × 5
  id    gender election_2017_5 election_2016_11 election_2016_8
  <chr> <chr>            <dbl>            <dbl>           <dbl>
1 a     m                    1                1               1
2 b     f                    2                2               2
3 c     f                    3                3               3
4 d     m                    4                4               4

我们还可以使用 stringr::string_extract_all 来处理向量而不是单个字符元素。使用来自 OP 尝试的修改后的正则表达式,我们可以在一次调用中提取月份和年份。只需从字符串的开头 (^) 或结尾 ($) 提取 (|) 数字 (\d+):"^\d+|\d+$".

答案是这样的:

df_1 %>% rename_with( ~stringr::str_extract_all(.x, "^\d+|\d+$") %>%
                              map_chr(~glue('election_{.x[2]}_{.x[1]}')),
                      matches("\d{2}/\d{2}/\d{4}"))

另一种使用 dplyr 但不使用 stringr 的方法。

此处使用 rename_with 到 select 输出 / 的列,拆分 / 上的字符串并使用 sapply 将拆分的结果连接在一起作为可用于重命名的向量。


df_1 %>%
    rename_with(.cols = contains('/'),
    ~ strsplit(.x, '/') %>% 
    sapply(
      function(x) paste0('election_',x[3],'_',x[2]),
      simplify=TRUE)
    )

已编辑以删除 as.character 调用,正如@GuedesBF 在评论中所解释的那样。

这是使用正则表达式的一行代码:

names(df_1) <- sub("(\d+).*?(\d+)$", "election_\2_\1", names(df_1))

这是如何工作的: 首先,将列名分成两个捕获组:

  • (\d+):第一个捕获组,捕获前两位
  • .*? 此后直到...
  • (\d+)$: ...第二个捕获组,捕获最后的数字。

然后,使用 sub 的替换参数,将字符串 election_ 添加到匹配的名称中,并使用反向引用 \1\2.

使用stringr

library(stringr)
names(df_1) <- str_replace(names(df_1), "(\d+).*?(\d+)$", "election_\2_\1")

结果:

df_1 
# A tibble: 4 × 5
  id    gender election_2017_05 election_2016_11 election_2016_08
  <chr> <chr>             <dbl>            <dbl>            <dbl>
1 a     m                     1                1                1
2 b     f                     2                2                2
3 c     f                     3                3                3
4 d     m                     4                4                4

这是另一种方法:

library(dplyr)
library(stringr)
df_1 %>% 
  rename_with(~str_c('election',str_sub(.x, -4,-1),str_sub(.x,-10,-9), sep = "_"), where(is.numeric))
  id    gender election_2017_05 election_2016_11 election_2016_08
  <chr> <chr>             <dbl>            <dbl>            <dbl>
1 a     m                     1                1                1
2 b     f                     2                2                2
3 c     f                     3                3                3
4 d     m                     4                4                4