如何将相等的值分组并使用Perl打印最小和最大参数?

时间:2013-01-12 21:06:45

标签: perl hash max min

我有这个输入数据:

miRNA17 dvex571195  207 230
miRNA17 dvex571195  189 229
miRNA17 dvex571195  207 241
miRNA17 dvex571195  207 230
miRNA17 dvex571195  80  111
miRNA18 dvex449276  12  43
miRNA18 dvex196735  2909    2929
miRNA18 dvex765590  183 213
miRNA22 dvex826214  206 236
miRNA23 dvex529006  111 168
miRNA24 dvex803612  317 347

我想将具有相同First和Second值的行分组。接下来,对于这些“组”,我需要在第三列中打印“组”的最小值,在第四列中打印“组”的最大值。输出将是:

miRNA17 dvex571195  80  241
miRNA18 dvex449276  12  43
miRNA18 dvex196735  2909    2929
miRNA18 dvex765590  183 213
miRNA22 dvex826214  206 236
miRNA23 dvex529006  111 168
miRNA24 dvex803612  317 347

我尝试将所有数据分组为大%哈希,我的密钥由第一列和第二列表示。但我可以使用一种方法来组织子后续列。代码是:

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

my %lines; # hash with 1rd and 2th elements as key
my %first_line_per_group; # stores in which line a group appeared first
while(my $line = <>) {
# remove line break
chomp $line;

# retrieve elements form line
my @elements = split /\s+/, $line;

# build key from elements 1 and 2 (array 0-based!)
my $key = $elements[0]." ".$elements[1];

if(! $lines{key}) {
    #my $min = max $elements[3];
    $first_line_per_group{$key} = $elements[0]."\t".$elements[1];#."\t".$min;
}
push @{ $lines{$key} }, $line;

}

# the result:
 for my $key (keys %lines) {
    print $first_line_per_group{$key}."\t";
    print "$_\n" for @{ $lines{$key} };
}

此代码的输出为:

miRNA17 dvex571195  miRNA17 dvex571195 207 230
miRNA17 dvex571195 189 229
miRNA17 dvex571195 207 241
miRNA17 dvex571195 207 230
miRNA17 dvex571195 80 111
miRNA18 dvex449276  miRNA18 dvex449276 12   43
miRNA18 dvex196735  miRNA18 dvex196735 2909 2929
miRNA18 dvex765590  miRNA18 dvex765590 183  213
miRNA22 dvex826214  miRNA22 dvex826214 206  236
miRNA23 dvex529006  miRNA23 dvex529006 111  168
miRNA24 dvex803612  miRNA24 dvex803612 317  347

但我需要消除分组值并完成上述工作。

2 个答案:

答案 0 :(得分:3)

您只缺少一件事:当$lines{key}为真时,您需要调整$first_line_per_group{$key},使其值包含组的最小值和最大值。

经过一些清理后,您最终得到以下代码:

my %groups;
while (<>) {
   my @f = split;
   my $key = "$f[0] $f[1]";

   if ($groups{$key}) {
      $f[2] = $groups{key}[2] if $groups{key}[2] < $f[2];
      $f[3] = $groups{key}[3] if $groups{key}[3] > $f[3];
   }

   $groups{$key} = \@f;
}

for my $key (keys(%groups)) {
   print(join(' ', @{ $groups{$key} }), "\n");
}

答案 1 :(得分:1)

你可能想看看这个程序,这似乎可以满足你的需要。

我不清楚您的文件是以制表符分隔还是仅以空格分隔,但只要值中没有空格就无关紧要。我已经在空格上拆分了每条记录,因为这就是你的代码所做的。

use strict;
use warnings;

use List::Util qw/ min max /;

my %data;

while (<>) {
  my @fields = split;
  push @{ $data{"@fields[0,1]"} }, [ @fields[2,3] ];
}

for my $key (sort keys %data) {
  my $val = $data{$key};
  print join(' ', $key, min(map $_->[0], @$val), max(map $_->[1], @$val)), "\n";
}

<强>输出

miRNA17 dvex571195 80 241
miRNA18 dvex196735 2909 2929
miRNA18 dvex449276 12 43
miRNA18 dvex765590 183 213
miRNA22 dvex826214 206 236
miRNA23 dvex529006 111 168
miRNA24 dvex803612 317 347