【问题标题】:awk to search field in file2 using range of file1awk 使用 file1 的范围搜索 file2 中的字段
【发布时间】:2016-12-22 15:26:58
【问题描述】:

我正在尝试使用awk 来查找file2 中的所有$2 值,即~30MB,它们在file1 中的$2$3 之间,即~2GB。如果$2file2 中的值介于file1 字段之间,则它与file1 中的$6 值一起打印。 file1file2 都是 tab-delimited 以及 desired output。如果没有要打印的内容,则处理下一行。下面的awk 运行但非常慢(已处理约 1 天但仍未完成)。有没有更好的方法来解决这个问题或更好的编程语言?

file2$1$2$3file1$1$2必须匹配file1$1并且在file1的范围内987654348@ 的file1

所以为了在输出中打印该行,它必须匹配 $1 并且在 $2$3 的范围内 file2

因此,由于来自 file2 的行在 file1$2$3 范围内匹配 $1,因此它被打印在输出中。 谢谢你:)。

file1 (~3MB)

 1  948953  948956  chr1:948953-948956  .   ISG15
 1  949363  949858  chr1:949363-949858  .   ISG15
 2  800000  900500  chr1:800000-900500  .   AGRN

file2 (~80MB)

 1  12214   .   C   G
 1  949800  .   T   G
 2  900000  rs123   -   A
 3  900000  .   C   -

想要的输出 tab-delimited

1   949800  .   T   G     ISG15
2   900000  rs123   -   A   AGRN

awk

 awk -F'\t' -v OFS='\t' '                   
NR == FNR {min[NR]=$2; max[NR]=$3; Gene[NR]=$NF; next}
 {                
     for (id in min) 
         if (min[id] < $2 && $2 < max[id]) {
             print $0, id, Gene[id]
             break              
         }
}                                     
' file1 file2

【问题讨论】:

  • 我编辑了这篇文章,希望对您有所帮助。谢谢你:)。
  • 2 900000 rs123 - A2 900000 rs123 - A AGRN 在所需的输出中吗?
  • 抱歉,这是正确的:2 900000 rs123 - A AGRN。在帖子中也进行了更改。谢谢你:)。
  • 如果您应该测试部分或全部这些解决方案,我有兴趣了解它们的执行时间吗?
  • 我会测试它们并在有机会时报告执行时间(年底总是很忙)。但我肯定会测试所有这些出色的解决方案。谢谢你:)。

标签: awk


【解决方案1】:

这会比你所拥有的更快,因为它只遍历与 file2 具有相同 $1 值的 file1 内容,并在找到匹配的范围后停止搜索:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==FNR {
    c = ++num[$1]
    beg[$1][c] = $2
    end[$1][c] = $3
    val[$1][c] = $NF
    next
}
$1 in val {
    for (c=1; c<=num[$1]; c++) {
        if ( (beg[$1][c] <= $2) && ($2 <= end[$1][c]) ) {
            print $0, val[$1][c]
            break
        }
    }
}

$ awk -f tst.awk file1 file2
1       949800  .       T       G       ISG15
2       900000  rs123   -       A       AGRN

不幸的是,对于未排序的输入,您没有太多选项可以使其更快。如果 file1 中的范围可以相互重叠,则删除“中断”。

【讨论】:

    【解决方案2】:

    您能否尝试关注一下,如果这对您有帮助,请告诉我。

    awk 'FNR==NR{A[$1]=$0;B[$1,++D[$1]]=$2;next} {++C[$1]}($2<B[$1,C[$1]] && $3>B[$1,C[$1]]){print A[$1]}'  Input_file2   Input_file1
    

    这里逐个读取文件,这里先读取文件Input_file2再读取Input_file1。

    【讨论】:

      【解决方案3】:

      如果性能是问题,您必须按值(和范围开始)对两个文件进行排序。

      对文件进行排序后,您的扫描可以是增量的(因此速度更快)

      这是一个未经测试的脚本

      $ awk '{line=$0; k=$2;   
              getline < "file1"; 
              while (k >= $2) getline < "file1"; 
              if(k <= $3) print line, $NF}' file2
      

      【讨论】:

      • 如果 getline 失败,这将进入无限循环,请参阅awk.freeshell.org/AllAboutGetline 了解如何安全地调用 getline。
      • 感谢大家的出色解决方案、解释和帮助。我真的很感激,因为我的数据集只会越来越大。谢谢你:)。
      【解决方案4】:

      您可以尝试使用gawk 中的多数组从file1 创建一个字典,这样计算效率更高(file1 的大小比file2 小),

      awk '
          NR==FNR{for(i=$2;i<=$3;++i) d[$1,i] = $6; next}
          d[$1,$2]{print $0, d[$1,$2]}' file1 file2
      

      你明白了,

      1  949800  .   T   G ISG15
      2  900000  rs123   -   A AGRN
      

      【讨论】:

        【解决方案5】:

        一种可能的方法是使用 AWK 生成另一个 AWK 文件。内存消耗应该很低,所以对于一个 真的 大的file1 这可能是一个救命稻草。至于速度,这可能取决于 AWK 实现的智能程度。我还没有机会在庞大的数据集上进行尝试;我很好奇你的发现。

        创建文件step1.awk:

        {
            sub(/^chr/, "", $1);
            print "$1==\"" $1 "\" && " $2 "<$2 && $2<" $3 " { print $0 \"\\t" $6 "\"; }";
        }
        

        应用到file1:

        $ awk -f step1.awk file1
        $1=="1" && 948953<$2 && $2<948956 { print $0 "\tISG15"; }
        $1=="1" && 949363<$2 && $2<949858 { print $0 "\tISG15"; }
        

        将输出传送到文件step2.awk 并将其应用于file2

        $ awk -f step1.awk file1 > step2.awk
        $ awk -f step2.awk file2
         1  949800  rs201725126 T   G   ISG15
        

        替代方案:生成 C

        我重写了step1.awk,使其生成 C 而不是 AWK 代码。这不仅可以解决您之前报告的内存问题;考虑到 C 被编译为本机代码这一事实,它也会很多快。

        BEGIN {
            print "#include <stdio.h>";
            print "#include <string.h>";
            print "int main() {";
            print "   char s[999];";
            print "   int a, b;";
            print "   while (fgets(s, sizeof(s), stdin)) {";
            print "      s[strlen(s)-1] = 0;";
            print "      sscanf(s, \"%d %d\", &a, &b);";
        }
        
        {
            print "      if (a==" $1 " && " $2 "<b && b<" $3 ") printf(\"%s\\t%s\\n\", s, \"" $6 "\");";
        }
        
        END {
            print "   }";
            print "}";
        }
        

        鉴于您的示例 file1,这将生成以下 C 源代码:

        #include <stdio.h>
        #include <string.h>
        int main() {
           char s[999];
           int a, b;
           while (fgets(s, sizeof(s), stdin)) {
              s[strlen(s)-1] = 0;
              sscanf(s, "%d %d", &a, &b);
              if (a==1 && 948953<b && b<948956) printf("%s\t%s\n", s, "ISG15");
              if (a==1 && 949363<b && b<949858) printf("%s\t%s\n", s, "ISG15");
              if (a==2 && 800000<b && b<900500) printf("%s\t%s\n", s, "AGRN");
           }
        }
        

        示例输出:

        $ awk -f step1.awk file1 > step2.c
        $ cc step2.c -o step2
        $ ./step2 < file2
         1  949800  .   T   G   ISG15
         2  900000  rs123   -   A   AGRN
        

        【讨论】:

        • 我添加了一个编辑,因为需要一个额外的字段,但我不确定如何合并它。谢谢你:)。
        • 我收到以下错误:awk: awkgram.y:5015: find_line: Assertion lineno > 0' 失败。中止(核心转储)`,并将在帖子中发布更多详细信息。非常感谢:)。
        • @Chris 考虑到错误的严重性,可能是 AWK 内存不足。我对内存消耗的乐观看法是错误的;我认为 AWK 会将step2.awk 的全部(解析)内容存储在内存中。
        • @Ruud 好吧,是的,awk 必须读取脚本文件 step2.awk 的内容才能执行它,所以如果它很大,那么它可能会导致问题。
        • @Chris 我重写了脚本以生成 C 而不是 AWK。这应该可以解决内存问题并且极大地提升性能。
        【解决方案6】:

        它可能无效,但应该起作用,但速度很慢:

        $ awk 'NR==FNR{ a[$2]=$0; next }
                      { for(i in a) 
                            if(i>=$2 && i<=$3) print a[i] "\t" $6 }
          ' f2 f1
        1       949800  .       T       G       ISG15
        3       900000  .       C       -       AGRN
        

        基本上它会读取内存中的file2,对于file1 中的每一行,它都会遍历file2 的每个条目(在内存中)。它不会将 2 GB 的文件读入内存,因此它仍然不会像您的版本那样查找。

        您可以通过将 print a[i] "\t" $6 替换为 {print a[i] "\t" $6; delete a[i]} 来加快速度。

        编辑:添加了分隔输出的制表符并刷新了输出以反映更改的数据。打印 "\t" 就足够了,因为文件已经用制表符分隔,并且在任何时候都不会重建记录。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-02-19
          相关资源
          最近更新 更多