awk来自两个文件的数字范围

时间:2016-12-14 05:02:34

标签: bash awk

我想从file1.txt中提取范围与file2.txt匹配的数据。

$ cat file1.txt
gene  position     type
DDX   0            A
DDX   1            B
DDX   2            C
DDX   3            D
DDX   4            E
DDX   5            F
ABC   0            A
ABC   1            B
ABC   2            C
ABC   3            D
ABC   4            E  
ABC   5            F

$ cat file2.txt
gene    start_position    end_position
DDX     2                 4
ABC     1                 2

预期产出:

gene  position     type
DDX   2            C
DDX   3            D
DDX   4            E
ABC   1            B
ABC   2            C

因此,在file1.txt中,我希望从DDX23以及所有4获取所有ABC职位12

我不太确定如何将其与file2.txt匹配。

我只知道使用awk的手动方式。例如,

awk -F '\t' '$1=="DDX" && $2>=2 && $1<=4' file1.txt

我有一个巨大的列表可以匹配file1.txtfile2.txt

6 个答案:

答案 0 :(得分:3)

  

我有一个巨大的列表来匹配file1.txt和file2.txt

在这种情况下,从file2构建一个awk脚本(使用awk),然后处理file1。

如你所述,你需要的是一系列陈述,如:

 $1=="DDX" && 2 <= $2 && $3 <= 4

例如,将此输出传递给awk:

$ awk 'NR > 1 { \
    printf( "$1==\"%s\" && %d <= $2 && $2 <= %d {print; next;}\n", $1, $2, $3 ) \
  }' file2.txt
$1=="DDX" && 2 <= $2 && $2 <= 4 {print; next;}
$1=="ABC" && 1 <= $2 && $2 <= 2 {print; next;}

处理file2一次,file1处理一次,生成的脚本一找到匹配就移动到下一个输入行。没有排序,我怀疑你会跑得快得多。

顺便说一句,我重新安排了你的不平等,使a < b && b < c形式模仿数学,a < b < c。如果采用该形式,您可能会发现避免错误,因为它将边界放在边缘。

答案 1 :(得分:1)

以下DDX逻辑应该适合您。一个更通用的逻辑,而不是比较实际的字符串,可以扩展到更多类型,然后只是ABCawk 'BEGIN{delete start; delete stop; printf "gene position type\n"} \ FNR==NR && NR > 1 {start[$1]=$2; stop[$1]=$3; next}(($1 in start) && (($2 >= start[$1]) && ($2 <= stop[$1]))){print}' file2.txt file1.txt gene position type DDX 2 C DDX 3 D DDX 4 E ABC 1 B ABC 2 C

awk

逻辑是构建一个表格,即startstopfile2.txt中的数组,其中包含来自FNR==NR && NR > 1 {start[$1]=$2; stop[$1]=$3; next}的每种基因类型的起始和结束范围。

file2.txt部分从($1 in start) && (($2 >= start[$1]) && ($2 <= stop[$1]))跳过标题,并为每个基因类型构建具有开始和停止范围的表。

file1.txt上的git branch -v用于解析其上的数组内容,其中存在基因类型且开始和结束范围在允许的限制范围内。

答案 2 :(得分:1)

使用GNU AWK&#39; arrays of arrays

awk -vi=0 '
$1 == "gene" { if (++i == 2) print; next }
i == 1       { g[$1][0] = $2; g[$1][1] = $3 }
i == 2       { if ($2 >= g[$1][0] && $2 <= g[$1][1]) print }
' file2.txt file1.txt

i变量表示读取的标题数:

  • i = 0:目前尚未读取任何标题,
  • i = 1:读取了file2.txt的标题;
  • i = 2:读取file1.txt的标题。

假设如果第一个字段等于"gene",那么记录就是标题。您可能需要调整此条件。

对于第一个输入文件(file2.txt),脚本会将范围的值收集到多维数组g中,其中第一个键引用第一个字段({{ 1}}),第二个键指的是较低的(gene)或较高的(0)限制。

对于第二个输入文件(1),脚本会检查第二个字段是否与当前file1.txt的范围匹配,并打印记录(如果匹配)。

对于非GNU AWK,您可以通过将gene替换为g[$1][0]来模拟多维数组,将g[$1,0]替换为g[$1][1]。在这种情况下,键与g[$1,1]内部变量连接(顺便说一句,您可以将其覆盖为任何其他AWK变量)。

我注意到您正在使用字段分隔符的选项卡。但问题中的示例内容不包含选项卡。所以我跳过设置SUBSEP

答案 3 :(得分:1)

您可以使用多维度数组来存储file2的最大和最小范围,以使用file1来过滤awk one-liner的结果,如下所示:

awk '(NR==FNR){if(FNR>1){f[$1];p[$1,"sp"]=$2;p[$1,"ep"]=$3};next}(FNR == 1 || ($1 in f && $2 >= p[$1,"sp"] && $2 <= p[$1,"ep"]))' file2.txt file1.txt
gene  position     type
DDX   2            C
DDX   3            D
DDX   4            E
ABC   1            B
ABC   2            C

答案 4 :(得分:1)

根据您对 huge 的定义,这也可能就足够了:

$ awk 'FNR==NR { lo[$1]=$2; hi[$1]=$3; next }           # store low and hi values
       FNR==1 || ($1 in lo) && $2>=lo[$1] && $2<=hi[$1] # print if between
  ' file2 file1
gene  position     type
DDX   2            C
DDX   3            D
DDX   4            E
ABC   1            B
ABC   2            C

此解决方案希望关键字中的范围是连续的,而不是:

gene    start_position    end_position
ABC     1                 2
ABC     4                 5

此解决方案无法容忍范围内的差距。

答案 5 :(得分:1)

awk 'FNR == NR  { m[$1] = $2; M[$1] = $3; next }
     FNR == 1 || $2>=m[$1] && $2<=M[$1] 
    ' file2.txt file1.txt

注意

  • FNR == NR在读取第一个文件时,NR计算所有行,其中FNR只是当前文件中的一个
  • m[$1] = $2; M[$1] = $3记住每个标签(条目/索引)的2个数组的限制
  • next对待下一行
  • FNR == 1 || $2>=m[$1] && $2<=M[$1]如果第1行(由于前一个next而产生的第二个文件)或位置($ 2)在相应标签的限制m和M之间($ 1)
  • [print]测试模式的默认操作是打印整行(打印$ 0)

ps:在发帖后发现我的解决方案几乎与@jamesbrown相同,抱歉