如何用dplyr的pivot_longer拆分表头转换多场景年份宽表?
宽表转长表:pivot_longer() 用法修正与names_pattern详解
问题背景
我有一个包含关联变量link_col、多场景(S1、S2、S3)多年份数据的宽表,数据构造代码如下:
link_col <- c("A0001", "A0002", "A0003") S1_ABC_2019 <- c(1000, 300, 5000) S1_ABC_2020 <- c(1000, 300, 5000) S1_ABC_2021 <- c(1000, 300, 5000) S2_ABC_2012 <- c(800, 600, 2000) S2_ABC_2013 <- c(800, 600, 2000) S2_ABC_2019 <- c(800, 600, 2000) S3_ABC_2013 <- c(600, 100, 900) S3_ABC_2020 <- c(600, 100, 900) S3_ABC_2021 <- c(600, 100, 900) data <- data.frame(link_col, S1_ABC_2019, S1_ABC_2020, S1_ABC_2021, S2_ABC_2012, S2_ABC_2013, S2_ABC_2019, S3_ABC_2013, S3_ABC_2020, S3_ABC_2021)
希望用dplyr生态的pivot_longer()将宽表转换为包含Scenario、Year、Value列的长表,但尝试的代码无法运行,也搞不懂names_pattern参数的用法。我尝试的代码如下:
data2 <- pivot_longer(data, cols = link_col, starts_with("S"), names_to = c("Scenario", "Year"), names_pattern = "S(\\d+)_ABC_2(\\d+)", values_to = Value)
问题修正与正确代码
你的代码有3处关键错误,修正后即可正常运行:
- cols参数错误:
link_col是需要保留的标识列,不能放到cols里,cols应该指定要转成行的列,也就是所有以S开头的列,正确写法是cols = starts_with("S") - values_to参数缺引号:
values_to的值是列名,必须用引号包裹,即values_to = "Value" - names_pattern正则不完整:原正则只捕获了年份中
2之后的部分,会导致年份变成019这种错误格式,应该捕获完整的四位年份
正确代码如下(注意要加载tidyr包,pivot_longer()属于tidyr):
library(dplyr) library(tidyr) data2 <- data %>% pivot_longer( cols = starts_with("S"), names_to = c("Scenario", "Year"), names_pattern = "S(\\d+)_ABC_(\\d{4})", values_to = "Value" )
names_pattern参数详解
names_pattern的核心是用正则捕获分组拆分列名,规则很简单:
- 正则中用
()包裹的部分就是要提取的内容,分组的顺序必须和names_to里的列名顺序完全对应 - 以本次的正则
S(\\d+)_ABC_(\\d{4})为例:S(\\d+):匹配以S开头的前缀,(\\d+)捕获后面的数字(比如1、2),对应Scenario列_ABC_:匹配列名里固定的分隔符,不需要捕获,直接跳过(\\d{4}):捕获连续四位数字,对应Year列的完整年份(比如2019、2012)
转换后结果示例
运行正确代码后,得到的长表结构如下(展示前5行):
# A tibble: 27 × 4 link_col Scenario Year Value <chr> <chr> <chr> <dbl> 1 A0001 1 2019 1000 2 A0001 1 2020 1000 3 A0001 1 2021 1000 4 A0001 2 2012 800 5 A0001 2 2013 800 # … with 22 more rows
内容的提问来源于stack exchange,提问作者Rlearn
相关产品推荐
相关产品推荐

