我有两个文件
由一条染色体及其位置组成的SNP数据(约40万个条目)
chr pos
a1 456
a2 789
. .
. .
so on
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中实现这一目标的更有效方法吗?
答案 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
}
}
}