使用多个 ID 列和值列通过 pivot_longer() 简化 gather()

Simplifying gather() with pivot_longer() using multiple ID columns and value columns

我正在努力更好地处理来自 gather 用户的 pivot_longer。从源文档看来,我应该能够使用 name_pattern 或 names_sep 在单个命令中执行以下操作,但我一直无法找到可行的解决方案。

数据

id1 <- c("person1","person2","person3")
id2 <- c("1001","1002","1003")
id3 <- c("2001","2002", "2003")
value_1 <- c(10,50,100)
value_2 <- c(20,200, 2000)
status_1 <- c("OK","BAD","GOOD")
status_2 <- c("AWFUL","EXCELLENT","AVERAGE")

df <- data.frame(id1,id2,id3,value_1,value_2,status_1,status_2)

预期输出:

      id1  id2  id3 gradeLevel    status value
1 person1 1001 2001          1        OK    10
2 person1 1001 2001          2     AWFUL    20
3 person2 1002 2002          1       BAD    50
4 person2 1002 2002          2 EXCELLENT   200
5 person3 1003 2003          1      GOOD   100
6 person3 1003 2003          2   AVERAGE  2000

我可以通过 gather 语句和一些额外的行来实现:

df %>% 
  gather(key, value,-id1, -id2,-id3) %>% 
  separate(key, c('cat', 'gradeLevel'),sep ="_") %>% 
  distinct() %>% 
  spread(cat,value)

有没有办法用 pivot_longer 来简化这个?我认为 names_pattern 很有前途,但我在正则表达式方面遇到了困难。我的大部分尝试都是尝试组合不同类型的列(双精度和因子)

df %>%
  pivot_longer(cols = value_1:status_2, names_to = c('col', '.value'), names_pattern = "(.*)_(.)")

您可以使用以下解决方案。在本例中,我们创建了 2 个捕获组。 .value 在这里所做的实际上是为 pivot_longer 定义名称的一部分,其中包含我们要测量的值的名称。这里下划线的左边是值 valuestatus。下划线的右侧实际上是我们第二个捕获组的结果定义了 id。并且应该注意 names_to 参数的长度应该与 names_pattern 或可能 names_sep.

中捕获组的数量相同
library(tidyr)

df %>%
  pivot_longer(!c(id1, id2, id3), names_to = c(".value", "gradelevel"), 
               names_pattern = "(\w+)_(\d+)")

# A tibble: 6 x 6
  id1     id2   id3   gradelevel value status   
  <chr>   <chr> <chr> <chr>      <dbl> <chr>    
1 person1 1001  2001  1             10 OK       
2 person1 1001  2001  2             20 AWFUL    
3 person2 1002  2002  1             50 BAD      
4 person2 1002  2002  2            200 EXCELLENT
5 person3 1003  2003  1            100 GOOD     
6 person3 1003  2003  2           2000 AVERAGE