根据条件跳转文件中的行块

时间:2013-09-27 15:41:52

标签: perl file

我有一个.bedGraph文件,如下所示:

chr     start   end     score
chr1    3000305 3000306 0.006
chr1    3000306 3000307 0.01
chr1    3000307 3000308 0.014
chr1    3000308 3000309 0.017
chr1    3000309 3000310 0.019
chr1    3000310 3000313 0.021
chr1    3000313 3000314 0.029
chr1    3000314 3000315 0.027
chr1    3000315 3000316 0.02
chr1    3000316 3000317 0.011

我必须编写一个脚本,该脚本将遍历此文件并查找得分> 0.02,获取该分数的 开始 值,然后继续搜索得分<0.02,此时它应该抓住之前的 结束 位置。所以在这种情况下,程序应该从文件的开头遍历,确定第一个得分> 0.02,抓住开始位置= 3000310 获得该得分并继续搜索直到得分低于0.02应该抓住之前的结束位置= 3000316 。在此之后,它应继续在文件中搜索此类块并获取包含得分> 0.02的的开始和结束位置。同样,它应该不要抓住包含分数> 0.02 的块的所有开始和结束,但只是这个块的第一个开始和结束

我已经编写了部分代码,但不知道如何继续进行:

open BEDGRAPH, $ARGV[0] or die print $!;

my $thresh=0.5;
my $j=1;
my $i=1;
my @arr = <BEDGRAPH>;
my @tmp;
for $i (0 .. $#arr)
{
my ($chr, $start, $end, $score) = split('\s',$arr[$i]);
if($score>=$thresh)
{
    push(@tmp,$chr);
    push(@tmp,$start);
    $j=$i+1;
    my ($chr1, $start1, $end1, $score1) = split('\s',$arr[$j]);
    while($score1>=$thresh)
    {
        $j=$j+1;
    }
    my ($chr2, $start2, $end2, $score2) = split('\s',$arr[$j-1]);
    push(@tmp,$end2);
    $i=$j+1;
    print @tmp;
}
elsif($score>=$thresh)
{
        $i=$i+1;
}
}

close(BEDGRAPH);

在这里,我试图推动所需的开始&amp;在@tmp结束职位并打印出来。

4 个答案:

答案 0 :(得分:3)

一些建议让你入门。

首先,为什么要循环浏览文件两次?当您将其读入数组并在处理数组时再次执行此操作。为什么不直接按行读取文件进行处理?

# Use a lexical filehandle and test `open` for failure
my $file = $ARGV[0];
open my $fh, "<", $file or die "Failed to open file '$file': $!";

while (<$fh>) {
    my ($chr, $start, $end, $score) = split;
...

请注意,我没有引用带有数组索引的列,而是使用有意义的变量名。

此外,避免像瘟疫一样的魔法数字,并将你的阈值放在变量中。这样,如果它确实从0.02更改为0.5,您只需在代码中的一个位置更新它。变量名对于阅读代码的人来说往往比魔术数更有意义。

my $threshold = 0.02;

在您阅读文件时,需要跟踪一些信息。

  1. 您是否在一个街区内(即分数大于您的门槛的部分)?
  2. 如果你在一个区块内,那么在区块的开头start的价值是多少?
  3. 如果您在一个区块内,前一行的end值是多少?你需要这个,因为你没有发现你在下一行之前已经离开了一个街区。
  4. 如果您考虑如何获取这些信息,您应该能够弄清楚其余部分。


    编辑完全使用您的最新编辑更改了代码。这应该是一个新问题。

    您的更新代码存在直接问题:

    open BEDGRAPH, $ARGV[0] or die print $!;
    

    使用词法文件句柄(open my $fh)而不是typeglobs(open FILE),它们是全局范围的。

    my @arr = <BEDGRAPH>;
    

    您在评论中提到您正在使用非常大的文件,但是您正在使用@array = <$fh>将它们读入内存。你应该真的使用while (<$fh>) ...

    while($score1>=$thresh)
    {
        $j=$j+1;
    }
    

    最后,您永远不会更改循环体中$score$thresh的值,因此它会永远运行。

答案 1 :(得分:2)

我认为您需要对ThisSuitisBlackNot所指出的程序进行更改。我以为我会发布一个可能的解决方案。

更新:如果chr名称可以更改,则此程序可能无法正常工作,需要进行调整。

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

my ($prev_chr, $prev_start, $prev_end);
my $thresh = .02;

while (<DATA>) {
    my ($chr, $start, $end, $score) = split;

    if ($score >= $thresh) {
        $prev_chr   //= $chr;
        $prev_start //= $start;
        $prev_end = $end;
    }
    else {
        if ($prev_chr) {
            print "$prev_chr $prev_start $prev_end\n";
            ($prev_chr, $prev_start, $prev_end) = (undef) x 3;
        }
    }
}
print "$prev_chr $prev_start $prev_end\n" if $prev_chr;

__DATA__
chr1    3000305 3000306 0.006
chr1    3000306 3000307 0.01
chr1    3000307 3000308 0.014
chr1    3000308 3000309 0.017
chr1    3000309 3000310 0.019
chr1    3000310 3000313 0.021
chr1    3000313 3000314 0.029
chr1    3000314 3000315 0.027
chr1    3000315 3000316 0.02
chr1    3000316 3000317 0.011

答案 2 :(得分:0)

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

open my $fh, '<', 'bedgraph.txt' or die "cant open bedgraph.txt $!";

my $thresh = 0.02;

my @start_pos;
my @end_pos;
my $previous_end;

my $header = <$fh>;

ABOVE:
while (<$fh>){
    my ($chr, $start, $end, $score) = split;
    if ($score > $thresh){
        push @start_pos, $start;
        $previous_end = $end;
        BELOW:
        while (<$fh>){ 
            my ($chr, $start, $end, $score) = split;
            if ($score < $thresh){
                push @end_pos, $previous;
                next ABOVE;  
            }
            $previous = $end;        
        }      
    }
}    

close $fh;

print "Start positions found: @start_pos\n";
print "End positions found: @end_pos\n";

#Start positions found: 3000310
#End positions found: 3000316

简要说明:

  • ABOVE循环:扫描文件,查找大于阈值的分数。 当找到/ 值时,它会存储当前行 start 列中的值 @start_pos 。然后它将控制传递给BELOW识别的循环。 让我们说这个值是在第六行找到的
  • 以下循环:开始在七号行扫描文件,寻找低于阈值的值。 当找到某个值时,它会存储上一行 end 列中 @end_pos 的值强>。然后它将控制传递给ABOVE识别的循环。 假设该值在第10行上找到。
  • ABOVE循环:开始在第11行扫描文件,并且proccess重复进行,直到文件的每一行都被读取。

答案 3 :(得分:0)

请考虑以下事项:

use strict;
use warnings;

my $startFound = 0;
my $priorEnd;

while (<>) {
    $. > 1 or next;    # Get past the header
    my ( undef, $start, $end, $score ) = split;

    if ( $score > .02 and !$startFound ) {
        $startFound = 1;
        print "Start: $start\n";
    }

    if ( $score < .02 and $startFound ) {
        $startFound = 0;
        print "End  : $priorEnd\n";
    }

    $priorEnd = $end;
}

用法:perl script.pl inFile [>outFile]

最后一个可选参数将输出定向到文件。

此脚本设置一个标志($startFound)来表示一个块的开头,然后检查低于.02的分数和该标志以找到该块的结束。 var $priorEnd只保留起始/结束对的最后一个'end'值。

希望这有帮助!