我有一张具有以下结构的表
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;
...
答案 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;
}