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

时间:2016-07-20 13:00:35

标签: linux bash unix 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分钟。

2 个答案:

答案 0 :(得分:2)

此解决方案需要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];
            }
        }
    }
}

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

答案 1 :(得分:0)

$ 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

vs你在答案结尾处提供的脚本+管道:

$ 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

虽然我的脚本仅在!seen[(idI<idJ ? idI FS idJ : idJ FS idI)]++礼貌地报告了它们。