如何引用散列数组的散列以比较值

How to reference a hash of array of hashes in order to compare values

我有以下数据结构:

my %hash = (
    'hsa_circ_0024017|chr11:93463035-93463135+|NM_033395|KIAA1731  FORWARD' => [ 
        { 
          'energy' => '-4.3', 
          'spacer' => 'AGGCACC', 
          'end' => '97', 
          'start' => '81' 
        } 
    ],
    'hsa_circ_0067224|chr3:128345575-128345675-|NM_002950|RPN1  FORWARD' => [ 
        { 
          'energy' => '-4.4', 
          'spacer' => 'CAGT', 
          'end' => '17', 
          'start' => '6' 
        }, 
        { 
          'energy' => '-4.1', 
          'spacer' => 'GTT', 
          'end' => '51', 
          'start' => '26' 
        }, 
        { 
          'energy' => '-4.1', 
          'spacer' => 'TTG', 
          'end' => '53', 
          'start' => '28' 
        } 
    ],
    ...
);

如何访问哈希的内容以便能够比较循环中的内容?

对于每个父散列 (hsa_circ...),我想将子散列(间隔符)一起比较。请原谅我很难说清楚。这当然是数据的一小部分样本。简而言之,我的目标是检测具有相同间隔符的哈希数组,如果它们确实具有相同的间隔符,那么我想选择具有 最低 的哈希数组能量分数。

像这样

$_=$hash{'hsa_circ_0024017|chr11:93463035-93463135+|NM_033395|KIAA1731  FORWARD'}->[0]{energy};

print  $_ ."\n";

原来如此 $this{hash}->[array_ref]{hash]

for my $line (keys(%by_line)) {
   my $spacer_defs = $by_line{$line};
   ...
}

for my $spacer_defs (values(%by_line)) {
   ...
}

将依次为每一行获取(引用)spacer definitions 的数组(引用)。然后,您可以按如下方式编辑该数组:

   my %uniq_spacer_defs;
   for my $spacer_def (@$spacer_defs) {
      my $spacer = $spacer_def->{spacer};
      $uniq_spacer_defs{$spacer} = $spacer_defs
         if !$uniq_spacer_defs{$spacer}
         || $uniq_spacer_defs{$spacer}{energy} < $spacer_def->{energy};
   }

   @$spacer_defs = values(%uniq_spacer_defs);

备注:

  • "spacer definitions" 的顺序可能会改变。如果这是一个问题,请替换

    @$spacer_defs = values(%uniq_spacer_defs);
    

    @$spacer_defs = grep { $uniq_spacer_defs{$_->{spacer}} == $_ } @$spacer_defs;
    
  • 如果有两个具有相同间隔基和相同能量的间隔基定义,则保留遇到的第一个。

问题:每个 arrayref 中可能存在具有相等 spacer 值的 hashref 组。在每个这样的组中,hashref 具有最低的 energy 值 需要确定,以替换该组。

大部分工作是在 partition_equal() 中完成的,它识别具有相等间隔符的 hashref 组

use warnings;
use strict;
use List::Util qw(reduce);
use Data::Dump qq(dd);

# Test data: two groups of equal-spacer hashrefs, in the first array only
my %hash = (  
    kA => [
        { 'energy' => -4.3, 'spacer' => 'AGGCACC' },
        { 'energy' => -2.3, 'spacer' => 'AGGCACC' },
        { 'energy' => -3.3, 'spacer' => 'CAGT' },
        { 'energy' => -1.5, 'spacer' => 'GTT' },
        { 'energy' => -2.5, 'spacer' => 'GTT' },
    ],
    kB => [
        { 'energy' => -4.4, 'spacer' => 'CAGT' },
        { 'energy' => -4.1, 'spacer' => 'GTT' },
        { 'energy' => -4.1, 'spacer' => 'TTG' },
    ],
);
#dd \%hash;

for my $key (keys %hash) {
    my ($spv, $unique) = partition_equal($hash{$key});
    next if not $spv;
    # Extract minimum-energy hashref from each group and add to arrayref
    # $unique, so that it can eventually overwrite this key's arrayref
    foreach my $spacer (keys %$spv) {
        my $hr_min = reduce { 
            $a->{energy} < $b->{energy} ? $a : $b 
        } @{$spv->{$spacer}};
        push @$unique, $hr_min;
    }
    # new: unique + lowest-energy ones for each equal-spacer group   
    $hash{$key} = $unique  if keys %$spv;
}    
dd \%hash;

# Sort array and compare neighbouring elements (hashrefs) 
sub partition_equal {
    my $ra = shift;
    my @sr = sort { $a->{spacer} cmp $b->{spacer} } @$ra;

    # %spv:    spacer value => [ hashrefs with it ], ...
    # @unique: hasrefs with unique spacer values    
    my (%spv, @unique);

    # Process first and last separately, to not have to test for them
    ($sr[0]{spacer} eq $sr[1]{spacer})
        ? push @{$spv{$sr[0]{spacer}}}, $sr[0]
        : push @unique, $sr[0];
    for my $i (1..$#sr-1) {
        if ($sr[$i]{spacer} eq $sr[$i-1]{spacer}  or 
            $sr[$i]{spacer} eq $sr[$i+1]{spacer}) 
        {
            push @{$spv{$sr[$i]{spacer}}}, $sr[$i]
        }
        else { push @unique, $sr[$i] }
    }
    ($sr[-1]{spacer} eq $sr[-2]{spacer})
        ? push @{$spv{$sr[-1]{spacer}}}, $sr[-1]
        : push @unique, $sr[-1];

    return if not keys %spv;
    return \%spv, \@unique;
}

输出

kA => [
        { energy => -3.3, spacer => "CAGT" },
        { energy => -2.5, spacer => "GTT" },
        { energy => -4.3, spacer => "AGGCACC" },
      ],
kB => [
        { energy => -4.4, spacer => "CAGT" },
        { energy => -4.1, spacer => "GTT" },
        { energy => -4.1, spacer => "TTG" },
      ],

arrayrefs 中的顺序未维护;新的 arrayref 首先具有具有唯一间隔值的 hashref,然后是具有最低能量值的 hashref(对于具有相同间隔值的每个原始组)。

子程序按间隔值对输入进行排序,因此它可以通过简单地遍历排序后的数组并仅比较邻居来识别相等的值。这应该是相当有效的。