如何搜索介于给定数字之间的数字范围?

时间:2018-09-19 10:50:49

标签: python perl

我有两个文件

file1

由一条染色体及其位置组成的SNP数据(约40万个条目)

chr pos
a1 456
a2 789
 . .
 . . 
so on

file2

GTF数据由染色体,position_start,position_end和详细信息(约500,000个条目)组成

chr pos_start pos_end detail
a1 100 400 gene1
a1 401 700 gene2
a2 200 500 gene3
a2 501 900 gene4
 . .
 . . 
so on

所需结果

chr pos chr pos_start pos_end detail
a1 456 a1 401 700 gene2
a2 789 a2 501 900 gene4

我正在使用shell脚本获得此结果:

(grep "$chr" file2.gtf | awk '{if($2 <= '$pos' && $3 >= '$pos') print $0}') 

在while循环中,但是要花费太多时间来处理file1中的所有数字。

有人知道在Shell,Python或Perl中实现这一目标的更有效方法吗?

2 个答案:

答案 0 :(得分:1)

这是一个perl版本。基本思想是将gtf数据缓存到哈希表中,然后对于snp文件中的每一行,它只会查看与该染色体匹配的gtf条目。

#!/usr/bin/perl
use warnings;
use strict;
use feature qw/say/;
use autodie;

my $snp_file = "file1.txt";
my $gtf_file = "file2.txt";

# Read the gtf data into a hash of arrays
my %gtf;
open my $file, "<", $gtf_file;
my $hdr = <$file>; # Discard header line
while (<$file>) {
  chomp;
  my @cols = split /\s+/;
  push @{$gtf{$cols[0]}}, \@cols;
}
close $file;

open $file, "<", $snp_file;
$hdr = <$file>; # Discard header line
say "chr\tpos\tchr\tstart\tend\tdetail";
# Read the snp data
$" = "\t"; # Use tab for array element separator
while (<$file>) {
  chomp;
  my ($chr, $pos) = split /\s+/;
  # Look up all matches of this chromosome in the gtf hash and filter just
  # the ones where pos is in range.
  my @matches = grep { $pos >= $_->[1] && $pos <= $_->[2] } @{$gtf{$chr}};
  # And print them out.
  for my $match (@matches) {
    say "$chr\t$pos\t@$match";
  }
}
close $file;

如果要对这些数据进行大量处理,我将追求的另一种选择是将其全部加载到sqlite或另一个数据库中,并使用SQL查找结果。这样,您就不必继续读取数据文件。您只需在预先填充的表格中查找内容(使用适当的索引即可使内容高效)。

答案 1 :(得分:0)

我认为awk可以满足您的需求:

awk '
   FNR==1  { next}
   FNR==NR { chr[FNR]=$1; start[FNR]=$2; end[FNR]=$3; det[FNR]=$4; N=FNR; next}
           { c=$1; p=$2;
             for(i=2;i<=N;i++){
                if((c==chr[i]) && (p>=start[i]) && (p<=end[i])){
                   print c, p, chr[i], start[i], end[i], det[i]
                   next
                }
             }
           }
   ' file2 file1

因此,首先从最后一行注意到,awk的一次调用正在处理两个文件。

在处理过程中,通过检查当前文件中的行号是否为1并跳过,以忽略每个文件的第一行:

FNR==1  { next}

然后,如果当前文件中的记录号等于awk已处理的总记录号,那么我们必须读取第一个文件。因此,我们将每个字段保存在以行号索引的数组中,并避免任何进一步的处理:

FNR==NR { chr[FNR]=$1; start[FNR]=$2; end[FNR]=$3; det[FNR]=$4; N=FNR; next}

否则,我们必须正在处理第二个文件。在这种情况下,我们遍历从第一个文件保存的所有数组以找到匹配的条目。如果我们在正确的范围内找到一个,我们将打印出所需的碎片,并立即移至下一条记录:

{ c=$1; p=$2;
  for(i=2;i<=N;i++){
     if((c==chr[i]) && (p>=start[i]) && (p<=end[i])){
         print c, p, chr[i], start[i], end[i], det[i]
         next
     }
   }
}