使用awk打印两列之间值范围重叠的记录对



我有不同的记录,对应于开始($6)和停止($7)的范围。我想做的是打印出所有范围重叠的记录对。

例如,我的数据如下:

id1 0   376 . scaffold1 5165761 5166916 
id2 0   366 . scaffold1 2297244 2298403 
id3 155 456 . scaffold1 692777  693770 
id4 185 403 . scaffold1 102245  729675

我想要的是像这样的结果

id3 id4

因为id4的范围与id3重叠。我一直在互联网上寻找解决方案,但似乎没有什么能解决我的问题。

如果有人能给我一些建议,我将不胜感激。


在遵循了下面回复中一些人的建议后,我确实尝试了这个代码,它确实有效!

awk '{start[$1]=$6;stop[$1]=$7;} END {for(i in start) {for(j in stop) {if(start[i] >= start[j] && start[i] <= stop[j]) print i,j}}}' file | awk '{if($1!=$2) print}' -

处理时间很短。。。对于一个有1400条记录的文件,它甚至不到1分钟就完成了。

此解决方案需要GNU awk:

{
    start = $6 * 10 + 5;
    stop = $7 * 10;
    data[start] = data[start] " " $1;
    data[stop] = data[stop] " " $1;
}
END {
    PROCINFO["sorted_in"] = "@ind_num_asc";
    for (d in data) {
        count = split(data[d], fields);
        for (i in fields) {
            id = fields[i];
            if (d % 10 == 5) { # start
                for (s in started) {
                    print s, id;
                }
                started[id] = 1;
            } else { # stop
                delete started[id];
            }
        }
    }
}

其基本思想是:将开始和停止标记(我称之为索引,可能是一个糟糕的选择)放在一个数组中,并根据其索引对该数组进行排序。然后,遍历数组。如果遇到"开始"标记,请将其放入另一个数组(称为"开始")。如果遇到"停止"标记,请将其从该数组中移除。现在,如果遇到"开始"标记,则该间隔与数组中当前"开始"的所有间隔重叠,因此打印出匹配项。通过确保"停止"标记位于具有相同原始索引的"开始"标记之前,可以消除角点情况。

$ cat tst.awk
{
    beg[$1] = $6
    end[$1] = $7
    ids[++numIds] = $1
}
END {
    for (i=1; i<=numIds; i++) {
        idI = ids[i]
        for (j=1; j<=numIds; j++) {
            idJ = ids[j]
            if (idI != idJ) {
                if ( ( (beg[idI] >= beg[idJ]) && (beg[idI] <= end[idJ]) ) ||
                     ( (end[idI] >= beg[idJ]) && (end[idI] <= end[idJ]) ) ) {
                    if ( !seen[(idI<idJ ? idI FS idJ : idJ FS idI)]++ ) {
                        print idI, idJ
                    }
                }
            }
        }
    }
}
$ awk -f tst.awk file
id3 id4

你在问题中提供的输入文件并没有涵盖很多情况,所以考虑到这个输入文件中有很多重叠的变体:

$ cat file
id1 185 403 . scaffold1 10  20
id2 185 403 . scaffold1 11  19
id3 185 403 . scaffold1  9  10
id4 185 403 . scaffold1 20  21
id5 185 403 . scaffold1  9  11
id6 185 403 . scaffold1 19  21
id7 185 403 . scaffold1 10  20
id8 185 403 . scaffold1  1   8

试试上面的:

$ awk -f tst.awk file
id1 id3
id1 id4
id1 id5
id1 id6
id1 id7
id2 id1
id2 id5
id2 id6
id2 id7
id3 id5
id3 id7
id4 id6
id4 id7
id5 id7
id6 id7

与你在答案末尾提供的脚本+管道:

$ awk '{start[$1]=$6;stop[$1]=$7;} END {for(i in start) {for(j in stop) {if(start[i] >= start[j] && start[i] <= stop[j]) print i,j}}}' file | awk '{if($1!=$2) print}' -
id3 id5
id4 id6
id4 id7
id4 id1
id5 id3
id6 id7
id6 id1
id6 id2
id7 id3
id7 id5
id7 id1
id1 id3
id1 id5
id1 id7
id2 id5
id2 id7
id2 id1

注意,您的脚本会两次报告某些(但不是全部)id之间的重叠:

id1 id7
id7 id1
id3 id5
id5 id3

而我的脚本由于CCD_ 3只报告了一次。

最新更新