如何根据从第二个文件中获取的值范围排除文件中的行



我有一个包含值范围列表的文件:

2    4
6    9
13   14

第二个文件如下:

HiC_scaffold_1  1   26
HiC_scaffold_1  2   27
HiC_scaffold_1  3   27
HiC_scaffold_1  4   31
HiC_scaffold_1  5   34
HiC_scaffold_1  6   35
HiC_scaffold_1  7   37
HiC_scaffold_1  8   37
HiC_scaffold_1  9   38
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  13  39
HiC_scaffold_1  14  39
HiC_scaffold_1  15  42

并且我希望从文件2中排除列2的值落在文件1定义的范围内的行。理想的输出是:

HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

我知道如何用awk提取单个范围:

awk '$2 == "2", $2 == "4"' file2.txt

但是我的文件1有很多范围值(行(,我需要排除而不是提取与这些值对应的行。

这是一个错误:

$ awk '
NR==FNR {                           # first file
    min[NR]=$1                      # store mins and maxes in pairs
    max[NR]=$2
    next
}
{                                   # second file
    for(i in min)                   
        if($2>=min[i]&&$2<=max[i])
            next
}1' ranges data

输出:

HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

如果范围不是很大的整数值,但数据很大,你可以制作一个值的排除图来加快比较:

$ awk '
NR==FNR {                       # ranges file
    for(i=$1;i<=$2;ex[i++]);    # each value in the range goes to exclude hash
    next
}
!($2 in ex)' ranges data        # print if not found in ex hash

如果您的范围不是很大:

$ cat tst.awk
NR==FNR {
    for (i=$1; i<=$2; i++) {
        bad[i]
    }
    next
}
!($2 in bad)
$ awk -f tst.awk file1 file2
HiC_scaffold_1  1   26
HiC_scaffold_1  5   34
HiC_scaffold_1  10  39
HiC_scaffold_1  11  39
HiC_scaffold_1  12  39
HiC_scaffold_1  15  42

seception

如果file2.txt的第二列总是等于其行的索引,则可以使用sed修剪行。如果您的情况并非如此,请参阅awkception一段。

sed $(sed 's/^([0-9]*)[[:space:]]*([0-9]*)/-e 1,2d/' file1.txt) file2.txt

其中file1.txt包含您的范围,file2.txt是数据本身。

基本上,它构造了一个sed调用,该调用链接-e i,jd表达式的列表,这意味着它将删除第i行和第j行之间的行。

在您的示例中,sed 's/^([0-9]*)[[:space:]]*([0-9]*)/-e 1,2d/' file1.txt将输出-e 2,4d -e 6,9d -e 13,14d,这是用于在file2.txt上调用sed的表达式列表。

最后它会调用:

sed -e 2,4d -e 6,9d -e 13,14d file2.txt

此命令删除第2行和第4行之间的所有行,第6行和第9行之间的全部行,以及第13行和第14行之间的全体行。

显然,如果file2.txt的第二列与其自己行的索引不匹配,则它不起作用。

awkception

awk "{$(awk '{printf "if ($2>=%d && $2<=%d) nextn", $1, $2}' file1.txt)}1" file2.txt

即使第二列与其行的索引不匹配,此解决方案仍然有效。

该方法使用awk创建awk程序,就像sedseception解决方案中创建sed表达式一样。

最后,这将调用:

awk '{
if ($2>=2 && $2<=4) next
if ($2>=6 && $2<=9) next
if ($2>=13 && $2<=14) next
}1' file2.txt

应该注意的是,该解决方案明显慢于sed

最新更新