在R中使用dplyr和stringr时,get_hundred函数处理数据集异常的原因?
问题:mutate调用自定义函数时结果不符合预期
需求
通过自定义函数get_hundred结合mutate,提取字符串中连续三位数字的首位(字符型),存入新列Hundreds:
- 字符串
'821'→'8' - 字符串
'Af823.22'→'8'
自定义函数
get_hundred <- function(s) { match_pos <- str_locate(s, "[0-9]{3}") return(str_sub(s, match_pos[1], match_pos[1])) }
测试数据集
df1 <- structure(list(call.number = c("372.35044 L4383", "344.049 C235", "344.410415 DIM", "346.944043 NEI", "808.0667 B2616", "363.6909945 CAST", "ABS 2015.0", "371.38 MACK", "372.1102 PRAW", "A823.3 WRIG/T", "havmf test", "[DENTISTRY] CROW", "[DENTISTRY] JAWS", "[DENTISTRY] LOWE", "[DENTISTRY] MOLA", "[DENTISTRY] SERI", "[DENTISTRY] SKUL", "[DENTISTRY] TEET", "[HEALTH]ANKL", "[HEALTH]FOOT"), num.items = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2)), row.names = c(NA, -20L), class = c("tbl_df", "tbl", "data.frame"))
数据过滤
仅保留符合^[A-Z]?[A-Z|a-z]?[0-9]{3}.*格式的记录:
df2 <- df1 %>% filter(str_detect(call.number, "^[A-Z]?[A-Z|a-z]?[0-9]{3}.*"))
异常现象
执行以下代码时:
df2 %>% mutate(Hundreds = get_hundred(call.number))
df2中"A823.3 WRIG/T"对应的Hundreds列被填入'A',但单独调用get_hundred("A823.3 WRIG/T")能正确返回'8'。
原因
str_locate处理向量输入时返回的是矩阵,而非单个位置值。在mutate中传入整个call.number列(向量)时,match_pos是n行2列的矩阵,此时match_pos[1]取的是矩阵的第一个元素(即第一行的匹配起始位置),后续所有行都会复用这个位置值进行截取。比如df2第一行的匹配起始位置是1,所以第9行用位置1截取到了'A'。而单独调用时,s是单个字符串,match_pos是长度为2的向量,match_pos[1]是该字符串的匹配起始位置,结果自然正确。
解决方案
方案1:优化正则表达式,直接提取目标字符
利用str_extract的向量化特性,结合符合过滤规则的正则,一步到位:
get_hundred <- function(s) { str_extract(s, "(?<=^[A-Za-z]{0,2})[0-9]") }
该正则匹配开头0-2个字母后的第一个数字,完全贴合需求,且原生支持向量输入。
方案2:逐行处理元素
用purrr::map_chr逐个处理每个字符串,确保每个元素的匹配位置独立计算:
library(purrr) get_hundred <- function(s) { map_chr(s, function(x) { match_pos <- str_locate(x, "[0-9]{3}") str_sub(x, match_pos[1], match_pos[1]) }) }
方案3:使用rowwise逐行执行mutate
让mutate逐行处理数据,此时每个字符串的match_pos都是独立的向量:
df2 %>% rowwise() %>% mutate(Hundreds = get_hundred(call.number)) %>% ungroup()
内容的提问来源于stack exchange,提问作者Klew Lesse
相关产品推荐
相关产品推荐

