使用 perl 循环遍历值列表 (x) 并与具有值范围的另一个文件进行比较
Using perl to cycle through a list of values (x) and compare to another file with value ranges
我有两个文件。一个文件有一个像这样的值列表
NC_SNPStest.txt
250
275
375
另一个文件有 space 分隔信息。第一列是范围的第一个值,第二列是范围的第二个值,第五列是范围的名称,第八列是作用于该范围的内容。
promoterstest.txt
20 100 yaaX F yaaX 5147 5.34 Sigma70 99
200 300 yaaA R yaaAp1 6482 6.54 Sigma70 35
350 400 yaaA R yaaAp2 6498 2.86 Sigma70 51
我正在尝试编写一个脚本,该脚本从文件 1 中获取第一行,然后逐行解析文件 2 以查看该值是否落在前两列之间的范围内。
找到第一个匹配项后,我想打印文件 1 中的值,然后打印文件 2 中匹配项所在行的第 5 列和第 8 列的值。如果在文件 2 中未找到匹配项,则只需打印文件 1 中的值并继续。
看起来这应该是一项足够简单的任务,但我在循环浏览这两个文件时遇到了问题。
这是我写的:
#!/usr/bin/perl
use warnings;
use strict;
open my $PromoterFile, '<', 'promoterstest.txt' or die $!;
open my $SNPSFile, '<', 'NC_SNPtest.txt' or die $!;
open (FILE, ">PromoterMatchtest.txt");
while (my $SNPS = <$SNPSFile>) {
chomp ($SNPS);
while (my $Cord = <$PromoterFile>) {
chomp ($Cord);
my @CordFile =split(/\s/, $Cord);
my $Lend = $CordFile[0];
my $Rend = $CordFile[1];
my $Promoter = $CordFile[4];
my $SigmaFactor = $CordFile[7];
foreach $a ($SNPS)
{
if ($a >= $Lend && $a <= $Rend)
{
print FILE "$a\t$CordFile[4]\t$CordFile[7]\n";
}
else
{
print FILE "$a\n";
}
}
}
}
close FILE;
close $PromoterFile;
close $SNPSFile;
exit;
到目前为止我的输出是这样的:
250
250 yaaAp1 Sigma70
250
调用文件 1 的第一行并循环文件 2 的地方。但是文件 2 的每一行都使用了 else
命令,并且脚本从不循环遍历文件 1 的其他行。
你的问题是你没有通过第二个文件重置你的进度。您从 $SNPSFile
中读取一行,对照第二个文件中的每一行进行检查。
但是当你重新开始时,你已经在文件末尾了,所以:
while (my $Cord = <$PromoterFile>) {
没有什么可读的。
一个快速解决方法是在其中添加一个 seek
命令,但这会使代码效率低下。我建议改为将文件 1 读入数组,然后引用它。
这是一份重写初稿,可能会有帮助。
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
open my $PromoterFile, '<', 'promoterstest.txt' or die $!;
open my $SNPSFile, '<', 'NC_SNPtest.txt' or die $!;
open my $output, ">", "PromoterMatchtest.txt" or die $!;
my @data;
while (<$PromoterFile>) {
chomp;
my @CordFile = split;
my $Lend = $CordFile[0];
my $Rend = $CordFile[1];
my $Promoter = $CordFile[4];
my $SigmaFactor = $CordFile[7];
push(
@data,
{ lend => $CordFile[0],
rend => $CordFile[1],
promoter => $CordFile[4],
sigmafactor => $CordFile[7]
}
);
}
print Dumper \@data;
foreach my $value (<$SNPSFile>) {
chomp $value;
my $found = 0;
foreach my $element (@data) {
if ( $value >= $element->{lend}
and $value <= $element->{rend} )
{
#print "Found $value\n";
print {$output} join( "\t",
$value, $element->{promoter}, $element->{sigmafactor} ),
"\n";
$found++;
last;
}
}
if ( not $found ) {
print {$output} $value,"\n";
}
}
close $output;
close $PromoterFile;
close $SNPSFile;
首先 - 我们打开 file2,将其中的内容读入哈希数组。 (如果其中任何元素是唯一的,我们可以将其关闭。)
然后我们逐行读取 SNPSfile,查找每个密钥 - 如果存在则打印它(至少一次,在第一次点击时),如果不存在则仅打印该密钥。
这会生成输出:
250 yaaAp1 Sigma70
275 yaaAp1 Sigma70
375 yaaAp2 Sigma70
这是你的目标吗?
除了输出 @data
内容的 'Dumper' 语句外:
$VAR1 = [
{
'sigmafactor' => 'Sigma70',
'promoter' => 'yaaX',
'lend' => '20',
'rend' => '100'
},
{
'sigmafactor' => 'Sigma70',
'promoter' => 'yaaAp1',
'rend' => '300',
'lend' => '200'
},
{
'promoter' => 'yaaAp2',
'sigmafactor' => 'Sigma70',
'rend' => '400',
'lend' => '350'
}
];
这是我对编程解决方案的看法。重要的是
使用词法文件句柄和open
的三参数形式
局部变量保持小写字母、数字和下划线
我还使用了 autodie
pragma 来消除显式测试 open
状态的需要,以及核心库 List::Util
中的 first
函数到让代码更清晰简洁
use strict;
use warnings;
use 5.010;
use autodie;
use List::Util 'first';
my @promoters;
{
open my $fh, '<', 'promoterstest.txt';
while ( <$fh> ) {
my @fields = split;
push @promoters, [ @fields[0,1,4,7] ];
}
}
open my $fh, '<', 'NC_SNPStest.txt';
open my $out_fh, '>', 'PromoterMatchtest.txt';
select $out_fh;
while ( <$fh> ) {
my ($num) = split;
my $match = first { $num >= $_->[0] and $num <= $_->[1] } @promoters;
if ( $match ) {
print join("\t", $num, @{$match}[2,3]), "\n";
}
else {
print $num, "\n";
}
}
输出
250 yaaAp1 Sigma70
275 yaaAp1 Sigma70
375 yaaAp2 Sigma70
我有两个文件。一个文件有一个像这样的值列表
NC_SNPStest.txt
250
275
375
另一个文件有 space 分隔信息。第一列是范围的第一个值,第二列是范围的第二个值,第五列是范围的名称,第八列是作用于该范围的内容。
promoterstest.txt
20 100 yaaX F yaaX 5147 5.34 Sigma70 99
200 300 yaaA R yaaAp1 6482 6.54 Sigma70 35
350 400 yaaA R yaaAp2 6498 2.86 Sigma70 51
我正在尝试编写一个脚本,该脚本从文件 1 中获取第一行,然后逐行解析文件 2 以查看该值是否落在前两列之间的范围内。
找到第一个匹配项后,我想打印文件 1 中的值,然后打印文件 2 中匹配项所在行的第 5 列和第 8 列的值。如果在文件 2 中未找到匹配项,则只需打印文件 1 中的值并继续。
看起来这应该是一项足够简单的任务,但我在循环浏览这两个文件时遇到了问题。
这是我写的:
#!/usr/bin/perl
use warnings;
use strict;
open my $PromoterFile, '<', 'promoterstest.txt' or die $!;
open my $SNPSFile, '<', 'NC_SNPtest.txt' or die $!;
open (FILE, ">PromoterMatchtest.txt");
while (my $SNPS = <$SNPSFile>) {
chomp ($SNPS);
while (my $Cord = <$PromoterFile>) {
chomp ($Cord);
my @CordFile =split(/\s/, $Cord);
my $Lend = $CordFile[0];
my $Rend = $CordFile[1];
my $Promoter = $CordFile[4];
my $SigmaFactor = $CordFile[7];
foreach $a ($SNPS)
{
if ($a >= $Lend && $a <= $Rend)
{
print FILE "$a\t$CordFile[4]\t$CordFile[7]\n";
}
else
{
print FILE "$a\n";
}
}
}
}
close FILE;
close $PromoterFile;
close $SNPSFile;
exit;
到目前为止我的输出是这样的:
250
250 yaaAp1 Sigma70
250
调用文件 1 的第一行并循环文件 2 的地方。但是文件 2 的每一行都使用了 else
命令,并且脚本从不循环遍历文件 1 的其他行。
你的问题是你没有通过第二个文件重置你的进度。您从 $SNPSFile
中读取一行,对照第二个文件中的每一行进行检查。
但是当你重新开始时,你已经在文件末尾了,所以:
while (my $Cord = <$PromoterFile>) {
没有什么可读的。
一个快速解决方法是在其中添加一个 seek
命令,但这会使代码效率低下。我建议改为将文件 1 读入数组,然后引用它。
这是一份重写初稿,可能会有帮助。
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
open my $PromoterFile, '<', 'promoterstest.txt' or die $!;
open my $SNPSFile, '<', 'NC_SNPtest.txt' or die $!;
open my $output, ">", "PromoterMatchtest.txt" or die $!;
my @data;
while (<$PromoterFile>) {
chomp;
my @CordFile = split;
my $Lend = $CordFile[0];
my $Rend = $CordFile[1];
my $Promoter = $CordFile[4];
my $SigmaFactor = $CordFile[7];
push(
@data,
{ lend => $CordFile[0],
rend => $CordFile[1],
promoter => $CordFile[4],
sigmafactor => $CordFile[7]
}
);
}
print Dumper \@data;
foreach my $value (<$SNPSFile>) {
chomp $value;
my $found = 0;
foreach my $element (@data) {
if ( $value >= $element->{lend}
and $value <= $element->{rend} )
{
#print "Found $value\n";
print {$output} join( "\t",
$value, $element->{promoter}, $element->{sigmafactor} ),
"\n";
$found++;
last;
}
}
if ( not $found ) {
print {$output} $value,"\n";
}
}
close $output;
close $PromoterFile;
close $SNPSFile;
首先 - 我们打开 file2,将其中的内容读入哈希数组。 (如果其中任何元素是唯一的,我们可以将其关闭。)
然后我们逐行读取 SNPSfile,查找每个密钥 - 如果存在则打印它(至少一次,在第一次点击时),如果不存在则仅打印该密钥。
这会生成输出:
250 yaaAp1 Sigma70
275 yaaAp1 Sigma70
375 yaaAp2 Sigma70
这是你的目标吗?
除了输出 @data
内容的 'Dumper' 语句外:
$VAR1 = [
{
'sigmafactor' => 'Sigma70',
'promoter' => 'yaaX',
'lend' => '20',
'rend' => '100'
},
{
'sigmafactor' => 'Sigma70',
'promoter' => 'yaaAp1',
'rend' => '300',
'lend' => '200'
},
{
'promoter' => 'yaaAp2',
'sigmafactor' => 'Sigma70',
'rend' => '400',
'lend' => '350'
}
];
这是我对编程解决方案的看法。重要的是
使用词法文件句柄和
open
的三参数形式
局部变量保持小写字母、数字和下划线
我还使用了 autodie
pragma 来消除显式测试 open
状态的需要,以及核心库 List::Util
中的 first
函数到让代码更清晰简洁
use strict;
use warnings;
use 5.010;
use autodie;
use List::Util 'first';
my @promoters;
{
open my $fh, '<', 'promoterstest.txt';
while ( <$fh> ) {
my @fields = split;
push @promoters, [ @fields[0,1,4,7] ];
}
}
open my $fh, '<', 'NC_SNPStest.txt';
open my $out_fh, '>', 'PromoterMatchtest.txt';
select $out_fh;
while ( <$fh> ) {
my ($num) = split;
my $match = first { $num >= $_->[0] and $num <= $_->[1] } @promoters;
if ( $match ) {
print join("\t", $num, @{$match}[2,3]), "\n";
}
else {
print $num, "\n";
}
}
输出
250 yaaAp1 Sigma70
275 yaaAp1 Sigma70
375 yaaAp2 Sigma70