如何在R语言中避免多次使用OR逻辑?优化多列筛选代码
问题描述
数据框
| patient_id | procedure_1 | procedure_2 | procedure 3 |
|---|---|---|---|
| 1 | A | . | . |
| 2 | A | B | . |
| 3 | . | . | . |
| 4 | . | . | . |
| 5 | C | . | B |
| 6 | D | . | . |
| 7 | A | . | . |
| 8 | B | A | . |
| 9 | C | . | . |
| 10 | D | A | . |
需求
筛选出接受过手术A或B的患者(不考虑顺序或是否同时接受两种手术)。
现有代码及痛点
现有代码可正常运行,但实际场景需处理50个手术变量(procedure_1至procedure_50)、10种目标手术(A至J)、700万条观测数据,原代码逐个变量写判断的方式扩展性差且效率不足:
library(dplyr) procedure_list <- list("A","B") dataframe <- dataframe %>% mutate(procedure_test = case_when(procedure_1 %in% procedure_list| procedure_2 %in% procedure_list| procedure_3 %in% procedure_list ~ 1, TRUE ~ 0) %>% subset(procedure_test==1)
优化方案
方案1:dplyr高效写法(简洁且低内存)
利用dplyr的if_any函数(需dplyr 1.0.0+版本)批量处理所有手术变量,直接在filter中完成筛选,避免生成中间列:
library(dplyr) # 目标手术用向量存储,比列表更高效 target_procedures <- c("A", "B") # 自动匹配所有procedure_开头的变量,判断每行是否有符合条件的手术 filtered_df <- dataframe %>% filter(if_any(starts_with("procedure_"), ~ .x %in% target_procedures))
如果版本较低,可改用across+rowSums的组合:
filtered_df <- dataframe %>% filter(across(starts_with("procedure_"), ~ .x %in% target_procedures) %>% rowSums() > 0)
方案2:data.table(超大数据集首选)
针对700万条数据,data.table的内存效率和运行速度远优于基础dplyr,推荐使用:
library(data.table) # 转换为data.table格式 setDT(dataframe) # 匹配所有手术变量列 procedure_cols <- grep("^procedure_", names(dataframe), value = TRUE) target_procedures <- c("A", "B") # 用Reduce累积逻辑或运算,直接生成筛选条件 filtered_dt <- dataframe[Reduce(`|`, lapply(procedure_cols, function(col) dataframe[[col]] %in% target_procedures))]
额外优化建议
- 将手术变量转为因子:由于手术类型仅A-J共10种,转成因子可大幅减少内存占用,加快匹配速度:
dataframe <- dataframe %>% mutate(across(starts_with("procedure_"), factor, levels = LETTERS[1:10])) - 避免修改原数据:尽量生成新的筛选后数据框,防止原数据被意外覆盖
- 按需加载包:如果仅用
data.table处理,无需加载dplyr,减少环境负担
内容的提问来源于stack exchange,提问作者Darla Carvallo
相关产品推荐
相关产品推荐

