You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用dplyr::pivot_longer转换宽表为长表的结构匹配问题

宽表转长表:调整pivot_longer的names_to和names_pattern参数

现有一个宽格式数据集,包含3个波次(wave)的好友提名(fid)数据,每个波次下有3个好友提名,且每个提名对应a、b、c三项跟进问题。需要将该宽表转换为指定结构的长表,但当前使用的pivot_longer代码输出不符合预期,需调整names_to和names_pattern参数。

示例数据

df <- read.table(text= "id    w1fid1  w1fid2  w1fid3  w2fid1  w2fid2  w2fid3  w3fid1  w3fid2  w3fid3  w1a1    w1a2    w1a3    w1b1    w1b2    w1b3    w1c1    w1c2    w1c3    w2a1    w2a2    w2a3    w2b1    w2b2    w2b3    w2c1    w2c2    w2c3    w3a1    w3a2    w3a3    w3b1    w3b2    w3b3    w3c1    w3c2    w3c3
1   10  3   9   4   13  9   15  14  NA  1   1   0   0   1   0   1   0   0   1   1   1   1   0   0   1   1   1   1   1   NA  1   0   NA  0   0   NA
2   13  NA  20  4   18  19  12  10  8   1   NA  0   0   NA  0   0   NA  0   1   0   0   1   1   0   0   1   0   0   0   0   1   1   0   0   1   0   0
3   NA  9   19  19  8   20  17  9   2   NA  1   1   NA  0   0   NA  0   0   0   0   1   0   0   0   1   0   0   0   0   1   0   0   0   1   1   0
4   13  19  9   19  16  2   2   11  14  1   1   1   1   1   1   1   1   0   0   0   1   0   0   1   0   0   0   0   0   0   0   0   1   0   1   0
5   11  8   9   18  20  7   9   17  12  0   1   1   0   0   0   1   1   1   1   0   1   1   1   0   1   0   1   1   0   0   1   1   1   0   1   0", header = TRUE)

期望输出

df_target <- read.table(text= "id   wave   fid a   b   c
1   1   10  1   0   1
1   1   3   1   1   0
1   1   9   0   0   0
1   2   4   1   1   1
1   2   13  1   0   1
1   2   9   1   0   1
1   3   15  1   1   0
1   3   14  1   0   0
1   3   NA  NA  NA  NA
2   1   13  1   0   0
2   1   NA  NA  NA  NA
2   1   20  0   0   0
2   2   4   1   1   0
2   2   18  0   1   1
2   2   19  0   0   0
2   3   12  0   1   1
2   3   10  0   0   0
2   3   8   0   0   0
3   1   NA  NA  NA  NA
3   1   9   1   0   0
3   1   19  1   0   0
3   2   19  0   0   1
3   2   8   0   0   0
3   2   20  1   0   0
3   3   17  0   0   1
3   3   9   0   0   1
3   3   2   1   0   0
4   1   13  1   1   1
4   1   19  1   1   1
4   1   9   1   1   0
4   2   19  0   0   0
4   2   16  0   0   0
4   2   2   1   1   0
4   3   2   0   0   0
4   3   11  0   0   1
4   3   14  0   1   0
5   1   11  0   0   1
5   1   8   1   0   1
5   1   9   1   0   1
5   2   18  1   1   1
5   2   20  0   1   0
5   2   7   1   0   1
5   3   9   1   1   0
5   3   17  0   1   1
5   3   12  0   1   0", header = TRUE)

当前代码及输出

当前代码

df %>% pivot_longer(-id, 
                     names_to = c('wave', 'type', '.value'),
                     names_pattern = "(\\w+\\d+)(\\w+)(\\d+)") 

当前输出

# A tibble: 60 × 5
      id wave  type    `1`   `2`   `3`
   <int> <chr> <chr> <int> <int> <int>
 1     1 w1    fid      10     3     9
 2     1 w2    fid       4    13     9
 3     1 w3    fid      15    14    NA
 4     1 w1    a         1     1     0
 5     1 w1    b         0     1     0
 6     1 w1    c         1     0     0
 7     1 w2    a         1     1     1
 8     1 w2    b         1     0     0
 9     1 w2    c         1     1     1
10     1 w3    a         1     1    NA
# ℹ 50 more rows

解决方案

问题出在正则表达式的分组逻辑和names_to的映射顺序错误。正确的做法是将列名拆分为波次标识、数据类型、提名序号三个部分,让数据类型对应最终的列名(fid/a/b/c),提名序号用来对齐同一提名的各项数据。

正确代码

library(tidyr)
library(dplyr)

df %>%
  pivot_longer(
    cols = -id,
    names_to = c("wave", ".value", "nomination"),
    names_pattern = "(w\\d)(fid|a|b|c)(\\d)"
  ) %>%
  # 将wave从字符型转为整数(如w1→1)
  mutate(wave = as.integer(sub("w", "", wave))) %>%
  # 调整列顺序并移除临时的nomination列
  select(id, wave, fid, a, b, c) %>%
  # 按id和wave排序,匹配期望输出
  arrange(id, wave)

代码说明

  1. names_pattern正则分组:
    • (w\\d):匹配波次前缀(w1/w2/w3)
    • (fid|a|b|c):匹配数据类型(好友提名或三个跟进问题)
    • (\\d):匹配每个波次下的提名序号(1/2/3)
  2. names_to映射:
    • 第一个分组对应wave列,存储波次信息
    • 第二个分组用.value,表示这部分字符串将作为最终的列名(fid/a/b/c)
    • 第三个分组对应临时列nomination,用于对齐同一序号的fid和a/b/c数据
  3. 后续通过mutate转换波次格式,select调整列顺序,arrange排序,最终得到与期望一致的长表结构。

内容的提问来源于stack exchange,提问作者L. Tucker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 09:15:54