Pandas按范围合并区间:合并同染色体重叠/间接重叠区间并累加概率
合并Pandas中同一染色体的连通区间并求和概率
这是基因组数据分析里超常见的需求嘛,我来给你一步步拆解怎么高效实现:
首先,咱们先明确核心需求:同一染色体下,所有重叠或通过其他区间间接连通的区间要合并成一个大区间,同时把这些区间的probability求和。
第一步:构造示例数据
先拿一个贴近需求的示例DataFrame来演示,其中chr1的三个区间是间接连通的(10-20和18-28重叠,18-28又和25-35重叠,所以三个要合并成一个),chr2的两个区间直接重叠:
import pandas as pd df = pd.DataFrame({ 'chrom': ['chr1', 'chr1', 'chr1', 'chr2', 'chr2'], 'start': [10, 18, 25, 5, 15], 'end': [20, 28, 35, 10, 20], 'probability': [0.2, 0.3, 0.1, 0.4, 0.2] })
第二步:定义区间合并函数
我们需要一个函数来处理单个染色体的区间,核心逻辑是先排序,再迭代合并连通区间,同时累加概率:
def merge_connected_intervals(group): # 先按start排序,确保区间按起始位置有序排列,这是合并连通区间的基础 sorted_group = group.sort_values('start').reset_index(drop=True) if sorted_group.empty: return pd.DataFrame() # 初始化合并后的第一个区间 merged_intervals = [] current_start = sorted_group.iloc[0]['start'] current_end = sorted_group.iloc[0]['end'] current_prob = sorted_group.iloc[0]['probability'] # 遍历后续每个区间 for idx in range(1, len(sorted_group)): row = sorted_group.iloc[idx] # 判断当前区间是否和已合并的区间连通(这里默认是闭区间,若为左闭右开则改成row['start'] < current_end) if row['start'] <= current_end: # 合并区间:更新end为两者的最大值,累加概率 current_end = max(current_end, row['end']) current_prob += row['probability'] else: # 无连通,将已合并的区间存入列表,初始化新的区间 merged_intervals.append({ 'chrom': sorted_group.iloc[0]['chrom'], 'start': current_start, 'end': current_end, 'probability': current_prob }) current_start = row['start'] current_end = row['end'] current_prob = row['probability'] # 把最后一个合并的区间加入列表 merged_intervals.append({ 'chrom': sorted_group.iloc[0]['chrom'], 'start': current_start, 'end': current_end, 'probability': current_prob }) return pd.DataFrame(merged_intervals)
第三步:分组应用合并函数
按chrom分组,把刚才的函数应用到每个组,最后合并结果:
final_merged_df = df.groupby('chrom').apply(merge_connected_intervals).reset_index(drop=True)
查看结果
运行后final_merged_df的输出如下:
| chrom | start | end | probability |
|---|---|---|---|
| chr1 | 10 | 35 | 0.6 |
| chr2 | 5 | 20 | 0.6 |
完全符合需求:chr1的三个连通区间合并成了10-35,概率求和为0.6;chr2的两个重叠区间合并成5-20,概率求和为0.6。
注意事项
- 区间类型适配:如果你的区间是左闭右开(比如基因组常用的0-based坐标),记得把判断条件从
row['start'] <= current_end改成row['start'] < current_end,避免错误合并不重叠的区间。 - 缺失值处理:如果你的数据里有
NaN值,建议先通过df.dropna(subset=['chrom', 'start', 'end', 'probability'])清理,避免函数出错。 - 性能:这个方法的时间复杂度主要来自排序(O(n log n)),对于百万级别的区间数据也能高效处理。
内容的提问来源于stack exchange,提问作者Praderas
相关产品推荐
相关产品推荐

