【问题标题】:awk; getting multiple lines from two files when they share a common header哇;当两个文件共享一个公共标题时从两个文件中获取多行
【发布时间】:2020-12-03 00:01:10
【问题描述】:

我有一个问题与有关该主题的许多其他问题非常相似,但我无法将这些解决方案扩展到我正在寻找的确切输出。

我有两个格式为 fastq 样式的文件,如下所示:

file1.txt

@header:with:id:number:0001 1:this:number:indicates:pair:number
ABCD
+
1324
@header:with:id:number:0001 2:this:number:indicates:pair:number
EFGH
+
5678
@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0003 1:this:number:indicates:pair:number
IJKL
+
3456
@header:with:id:number:0003 2:this:number:indicates:pair:number
MNOP
+
7890

file2.txt

@header:with:id:number:0004 1:this:number:indicates:pair:number
QRST
+
1324
@header:with:id:number:0004 2:this:number:indicates:pair:number
UVWX
+
5678
@header:with:id:number:0005 1:this:number:indicates:pair:number
CDEF
+
3456
@header:with:id:number:0005 2:this:number:indicates:pair:number
GHIJ
+
7890
@header:with:id:number:0002 1:this:number:indicates:pair:number
YZAB
+
9012

每个“块”有四行,其中第一行(标题)始终以 @ 开头,并包括一个 id 编号(例如 0001)和一个索引(即在“空格”之后的 1 或 2)。 每个 id 编号应该在具有两个索引的同一个文件中出现两次(就像上面示例中除 0002 之外的所有 id 编号一样)。 现在我想分别存储 id-number 出现在两个文件中的块(表示每个文件中只出现一次的块)。

在这种情况下,输出应该是:

@header:with:id:number:0002 1:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0002 2:this:number:indicates:pair:number
YZAB
+
9012

并且这些行应该从原始文件中删除。

为此,到目前为止,我已将 awk 与以下命令一起使用

awk -F" " '/^@/ && NR==FNR {lines[$1]; next}
    $1 in lines {x=NR+3}
    (NR<=x) {print $0}' file2.txt file1.txt

这个输出:

@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012

到一半。

我的问题是,如何在 两个 文件中出现的标头中搜索 id 编号,将它们存储在第三个文件中并从两个原始文件中删除相应的块?

【问题讨论】:

  • 这是fastq 格式吗?如果您明确指出这种格式,您就更有可能接触到使用这种格式的人。
  • 听起来真的很想打印并删除 ID 仅在文件中出现一次的块 - 这比比较两个文件中的 ID 以找出两个文件中出现的 ID 更简单如果这将始终是相同的 ID,在一个文件中只出现一次。
  • @EdMorton 理想情况下,一个 id 号在同一个文件中出现两次,一次是索引 1,一次是索引 2。但是如果其中一个索引丢失,则预计它会出现在另一个文件中因此,id-numbers 出现在两个文件中。 (这是由于下游处理步骤中的一些不确定性,这些块应该被区别对待,因此我想将它们分开)。希望这能澄清你的问题。
  • @tripleee 是的,这确实是 fastq 格式。感谢您的提示,我会更新问题以明确提及这一点。
  • @MostlyHarmless 是的,这就是您在问题中所描述的。我的观点是,找到一个在文件中出现一次的 ID 并从该文件中删除相关记录比找到两个文件中都出现的 ID 然后从每个文件中删除记录更容易,那么为什么不改为你的要求呢?

标签: awk fastq


【解决方案1】:

使用 GNU awk:

awk 'BEGIN { 
             RS="@header" # Set the input record separator
           } 
   FNR==NR { # process the first file
             ORS="@header"; # Set the output record separator
             split($0,map,":"); # Split the record into array map using ":" as the delimiter
             map1[substr(map[5],1,4)]=$0 # map[5] will be e.g 0002 2. We only want 0002 and so use substr to create an index for array map1 with the record as the value
           } 
   NR!=FNR { # process the second file
             ORS="@header";
             split($0,map,":");
             id=substr(map[5],1,4); # id e.g. 0002
             if (id in map1) { 
                               print $0; # If id in map1 array print this record
                               print map1[id] # if id in map1 array print array value
             } 
            }' file1.txt file2.txt

一个班轮:

awk 'BEGIN { RS="@header" } FNR==NR { ORS="@header";split($0,map,":");map1[substr(map[5],1,4)]=$0 } NR!=FNR { ORS="@header";split($0,map,":");id=substr(map[5],1,4);if (id in map1) { print $0;print map1[id] } }' file1.txt file2.txt

【讨论】:

  • 感谢您的努力。我对此进行了测试,它给了我预期的答案。我是否正确理解 map[5],1,4 在用 ':' 分隔后得到第 5 个元素,然后 1,4 得到第 5 个元素的前 4 位数字?在这种情况下,我如何才能将其扩展到寻找 4 位数字来寻找任意数量的数字?
  • 是的 substr 1,4 从第一个到第四个字符
  • 你可以使用 match(map[5],/^[[:digit:]]+/);substr(map[5],RSTART,RLENGTH)
  • 确实有效,虽然我不得不删除';'在 substr 参数之前。
【解决方案2】:

您能否尝试使用所示示例进行以下、编写和测试,我相信它应该可以在任何awk 中工作,但只能在 GNU awk 中测试它。

awk '
FNR==NR{
  if($0~/^@/){
    match($0,/^@header:with:id:number:[0-9]{4}/)
    mat1=substr($0,RSTART,RLENGTH)
    arr1[mat1]++
  }
    val1[mat1]=(val1[mat1]?val1[mat1] ORS:"")$0
    next
}
{
  if($0~/^@/){
    match($0,/^@header:with:id:number:[0-9]{4}/)
    mat2=substr($0,RSTART,RLENGTH)
    arr2[mat2]++
  }
  val2[mat2]=(val2[mat2]?val2[mat2] ORS:"")$0
}
END{
  for(key1 in arr1){
    if(arr1[key1]==1 && arr2[key1]==1){print val1[key1] ORS val2[key1] }
  }
}' file1.txt file2.txt 

这将在两个强制文件中查找匹配索引的计数为1,以防您希望在任一文件中有1 计数,然后在上述条件下将arr1[key1]==1 &amp;&amp; arr2[key1]==1 更改为arr1[key2]==1

输出将如下所示。

@header:with:id:number:0002 2:this:number:indicates:pair:number
PQRS
+
9012
@header:with:id:number:0002 1:this:number:indicates:pair:number
YZAB
+
9012

【讨论】:

    【解决方案3】:

    您可以使用此gnu awk 打印每个文件仅出现一次的所有标题:

    awk -v ORS= -v RS='@header:' -F '[:[:blank:]]+' 'NF {
       if ($4 in seen)
          delete seen[$4]
       else
          seen[$4] = prt $0
    }
    ENDFILE {
       for (i in seen)
          print seen[i]
       delete seen
    }
    {prt = RT}' file1 file2
    
    
    @header:with:id:number:0002 2:this:number:indicates:pair:number
    PQRS
    +
    9012
    @header:with:id:number:0002 1:this:number:indicates:pair:number
    YZAB
    +
    9012
    

    【讨论】:

    • 感谢您的回答!它工作得很好,但据我了解,它并没有明确查找两个文件中是否出现标头,而是获取同一文件中没有一对的所有标头。这就是我要找的东西,如果我的问题不清楚,我很抱歉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-12
    相关资源
    最近更新 更多