使用case_when基于子串生成新列出错及优化方法咨询
问题解答:基于字符串匹配生成DataFrame新列的错误分析与优化方法
1. 错误原因分析
你的代码存在两个关键问题:
- 变量名拼写错误:你创建的DataFrame叫
df,但后续mutate时误用了未定义的test变量,应该写成df %>% mutate(...)。 - 匹配逻辑错误:
case_when中使用length(grep('./1', df$filename)) > 0是判断整个列是否存在匹配./1的元素,只要列里有一个符合,这个条件就会返回TRUE。因为你的数据里确实有./1开头的行,所以第一个条件对所有行都成立,导致所有filenum都被赋值为1。
正确的逻辑应该是逐行判断当前行的filename是否匹配,要用grepl(返回与原列长度一致的逻辑向量,每一行对应一个判断结果),而不是grep(返回匹配元素的位置索引,length统计的是整个列的匹配总数)。
修正后的代码如下:
library(tidyverse) filename <- c('./1_test1', './1_test2', './2_test1', './2_test2', './3_test1', './3_test2') df <- data.frame(filename) df <- df %>% mutate(filenum = case_when( grepl('./1', filename) ~ 1, grepl('./2', filename) ~ 2, grepl('./3', filename) ~ 3 ))
运行后就能得到预期结果:
filename filenum 1 ./1_test1 1 2 ./1_test2 1 3 ./2_test1 2 4 ./2_test2 2 5 ./3_test1 3 6 ./3_test2 3
2. 更高效的子串提取方法
针对从固定格式字符串中生成新列的需求,有几种更简洁的方法:
方法一:直接提取数字(推荐)
如果你的filename格式固定为./数字_xxx,可以用str_extract直接提取数字部分,无需多条件判断:
df <- df %>% mutate(filenum = as.integer(str_extract(filename, "\\d")))
这里\\d是正则表达式,匹配单个数字,str_extract会提取第一个匹配的数字,再转成整数类型,结果和预期一致。
方法二:用str_detect替代grepl(tidyverse风格)
str_detect是tidyverse原生的字符串判断函数,写法更统一:
df <- df %>% mutate(filenum = case_when( str_detect(filename, './1') ~ 1, str_detect(filename, './2') ~ 2, str_detect(filename, './3') ~ 3 ))
方法三:基于文件路径拆分
如果是处理文件路径,也可以先去掉路径前缀,再拆分字符串:
df <- df %>% mutate(basename = basename(filename), filenum = as.integer(str_split(basename, "_", simplify = TRUE)[,1]))
内容的提问来源于stack exchange,提问作者Nemo
相关产品推荐
相关产品推荐

