R语言如何无需rowwise,在管道工作流中用case_when高效匹配大量条件?
解决方案
问题根源
你定义的is_good_car是标量处理函数,仅支持传入长度为1的x。当你直接传入整个列向量时,R会触发向量循环补齐规则抛出警告,且any()会对全列的比较结果统一判断,仅返回单个逻辑值,导致所有行的判断结果异常。rowwise虽然可以强制逐行运算,但运算开销远高于向量化操作,不适合万行级以上的数据集。
方案1:优先使用内置向量化操作(性能最优)
针对你提到的「判断值是否属于指定集合」的场景,R原生的%in%运算符本身就是完全向量化的,底层为C实现,处理百万行级数据也无压力,完全不需要自定义函数和逐行运算:
library(dplyr) mtcars %>% mutate(car = rownames(.)) %>% as_tibble() %>% mutate( good_cars = case_when( car %in% c("Mazda RX4", "Datsun 710", "Valiant") ~ "good", TRUE ~ "rubbish" ) ) %>% select(car, good_cars)
该方案输出与rowwise得到的正确结果完全一致,运算速度是所有方案中最快的。对于你提到的数值区间集合判断,为了避免生成超大向量占用内存,也可以写成向量化的区间判断逻辑:(x >=1 & x <=999) | (x >=3000 & x <=200000) | (x >=250000 & x <=250100),性能比直接生成全量集合向量更高。
方案2:通用标量函数迭代方案
如果你的自定义判断逻辑比较复杂,无法用内置向量化函数改写,可以用purrr::map_lgl逐元素迭代运算,性能远高于rowwise:
library(dplyr) library(purrr) # 原自定义函数无需修改 is_good_car <- function(x){ any( x == c( "Mazda RX4", "Datsun 710", "Valiant" ) ) } mtcars %>% mutate(car = rownames(.)) %>% as_tibble() %>% mutate( good_cars = case_when( map_lgl(car, is_good_car) ~ "good", TRUE ~ "rubbish" ) ) %>% select(car, good_cars)
方案3:函数向量化包装
你也可以直接用Vectorize()将原标量函数包装为向量化函数,之后可以直接传入列向量调用:
library(dplyr) is_good_car <- function(x){ any( x == c( "Mazda RX4", "Datsun 710", "Valiant" ) ) } # 包装为向量化函数 is_good_car_vec <- Vectorize(is_good_car) mtcars %>% mutate(car = rownames(.)) %>% as_tibble() %>% mutate( good_cars = case_when( is_good_car_vec(car) ~ "good", TRUE ~ "rubbish" ) ) %>% select(car, good_cars)
万行级数据集下,方案1的运算速度是rowwise的数百倍,方案2、3的运算速度是rowwise的数十倍,完全可以满足你的性能需求。
内容的提问来源于stack exchange,提问作者Scransom
相关产品推荐
相关产品推荐

