合并行并在满足条件时执行操作

时间:2013-06-06 17:25:11

标签: perl merge sum lines

我是perl的新手,我想读一张表,并从特定的行中得到一些值的总和。这是我的输入文件的简化示例:

INPUT:

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;
}

2 个答案:

答案 0 :(得分: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";

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

我们将使用多级散列,将基因用作第一级密钥,将内含子用作第二级密钥。值是count / tow / whatever:

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";
  }
}

答案 1 :(得分:0)

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

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

所以我在SQL中提出的解决方案,如果从数据库收集数据是: 在GENE和feature字段上编写一个涉及GROUP BY的SQL语句,并聚合size列。 如果您的表看起来与您描述的完全相同,那么我们将其称为GeneInformation表,并将数据文件加载到SQL数据库(可能是SQLLite),然后您的选择查询将是:

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

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