通过内部哈希值对哈希散列进行排序

时间:2015-08-13 13:04:55

标签: perl sorting hash

我有哈希哈希

my %change;

while ( <DATA> ) {
    chomp;
    my ($gene, $condition, $change) = split;
    $change{$gene}{$condition} = $change;
}

print Dumper \%change;

__DATA__
gene1   condition1  10
gene2   condition1  0.5
gene3   condition1  1.5
gene1   condition2  2
gene2   condition2  13.5
gene3   condition2  0.25

我想按价值对其进行排序:

gene2   condition2  13.5
gene1   condition1  10
gene1   condition2  2
gene3   condition1  1.5
gene2   condition1  0.5
gene3   condition2  0.25

我正在使用:

for my $g (keys %change){
    for my $con (keys $change{$g}){
        for my $ch (sort { $change{$g}{$a} <=> $change{$g}{$b} } keys $change{$g}{$con} ) {
            print "$g\t$con\t$ch\n";
        }

    }
}

但这不起作用,并产生错误

  

引用键上的参数类型必须是unsitled hashref或arrayref at untitled.pl第23行第6行。

第23行

for my $ch (sort { $change{$g}{$a} <=> $change{$g}{$b} } keys $change{$g}{$con}){

有人能指出我正确的方向吗?

4 个答案:

答案 0 :(得分:5)

我认为你不太可能需要像这样的哈希结构中的数据。当然,出于此任务的目的,您最好使用数组数组

use strict;
use warnings;

my @change;

while ( <DATA> ) {
    push @change, [ split ];
}

print "@$_\n" for sort { $b->[2] <=> $a->[2] } @change;


__DATA__
gene1   condition1  10
gene2   condition1  0.5
gene3   condition1  1.5
gene1   condition2  2
gene2   condition2  13.5
gene3   condition2  0.25

输出

gene2 condition2 13.5
gene1 condition1 10
gene1 condition2 2
gene3 condition1 1.5
gene2 condition1 0.5
gene3 condition2 0.25

如果您解释数据需要什么样的访问权限,那么我相信有更好的选择。例如,我建议%gene%condition哈希将基因或条件ID映射到使用该基因的数组元素列表。然后,当您知道基因或条件时,您可以访问数据

答案 1 :(得分:4)

正如我在评论中提到的,最简单的解决方案是不首先将文本输入解析为散列,然后对散列进行排序,而是将数据收集到更合适的形式并在那里进行排序。

另外,请注意,在迭代值时无法进行排序。您需要编译一个列表,并同时对该列表进行排序,因为sort本身就是一种迭代器。

我首先展示了我给出的输入方法的选择,然后是如何对哈希进行排序。

use strict;
use warnings;

my %change;
my @sort;
while(<DATA>) {
    chomp;
    my ($gene, $condition, $change) = split;
    $change{$gene}{$condition} = $change;
    push @sort, [ $change, $_ ];
}

@sort = sort { $a->[0] <=> $b->[0] } @sort;
say $_->[1] for @sort;

# Using the hash:

my @values;
for my $gene (keys %change) {
    for my $con (keys %{ $change{$gene} }) {
        my $num = $change{$gene}{$con};
        push @values, [ $num, "$gene\t$con\t$num" ];
    }
}
@values = sort { $a->[0] <=> $b->[0] } @values;
say $_->[1] for @values;

__DATA__
gene1   condition1  10
gene2   condition1  0.5
gene3   condition1  1.5
gene1   condition2  2
gene2   condition2  13.5
gene3   condition2  0.25

正如您所看到的,我正在使用一种缓存来更轻松地访问该值。例如,push @sort, [ $change, $_ ]存储带有数值的数组ref,以及来自输入的原始字符串。然后可以在排序时使用$a->[0]访问这些值,并在打印时使用$_->[1]

我发现这种方法简单而强大。虽然如果输入文件非常大,但由于数据重复,可能会导致一些内存问题。但是在现代系统中,任何小于千兆字节的东西都应该没问题。

答案 2 :(得分:3)

您可以展平哈希结构,然后按值(数组数组中的最后一个元素)按数字排序

my $VAR1 = {
      'gene1' => {
                   'condition1' => '10',
                   'condition2' => '2'
                 },
      'gene2' => {
                   'condition1' => '0.5',
                   'condition2' => '13.5'
                 },
      'gene3' => {
                   'condition1' => '1.5',
                   'condition2' => '0.25'
                 }
    };

my @sorted = sort {
    $b->[2] <=> $a->[2]
  }
  map {
    my $k = $_;
    my $h = $VAR1->{$k};
    map [ $k, $_, $h->{$_} ], keys %$h;
  }
  keys %$VAR1;

print "@$_\n" for @sorted;

输出

gene2 condition2 13.5
gene1 condition1 10
gene1 condition2 2
gene3 condition1 1.5
gene2 condition1 0.5
gene3 condition2 0.25

使用foreach代替map

my @arr;
for my $k (keys %$VAR1) {
  my $h = $VAR1->{$k};
  for (keys %$h) {
    push @arr, [ $k, $_, $h->{$_} ];
  }
}
my @sorted = sort { $b->[2] <=> $a->[2] } @arr;

答案 3 :(得分:3)

你只有两个深度,所以

  • %change是哈希。
  • $change{$g}是对哈希
  • 的引用
  • %{ $change{$g} }是哈希。
  • $change{$g}{$con}是一个数字。
  • 据报道,
  • %{ $change{$g}{$con} }是错误。

修复是......好吧,没有修复。您采用的方法无法用于解决您的问题。

您无法对哈希进行排序。您可以对哈希的键进行排序,但这不是您想要的。您将要对密钥对进行排序。首先,您必须创建这些密钥对。

map {
   my $outer_key = $_;
   map {
      my $inner_key = $_;
      [ $outer_key, $inner_key ]
   } keys %{ $change{$_} }
} keys(%change)

这会创建

[
   [ 'gene1', 'condition1' ],
   [ 'gene1', 'condition2' ],
   [ 'gene2', 'condition1' ],
   [ 'gene2', 'condition2' ],
   [ 'gene3', 'condition1' ],
   [ 'gene3', 'condition2' ],
]

当我们对它们进行排序时

sort { $change{ $a->[0] }{ $a->[1] } <=> $change{ $b->[0] }{ $b->[1] }

所有在一起:

for (
   sort { $change{ $a->[0] }{ $a->[1] } <=> $change{ $b->[0] }{ $b->[1] }
   map {
      my $gene = $_;
      map {
         my $con = $_;
         [ $gene, $con ]
      } keys %{ $change{$_} }
   } keys(%change)
) {
   my ($gene, $con) = @$_;
   print("$g\t$con\t$change{$gene}{$con}\n");
}

但是,如果我们创建了以下扁平化结构呢?

[
   [ 'gene1', 'condition1', 10    ],
   [ 'gene1', 'condition2',  2    ],
   [ 'gene2', 'condition1',  0.5  ],
   [ 'gene2', 'condition2', 13.5  ],
   [ 'gene3', 'condition1',  1.5  ],
   [ 'gene3', 'condition2',  0.25 ],
]

这可以让我们简化一些。

for (
   sort { $a->[2] <=> $b->[2] }
   map {
      my $gene = $_;
      map {
         my $con = $_;
         [ $gene, $con, $change{$gene}{$con} ]
      } keys %{ $change{$_} }
   } keys(%change)
) {
   my ($gene, $con, $ch) = @$_;
   print("$g\t$con\t$ch\n");
}