DNA坐标非重叠区间提取咨询:同染色体外显子数据处理
提取DNA非重叠区间解决方案
问题描述
需要处理基因组区间数据,针对同一染色体、同一外显子的多行记录,提取外显子区间中完全不与任何start1-end1区间重叠的区域,最终生成non_overlap列(理想状态可拆分为non_overlap_start和non_overlap_end列)。
初始数据示例
import pandas as pd df = pd.DataFrame({ 'chrom': [1,1,1,2], 'exon_start': [1,1,150,5], 'exon_end': [100,100,155,50], 'start1': [50,70,155,25], 'end1': [60,80,160,100] })
解决方案步骤
核心思路是按染色体+外显子分组,合并组内所有重叠的start1-end1区间,再计算外显子区间与合并后区间的差集:
- 定义区间处理函数:负责截断超出外显子的区间、合并重叠区间、计算非重叠区域
- 分组应用函数:对每个染色体-外显子组计算非重叠区域
- 可选:拆分区间列:将
non_overlap的字符串列表拆分为起始/终止列
代码实现
def get_non_overlap(exon_start, exon_end, intervals): # 截断每个start1-end1区间到外显子范围内 trimmed_intervals = [] for s, e in intervals: trim_s = max(s, exon_start) trim_e = min(e, exon_end) if trim_s <= trim_e: trimmed_intervals.append((trim_s, trim_e)) # 合并重叠/相邻的区间 if not trimmed_intervals: return [(exon_start, exon_end)] # 按起始坐标排序 sorted_intervals = sorted(trimmed_intervals, key=lambda x: x[0]) merged = [list(sorted_intervals[0])] for s, e in sorted_intervals[1:]: last_s, last_e = merged[-1] if s <= last_e + 1: # 相邻区间也合并 merged[-1][1] = max(last_e, e) else: merged.append([s, e]) # 计算外显子与合并后区间的差集 non_overlap_regions = [] prev_end = exon_start for s, e in merged: if prev_end < s: non_overlap_regions.append((prev_end, s - 1)) prev_end = max(prev_end, e) if prev_end < exon_end: non_overlap_regions.append((prev_end + 1, exon_end)) # 格式化为字符串格式 return [f"{a}-{b}" for a, b in non_overlap_regions] # 按染色体、外显子起始/终止分组,收集每组的所有start1-end1区间 grouped_intervals = df.groupby(['chrom', 'exon_start', 'exon_end'])[['start1', 'end1']].apply( lambda x: list(zip(x['start1'], x['end1'])) ).rename('intervals') # 合并分组结果到原DataFrame df = df.merge(grouped_intervals, on=['chrom', 'exon_start', 'exon_end']) # 计算non_overlap列 df['non_overlap'] = df.apply( lambda row: get_non_overlap(row['exon_start'], row['exon_end'], row['intervals']), axis=1 ) # 可选:拆分non_overlap为起始/终止列(需展开多行) df_exploded = df.explode('non_overlap') df_exploded[['non_overlap_start', 'non_overlap_end']] = df_exploded['non_overlap'].str.split('-', expand=True).astype(int) # 清理临时列 df = df.drop('intervals', axis=1)
最终结果
处理后的DataFrame:
chrom exon_start exon_end start1 end1 non_overlap 0 1 1 100 50 60 [1-49, 61-69, 81-100] 1 1 1 100 70 80 [1-49, 61-69, 81-100] 2 1 150 155 155 160 [150-154] 3 2 5 50 25 100 [5-24]
关键说明
- 分组逻辑:必须以
chrom+exon_start+exon_end作为分组键,确保只处理同一外显子下的所有目标区间 - 区间截断:避免
start1-end1超出外显子范围的部分干扰非重叠区域计算 - 区间合并:减少差集计算的复杂度,确保只处理连续的覆盖区域
内容的提问来源于stack exchange,提问作者youtube
相关产品推荐
相关产品推荐

