如何使用dplyr基于另一个DataFrame的数值区间过滤行?
用dplyr基于另一DataFrame的区间条件过滤数据
问题背景
我有两个DataFrame,需要用第二个的start/end区间规则过滤第一个:
待过滤数据集 singletons_bovis
| gene_id | start | end |
|---|---|---|
| WB0001 | 54 | 1500 |
| WB0002 | 5410 | 15246 |
过滤规则数据集 singletons_elegans
| transcript_id | start | end |
|---|---|---|
| C55C2.5d | 2547 | 54826 |
| F12B6.1a.1 | 0 | 458 |
需求:保留singletons_bovis中同时满足以下两个条件的行:
- 该行的
start落在singletons_elegans任意一行的start±250区间内 - 该行的
end落在singletons_elegans任意一行的end±250区间内
我试了这段代码但跑不通:
df <- singletons_bovis %>% filter((start < singletons_elegans$start+250 & start > singletons_elegans$start-250) & (end > singletons_elegans$end-250 & end < singletons_elegans$end+250))
为啥之前的代码不行
你这段代码的问题有两个:
- 逻辑匹配错了:你是把两个DataFrame的行按位置一一对应比较,但需求是检查每个
bovis的行是否匹配elegans里的任意一行,而非同位置的行。 - 向量长度不兼容:如果两个DataFrame行数不一样,R会自动循环补齐短向量,导致判断结果完全不符合预期。
正确的dplyr实现方法
下面给你三种可行的写法,按需选择:
方法1:笛卡尔积筛选(直观易懂)
先把两个数据集的所有行组合配对,然后筛选符合条件的组合,最后去重保留原bovis的行:
library(dplyr) filtered_df <- singletons_bovis %>% # 生成所有行的组合,x代表bovis的列,y代表elegans的列 cross_join(singletons_elegans, suffix = c(".x", ".y")) %>% # 同时满足start和end的区间条件 filter( start.x >= start.y - 250 & start.x <= start.y + 250, end.x >= end.y - 250 & end.x <= end.y + 250 ) %>% # 只保留原bovis的列,去重避免重复行 select(gene_id, start = start.x, end = end.x) %>% distinct()
方法2:逐行检查(高效小数据集)
用rowwise()逐行处理bovis,对每一行检查是否存在elegans里的任意一行满足条件:
filtered_df <- singletons_bovis %>% rowwise() %>% filter( # 检查当前行start是否在任意elegans的start±250区间 any(start >= singletons_elegans$start - 250 & start <= singletons_elegans$start + 250), # 检查当前行end是否在任意elegans的end±250区间 any(end >= singletons_elegans$end - 250 & end <= singletons_elegans$end + 250) ) %>% # 取消逐行处理模式 ungroup()
方法3:purrr映射(适合大数据集)
结合purrr的映射函数,逐行判断条件,效率比rowwise()更高:
library(dplyr) library(purrr) filtered_df <- singletons_bovis %>% filter( # 对每个start值,检查是否匹配任意elegans的start区间 map_lgl(start, ~ any(.x >= singletons_elegans$start - 250 & .x <= singletons_elegans$start + 250)), # 对每个end值,检查是否匹配任意elegans的end区间 map_lgl(end, ~ any(.x >= singletons_elegans$end - 250 & .x <= singletons_elegans$end + 250)) )
示例数据验证
针对你给出的示例数据:
- WB0001的start=54落在F12B6.1a.1的start±250区间(-250到250),但end=1500不在任何elegans行的end±250区间(F12B6.1a.1是208-708,C55C2.5d是54576-55076),会被过滤。
- WB0002的start和end都不满足条件,所以最终结果为空。
内容的提问来源于stack exchange,提问作者Lilly
相关产品推荐
相关产品推荐

