【问题标题】:Fast alternative to grep -fgrep -f 的快速替代方案
【发布时间】:2012-07-14 10:59:51
【问题描述】:

file.contain.query.txt

ENST001

ENST002

ENST003

file.to.search.in.txt

ENST001  90

ENST002  80

ENST004  50

因为 ENST003 在第二个文件中没有条目,而 ENST004 在第一个文件中没有条目,所以预期的输出是:

ENST001 90

ENST002 80

要在特定文件中 grep 多查询,我们通常执行以下操作:

grep -f file.contain.query <file.to.search.in >output.file

因为我在 file.to.search.in 中有 10000 个查询和近 100000 个原始查询,所以需要很长时间才能完成(比如 5 小时)。是否有快速替代 grep -f 的方法?

【问题讨论】:

  • 您有什么需求?你想要一个文件,其中第二个的行用第一个的键过滤吗?
  • 我编辑了预期的结果
  • 输入重定向是不必要的。
  • 什么语言环境?试试LANG=C grep -F ... 看看速度是否由于语言环境和(正如@tripleee 谨慎建议的那样)固定字符串匹配而提高。

标签: perl awk


【解决方案1】:

如果您想要一个纯 Perl 选项,请将您的查询文件键读入哈希表,然后对照这些键检查标准输入:

#!/usr/bin/env perl
use strict;
use warnings;

# build hash table of keys
my $keyring;
open KEYS, "< file.contain.query.txt";
while (<KEYS>) {
    chomp $_;
    $keyring->{$_} = 1;
}
close KEYS;

# look up key from each line of standard input
while (<STDIN>) {
    chomp $_;
    my ($key, $value) = split("\t", $_); # assuming search file is tab-delimited; replace delimiter as needed
    if (defined $keyring->{$key}) { print "$_\n"; }
}

你会这样使用它:

lookup.pl < file.to.search.txt

哈希表可能会占用大量内存,但搜索要快得多(哈希表查找是在恒定时间内进行的),这很方便,因为要查找的键比要存储的键多 10 倍。

【讨论】:

  • 这是法拉利与 grep -f 相比 .. 谢谢
【解决方案2】:

如果您有固定字符串,请使用grep -F -f。这比正则表达式搜索要快得多。

【讨论】:

    【解决方案3】:

    这个 Perl 代码可以帮助你:

    use strict;
    open my $file1, "<", "file.contain.query.txt" or die $!;
    open my $file2, "<", "file.to.search.in.txt" or die $!;
    
    my %KEYS = ();
    # Hash %KEYS marks the filtered keys by "file.contain.query.txt" file
    
    while(my $line=<$file1>) {
        chomp $line;
        $KEYS{$line} = 1;
    }
    
    while(my $line=<$file2>) {
        if( $line =~ /(\w+)\s+(\d+)/ ) {
            print "$1 $2\n" if $KEYS{$1};
        }
    }
    
    close $file1;
    close $file2;
    

    【讨论】:

    • 您忘记检查系统调用的返回值。
    【解决方案4】:

    如果文件已经排序:

    join file1 file2
    

    如果没有:

    join <(sort file1) <(sort file2)
    

    【讨论】:

      【解决方案5】:

      如果您使用 perl 5.10 或更高版本,您可以将“查询”字词加入正则表达式,查询字词由“竖线”分隔。 (Like:ENST001|ENST002|ENST003) Perl 构建了一个“trie”,就像哈希一样,在恒定时间内进行查找。它应该与使用查找哈希的解决方案一样快。只是为了展示另一种方法。

      #!/usr/bin/perl
      use strict;
      use warnings;
      use Inline::Files;
      
      my $query = join "|", map {chomp; $_} <QUERY>;
      
      while (<RAW>) {
          print if /^(?:$query)\s/;
      }
      
      __QUERY__
      ENST001
      ENST002
      ENST003
      __RAW__
      ENST001  90
      ENST002  80
      ENST004  50
      

      【讨论】:

        【解决方案6】:

        Mysql:

        将数据导入 Mysql 或类似工具将提供巨大的改进。这可行吗?您可以在几秒钟内看到结果。

        mysql -e 'select search.* from search join contains using (keyword)' > outfile.txt 
        
        # but first you need to create the tables like this (only once off)
        
        create table contains (
           keyword   varchar(255)
           , primary key (keyword)
        );
        
        create table search (
           keyword varchar(255)
           ,num bigint
           ,key (keyword)
        );
        
        # and load the data in:
        
        load data infile 'file.contain.query.txt' 
            into table contains fields terminated by "add column separator here";
        load data infile 'file.to.search.in.txt' 
            into table search fields terminated by "add column separator here";
        

        【讨论】:

        • 我没有对此进行测试,但它会根据您的情况进行一些调整。除非您希望它基于 ram,否则它将占用很少的内存。
        【解决方案7】:
        use strict;
        use warings;
        
        system("sort file.contain.query.txt > qsorted.txt");
        system("sort file.to.search.in.txt  > dsorted.txt");
        
        open (QFILE, "<qsorted.txt") or die();
        open (DFILE, "<dsorted.txt") or die();
        
        
        while (my $qline = <QFILE>) {
          my ($queryid) = ($qline =~ /ENST(\d+)/); 
          while (my $dline = <DFILE>) {
            my ($dataid) = ($dline =~ /ENST(\d+)/);
            if ($dataid == $queryid)   { print $qline; }
            elsif ($dataid > $queryid) { break; } 
          }
        }
        

        【讨论】:

          【解决方案8】:

          这可能有点过时,但它是为简单的 UNIX 实用程序量身定制的。给定:

          • 键是固定长度的(这里是 7 个字符)
          • 文件已排序(在示例中为 true),允许使用快速合并排序

          然后:

          $ sort -m file.contain.query.txt file.to.search.in.txt | tac | uniq -d -w7
          
          ENST002  80
          
          ENST001  90
          

          变体:

          要去除键后打印的数字,请删除 tac 命令:

          $ sort -m file.contain.query.txt file.to.search.in.txt | uniq -d -w7
          

          要保持排序,请在末尾添加一个额外的 tac 命令:

          $ sort -m file.contain.query.txt file.to.search.in.txt | tac | uniq -d -w7 | tac
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-11
            • 1970-01-01
            • 2013-10-03
            相关资源
            最近更新 更多