R语言pivot_longer的names_pattern匹配两位数波次编号问题
问题原因
你的正则表达式(.*)(\\d+)$使用了贪婪匹配规则,.*会优先匹配尽可能长的字符:
- 对于
Age1到Age9的列名,.*匹配Age,\\d+匹配末尾的1-9,拆分逻辑正常 - 对于
Age10到Age13的列名,.*会把Age1作为第一分组匹配结果,仅把末尾的0/1/2/3分到波次分组,因此会额外生成Age1列,同时出现错误的波次值0等。
解决方法
调整names_pattern的正则表达式即可,推荐两种写法:
写法1:使用非贪婪匹配
在.*后加?开启非贪婪匹配,让第一分组尽可能短的匹配,把末尾所有数字都留给波次分组:
df_long <- pivot_longer(df, cols = !no_numb, names_to = c(".value", "wave"), names_pattern = "(.*?)(\\d+)$")
写法2:限定第一分组仅匹配非数字字符
直接指定第一分组只匹配数字以外的字符,逻辑更严谨,避免后续变量名包含数字时出错:
df_long <- pivot_longer(df, cols = !no_numb, names_to = c(".value", "wave"), names_pattern = "([^0-9]+)(\\d+)$")
如果需要波次直接输出为数值型,可新增names_transform参数:
df_long <- pivot_longer(df, cols = !no_numb, names_to = c(".value", "wave"), names_pattern = "([^0-9]+)(\\d+)$", names_transform = list(wave = as.integer))
可选优化
你原有代码中识别固定列的正则可以简化,开头的*无实际匹配作用,修改后逻辑更清晰:
no_numb <- grep("[A-Za-z]$", names_test)
转换效果
修正后输出的长表每个ID对应13行,仅保留1个Age列,wave列取值为1-13的波次编号,不会再出现拆分错误的Age1列。
内容的提问来源于stack exchange,提问作者Hi_Viz
相关产品推荐
相关产品推荐

