0

我是 perl 的新手,我想阅读一个表格并对特定行的一些值求和。这是我的输入文件的简化示例:

输入 :

Gene  Size Feature

GeneA 1200 Intron 1

GeneB 100  Intron 1

GeneB 200  Intron 1

GeneB 150  Intron 2

GeneC 300  Intron 5

输出 :

GeneA 1200 Intron 1

GeneB 300  Intron 1 <-- the size values are summed 

GeneB 150  Intron 2

GeneC 300  Intron 5

因为基因 B 存在于具有两种不同大小的内含子 1,所以我想将这两个值相加,并且每个内含子编号只打印一行。

这是我想做的代码示例。但如果我能理解如何处理这种数据,我想让它变得更复杂。

#!/usr/bin/perl
use strict;
use warnings;
my $sum;
my @GAP_list;
my $prevline = 'na';
open INFILE,"Table.csv";
while (my $ligne = <INFILE>) 
  {
chomp ($ligne);
my @list = split /\t/, $ligne;

  my $gene= $list[0];   
  my $GAP_size= $list[2];  
  my $intron= $list[3];
  my $intron_number=$list[4];


  if($prevline eq 'na'){
  push @GAP_list, $GAP_size;
  }
  elsif($prevline ne 'na') {
  my @list_p = split /\t/,$prevline;
  my $gene_p= $list_p[0];   
  my $GAP_size_p= $list_p[2]; 
  my $intron_p= $list_p[3];
  my $intron_number_p=$list_p[4];
      if (($gene eq $gene_p) && ($intron eq $intron_p) && ($intron_number eq $intron_number_p)){
  push @GAP_list, $GAP_size;
       }
   }
  else{
  $sum = doSum(@GAP_list);
  print "$gene\tGAP\t$GAP_size\t$intron\t$intron_number\t$sum\n";
    $prevline=$ligne;

  }     

 }  

# Subroutine
sub doSum {
    my $sum = 0;
    foreach my $x (@_) { 
        $sum += $x; 
    }
    return $sum;
}
4

2 回答 2

1

假设字段由制表符分隔,则以下策略将起作用。它缓冲最后一行,如果其他字段相等,则相加,或者打印旧数据,然后用当前行替换缓冲区。

处理完整个输入后,我们一定不要忘记打印出仍在缓冲区中的内容。

my $first_line = do { my $l = <>; chomp $l; $l };
my ($last_gene, $last_tow, $last_intron) = split /\t/, $first_line;

while(<>) {
  chomp;
  my ($gene, $tow, $intron) = split /\t/;
  if ($gene eq $last_gene and $intron eq $last_intron) {
    $last_tow += $tow;
  } else {
    print join("\t", $last_gene, $last_tow, $last_intron), "\n";
    ($last_gene, $last_tow, $last_intron) = ($gene, $tow, $intron);
  }
}

print join("\t", $last_gene, $last_tow, $last_intron), "\n";

只要可能折叠在一起的基因总是连续的,这种方法就可以正常工作。如果可连接记录分布在整个文件中,我们必须保留所有记录的数据结构。解析整个文件后,我们可以发出排序良好的总和。

我们将使用一个多级哈希,它使用基因作为第一级键,内含子作为第二级键。该值是计数/拖曳/无论如何:

my %records;

# parse the file
while (<>) {
  chomp;
  my ($gene, $tow, $intron) = split /\t/;
  $records{$gene}{$intron} += $tow;
}

# emit the data:
for my $gene (sort keys %records) {
  for my $intron (sort keys %{ $records{$gene} }) {
    print join("\t", $gene, records{$gene}{$intron}, $intron), \n";
  }
}
于 2013-06-06T17:39:32.533 回答
0

这似乎更像是可以使用简单的SQL Query轻松完成的事情。特别是当您以数据库表格式获取文件时。我无法对您的问题发表评论,因为我没有足够的声誉来向您询问更多有关它的信息。

因此,我假设您从表中获取数据。并不是说你不能在 Perl 中解决这个问题。但我强烈建议在获取数据文件时使用数据库进行这样的计算,因为这看起来更容易。而且我不确定您为什么选择在 Perl 中执行此操作,尤其是当您在一个文件中有很多此类字段并且您想对所有这些字段执行此类操作时。在通过 SQL 查询解决问题时,您仍然可以使用 Perl 与数据库进行交互。

因此,如果从数据库中收集数据,我建议的 SQL 解决方案是:在 GENE 和特征字段上编写涉及 GROUP BY 的 SQL 语句,并聚合大小列。如果您的表看起来与您描述的完全一样,让我们​​称之为 GeneInformation 表,并且您将数据文件加载到 SQL 数据库(可能是 SQLLite),那么您的选择查询将是:

SELECT gene, feature, SUM(size) FROM GeneInformation 
 GROUP 
    BY gene, feature;

这应该会给你一个基因、特征和它们相应的总大小的列表。
如果 SQL 解决方案对您来说完全不可能,那么我将讨论 Perl 解决方案。 我注意到 Perl 解决方案是基于特定基因的值将连续出现在文件中的假设。如果是这种情况,那么我想对amon的回答投赞成票(目前我不能这样做)。

于 2013-06-06T18:33:46.813 回答