perl:从列表中查找宽度最大的区域

时间:2015-04-25 18:15:09

标签: perl

我有一张具有以下结构的表

gene    transcript    exon    length
A       NM_1          1       10
A       NM_1          2       5
A       NM_1          3       20
A       NM_2          1       10
A       NM_2          2       5
A       NM_2          3       50
B       NM_5          1       10
...     ...           ...     ...

基本上,该表由一个包含所有人类基因的列组成。第二列包含成绩单名称。同一基因可以有多个转录本。第三列包含外显子编号。每个基因都由多个外显子组成。第四列包含每个外显子的长度。

现在我想创建一个如下所示的新表:

 gene   transcript    length
 A      NM_2          65
 B      NM_5          10
 ...    ...           ...

所以我基本上想做的是找到每个基因最长的转录本。 这意味着当每个基因(柱基因)有多个转录本(列转录本)时,我需要在该基因转录本的所有外显子的长度列中得到值的总和。

因此,在该示例中,基因A有两个转录本:NM_1和NM_2。每个都有三个外显子。 NM_1的这三个值的总和= 10 + 5 + 20 = 35,对于NM_2,它的10 + 5 + 50 = 65.因此对于基因A,NM_2是最长的转录本,所以我想把它放在新表。对于基因B,只有1个转录本,其中一个外显子长度为10.所以在新表中,我只想要报告该转录本的长度。

之前我曾经使用过哈希,所以我想到了存储'基因'和'成绩单'作为两个不同的键:

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

open(my $test,'<',"test.txt") || die ("Could not open file $!");
open(my $output, '+>', "output.txt") || die ("Can't write new file: $!");

# skip the header of $test # I know how to do this

my %hash = ();
while(<$test>){
    chomp;
    my @cols = split(/\t/);
    my $keyfield = $cols[0]; #gene name
    my $keyfield2 = $cols[1]; # transcript name
    push @{ $hash{$keyfield} }, $keyfield2;

...

2 个答案:

答案 0 :(得分:1)

考虑到你要做的事情,我会想到这样的事情:

use strict;
use warnings;

my %genes;

my $header_line = <DATA>;

#read the data
while (<DATA>) {
    my ( $gene, $transcript, $exon, $length ) = split;
    $genes{$gene}{$transcript} += $length;
}

print join( "\t", "gene", "transcript", "length_sum" ), "\n";

foreach my $gene ( keys %genes ) {
    #sort by length_sum, and 'pop' the top of the list. 
    my ($longest_transcript) =
        ( sort { $genes{$gene}{$b} <=> $genes{$gene}{$a} or $a cmp $b }
            keys %{ $genes{$gene} } );
    print join( "\t",
        $gene, $longest_transcript, $genes{$gene}{$longest_transcript} ),
        "\n";
}


__DATA__ 
gene    transcript    exon    length
A       NM_1          1       10
A       NM_1          2       5
A       NM_1          3       20
A       NM_2          1       10
A       NM_2          2       5
A       NM_2          3       50
B       NM_5          1       10

<强>输出

gene    transcript  length_sum
B   NM_5    10
A   NM_2    65

答案 1 :(得分:0)

使用List::UtilsBy中的nmax_by(数字最大值)函数,这样做更加不整洁。该程序在哈希中累计总长度,然后使用nmax_by为每个基因挑选最长的转录本。

我认为您可以在$fh上打开输入文件而不是使用DATA句柄?或者您可以将路径传递到命令行上的输入文件,只需使用<>而不是<$fh>,而无需明确打开任何内容。

use strict;
use warnings;

use List::UtilsBy qw/ nmax_by /;

my $fh = \*DATA;

<$fh>; # Drop header line

my %genes;

while ( <$fh> ) {
  my ($gene, $trans, $exon, $len) = split;
  $genes{$gene}{$trans} += $len;
}

my $fmt = "%-7s%-14s%-s\n";
printf $fmt, qw/ gene transcript length /;
for my $gene ( sort keys %genes ) {
  my $trans = nmax_by { $genes{$gene}{$_} } keys %{ $genes{$gene} };
  printf ' '.$fmt, $gene, $trans, $genes{$gene}{$trans};
}


__DATA__
gene    transcript    exon    length
A       NM_1          1       10
A       NM_1          2       5
A       NM_1          3       20
A       NM_2          1       10
A       NM_2          2       5
A       NM_2          3       50
B       NM_5          1       10

<强>输出

gene   transcript    length
 A      NM_2          65
 B      NM_5          10

<强>更新

这是一个缩短版nmax_by,可供您测试。你可以在程序的顶部添加它,或者如果你想把它放在最后,那么你需要在顶部用sub nmax_by(&@);预先声明它,因为它有一个原型

sub nmax_by(&@) {
   my $code = shift;
   my ($max, $maxval);
   for ( @_ ) {
     my $val = $code->($_);
     ($max, $maxval) = ($_, $val) unless defined $maxval and $maxval >= $val;
   }
   $max;
}