R中mapply结合rep与dplyr管道的异常行为排查
问题排查与解决
需求说明
要处理带*和|分隔符的字符串,示例输入:
"3*4|2*7.4|8*3.2"
其中:
*前的数字是重复频率,*后的是待重复的数值- 每组
频率*数值用|分隔
期望输出的向量是:
c("4","4","4","7.4","7.4","3.2","3.2","3.2","3.2","3.2","3.2","3.2","3.2")
错误代码及异常结果
你写的代码没有报错,但输出结果不符合预期:
strsplit("3*4|2*7.4|8*3.2", "[*|]") %>% unlist %>% mapply(FUN = rep, x = .[seq(from = 2, to = length(.), by = 2)], times = .[seq(from = 1, to = length(.), by = 2)], USE.NAMES = FALSE) %>% unlist
实际输出:
[1] "4" "4" "4" "7.4" "7.4" "7.4" "7.4" "3.2" "3.2" "4" "4" "4" "4" "4" "4" "4" "7.4" "7.4" "7.4" "7.4" "7.4" "7.4" "7.4" "7.4" "3.2" "3.2" "3.2"
可以看到7.4重复了4次(预期是2次),3.2重复次数也不对。
问题根源
问题出在mapply的循环回收特性上:
- 字符串拆分后得到的向量是:
c("3", "4", "2", "7.4", "8", "3.2") - 你取的
x是偶数索引元素:c("4", "7.4", "3.2"),times是奇数索引元素:c("3", "2", "8") mapply默认会把输入的多个向量做全组合循环——也就是每个x元素都会和每个times元素配对执行rep,最后把所有结果合并,这就导致了重复次数完全混乱。
修正方案
方案1:用Map替代mapply
Map是mapply的简化版本,默认不会做循环回收,只会一一对应配对元素:
strsplit("3*4|2*7.4|8*3.2", "[*|]") %>% unlist %>% { Map(rep, x = .[seq(2, length(.), 2)], times = .[seq(1, length(.), 2)]) } %>% unlist
方案2:先按|拆分每组,再逐个处理
这种方式逻辑更直观,不容易出错:
strsplit("3*4|2*7.4|8*3.2", "\\|") %>% unlist %>% lapply(function(group) { # 拆分每组的频率和数值 parts <- strsplit(group, "\\*")[[1]] rep(parts[2], as.integer(parts[1])) }) %>% unlist
两种方案都能得到预期输出:
[1] "4" "4" "4" "7.4" "7.4" "3.2" "3.2" "3.2" "3.2" "3.2" "3.2" "3.2" "3.2"
内容的提问来源于stack exchange,提问作者Aku-Ville Lehtimäki
相关产品推荐
相关产品推荐

