在R语言的ifelse语句中如何正确使用for循环的迭代计数器?
问题:R语言for循环中ifelse调用迭代计数器出错
我是R语言新手,第一次用for循环,在ifelse()函数里调用循环的迭代计数器i时遇到了问题。当循环迭代i=1时,concat_1列应该是seqBase和grpRnk的拼接值,但输出结果不对,错误情况如下(最右侧手动添加了每行的错误说明):
> nCode # A tibble: 8 x 6 Name Group nmCnt seqBase grpRnk concat_1 << ERROR in concat_1 column when loop i = 1 <chr> <dbl> <int> <int> <dbl> <dbl> 1 R 0 1 1 0 1 2 R 0 2 2 0 1 seqBase + grpRank = 2 3 X 0 1 1 0 1 4 X 1 2 2 1 1.1 seqBase + grpRank = 2.1 5 X 1 3 2 2 1.2 seqBase + grpRank = 2.2 6 X 0 4 3 0 1 seqBase + grpRank = 3 7 X 0 5 4 0 1 seqBase + grpRank = 4 8 X 0 6 5 0 1 seqBase + grpRank = 5
请问下面标注的ifelse()代码为什么会产生错误结果?
代码:
library(dplyr) library(stringr) myDF1 <- data.frame( Name = c("R","R","X","X","X","X","X","X"), Group = c(0,0,0,1,1,0,0,0) ) nCode <- myDF1 %>% group_by(Name) %>% mutate(nmCnt = row_number()) %>% ungroup() %>% mutate(seqBase = ifelse(Group == 0 | Group != lag(Group), nmCnt,0)) %>% mutate(seqBase = na_if(seqBase, 0)) %>% group_by(Name) %>% fill(seqBase) %>% mutate(seqBase = match(seqBase, unique(seqBase))) %>% ungroup() %>% mutate(grpRnk = ifelse(Group > 0, sapply(1:n(), function(x) sum(Name[1:x]==Name[x] & Group[1:x] == Group[x])),0)) for(i in 1:1) { concat_col <- str_c("concat_",i) reSeq_col <- str_c("reSeq_",i) nCode <- nCode %>% mutate(!! concat_col:= as.numeric(paste0(ifelse(i==1,seqBase,reSeq_col),".",grpRnk))) # problem lies here }
问题原因与解决方法
核心错误:列引用方式错误
你代码里的reSeq_col是字符串(比如"reSeq_1"),但在ifelse的第二个分支里直接写reSeq_col,R会把它当成普通字符串值,而不是数据框里对应的列。虽然当前循环i=1时,条件i==1为真,理论上会返回seqBase列,但由于ifelse的两个分支类型不一致(一个是数值向量,一个是字符向量),R会强制做隐式类型转换,可能导致意外结果(比如你的错误输出里seqBase的值被错误替换)。
当需要引用以字符串形式存储的列名时,必须用!! sym(reSeq_col)把字符串转换成列引用,这样R才会去数据框里找对应的列。
修改后的代码
for(i in 1:1) { concat_col <- str_c("concat_",i) reSeq_col <- str_c("reSeq_",i) nCode <- nCode %>% mutate(!! concat_col:= as.numeric(paste0(ifelse(i==1, seqBase, !! sym(reSeq_col)), ".", grpRnk))) }
更优写法:避免字符串拼接
字符串拼接转数值的方式效率较低,还容易出错,建议直接用数值计算生成目标值:
for(i in 1:1) { concat_col <- str_c("concat_",i) reSeq_col <- str_c("reSeq_",i) nCode <- nCode %>% mutate(!! concat_col:= ifelse(i==1, seqBase, !! sym(reSeq_col)) + grpRnk/10) }
这种方式直接计算seqBase + grpRnk/10(比如2+1/10=2.1),和字符串拼接转数值的结果一致,但更高效可靠。
内容的提问来源于stack exchange,提问作者Village.Idyot
相关产品推荐
相关产品推荐

