基因组坐标匹配优化:邻近非编码区间查找及脚本提速需求
基因组区间匹配性能优化与功能扩展方案
问题背景
现有两个基因组坐标文件:
- File1:非编码区间,每行包含
uniqID、start(起始坐标)、stop(终止坐标)、chrID(染色体ID)、span(区间长度)字段 - File2:编码区间,字段格式与File1一致
需求:
- 针对File2的每个编码区间,找到同染色体、可容纳该编码区间的最近下方非编码区间,将编码区间按1至两区间长度差的随机值偏移
- 扩展功能:支持查找最近上方的可容纳非编码区间并完成偏移
- 优化现有脚本性能:原脚本因File2含10万行数据,重复读取拆分后的染色体文件导致速度极慢
示例文件
File1(非编码区间)
chr_17_0_3614 0 3614 chr_17 3614 chr_17_16177_17009 16177 17009 chr_17 832 chr_20_0_103982 0 103982 chr_20 103982 chr_20_105978_107383 105978 107383 chr_20 1405 chr_20_107502_108744 107502 108744 chr_20 1242 chr_20_108897_110439 108897 110439 chr_20 1542
File2(编码区间)
chr_17_3614_4570 3614 4570 chr_17 956 chr_17_7901_8585 7901 8585 chr_17 684 chr_17_10351_10698 10351 10698 chr_17 347 chr_20_104882_104971 104882 104971 chr_20 89 chr_20_105763_105978 105763 105978 chr_20 215 chr_20_107383_107502 107383 107502 chr_20 119
现有脚本及性能瓶颈
原脚本通过拆分非编码文件到按染色体命名的子文件,再循环读取每个编码区间并对应读取染色体文件。但10万次循环会启动10万个awk进程,加上重复磁盘IO,导致性能极差。
原脚本代码:
#!/usr/bin/bash # split non-coding into individual chromosome files awk '{print > "split/"$4".txt"}' non_coding.txt # find nearest coords while read -r uniqId start stop chrID span; do awk -v rseed="$RANDOM" -v qStart="$start" -v qStop="$stop" -v uId="$uniqId" -v span="$span" 'BEGIN { belowEnd = 0; belowStart = 0; availSpan = 0; belowUniqId = "none"; srand(rseed) } {if (qStart < $3) {next} else if (qStart >= $3 && span <= $5) {belowEnd = $3; belowStart = $3-span; availSpan = $5; belowUniqId = $1; min=1; max=$5-span; offset=int(min+rand()*(max-min+1))}} END { print "OffsetBelow_"uId, belowStart-offset, belowEnd-offset, $4}' split/"$chrID".txt done < coding.txt
优化方案(单awk进程+内存预加载)
核心思路:仅启动一次awk进程,将所有非编码区间按染色体预加载到内存哈希表,一次性处理所有编码区间,彻底消除重复IO和进程启动开销,同时扩展上方区间查找功能。
优化后脚本:
#!/usr/bin/awk -f # 预加载非编码区间,按染色体分组存储 NR == FNR { # 以染色体ID为一级键,非编码区间stop为二级键,存储区间信息 non_coding[$4][$3] = $2 "," $3 "," $5 "," $1 next } # 处理编码区间 { chr = $4 q_start = $2 q_stop = $3 q_span = $5 q_id = $1 # -------------------------- 查找最近下方可容纳区间 -------------------------- best_below_stop = -1 best_below_data = "" # 遍历当前染色体的非编码区间,筛选符合条件的最近区间 for (stop in non_coding[chr]) { split(non_coding[chr][stop], nc, ",") nc_start = nc[1] nc_stop = nc[2] nc_span = nc[3] if (q_start >= nc_stop && nc_span >= q_span) { if (nc_stop > best_below_stop) { best_below_stop = nc_stop best_below_data = non_coding[chr][stop] } } } # 生成下方偏移结果 if (best_below_stop != -1) { split(best_below_data, nc, ",") max_offset = nc[3] - q_span offset = int(1 + rand() * max_offset) new_start = nc[2] - q_span - offset new_stop = nc[2] - offset print "OffsetBelow_" q_id, new_start, new_stop, chr } else { print "OffsetBelow_" q_id, "NO_MATCH", "NO_MATCH", chr } # -------------------------- 查找最近上方可容纳区间 -------------------------- best_above_start = 1e18 best_above_data = "" for (stop in non_coding[chr]) { split(non_coding[chr][stop], nc, ",") nc_start = nc[1] nc_stop = nc[2] nc_span = nc[3] if (q_stop <= nc_start && nc_span >= q_span) { if (nc_start < best_above_start) { best_above_start = nc_start best_above_data = non_coding[chr][stop] } } } # 生成上方偏移结果 if (best_above_start != 1e18) { split(best_above_data, nc, ",") max_offset = nc[3] - q_span offset = int(1 + rand() * max_offset) new_start = nc[1] + offset new_stop = nc[1] + q_span + offset print "OffsetAbove_" q_id, new_start, new_stop, chr } else { print "OffsetAbove_" q_id, "NO_MATCH", "NO_MATCH", chr } }
使用说明
- 确保两个输入文件的字段顺序严格为
uniqID、start、stop、chrID、span - 运行脚本:
awk -f optimize_script.awk non_coding.txt coding.txt - 输出会同时包含下方和上方的偏移结果,无匹配区间时标记为
NO_MATCH
性能提升核心点
- 单进程处理:避免10万次awk进程启动的开销
- 内存预加载:仅读取一次非编码文件,所有查询在内存中完成,彻底消除重复磁盘IO
- 无中间文件:无需拆分非编码文件到磁盘,节省磁盘空间和IO操作
内容的提问来源于stack exchange,提问作者Dja Cha
相关产品推荐
相关产品推荐

