【问题标题】:Sort nested hash with multiple conditions使用多个条件对嵌套哈希进行排序
【发布时间】:2019-12-04 15:45:23
【问题描述】:

我对 perl 编程有点陌生,我有一个哈希值,可以这样表述:

$hash{"snake"}{ACB2}   = [70, 120];
$hash{"snake"}{SGJK}   = [183, 120];
$hash{"snake"}{KDMFS}   = [1213, 120];
$hash{"snake"}{VCS2}   = [21, 120];
...
$hash{"bear"}{ACB2}   = [12, 87];
$hash{"bear"}{GASF}   = [131, 87];
$hash{"bear"}{SDVS}   = [53, 87];
...
$hash{"monkey"}{ACB2}   = [70, 230];
$hash{"monkey"}{GMSD}   = [234, 230];
$hash{"monkey"}{GJAS}   = [521, 230];
$hash{"monkey"}{ASDA}   = [134, 230];
$hash{"monkey"}{ASMD}   = [700, 230];

hash的结构总结如下:

%hash{Organism}{ProteinID}=(protein_length, total_of_proteins_in_that_organism)

我想根据一些条件对该哈希进行排序。首先,我只想考虑那些蛋白质总数大于 100 的生物体,然后我想显示生物体的名称以及最大的蛋白质及其长度。

为此,我将采用以下方法:

    foreach my $org (sort keys %hash) {
        foreach my $prot (keys %{ $hash{$org} }) {
            if ($hash{$org}{$prot}[1] > 100) {
                @sortedarray = sort {$hash{$b}[0]<=>$hash{$a}[0]} keys %hash;

                print $org."\n";
                print @sortedarray[-1]."\n";
                print $hash{$org}{$sortedarray[-1]}[0]."\n"; 
            }
        }
    }

但是,它打印生物体名称的次数与蛋白质总数一样多,例如,它打印“蛇”120 次。此外,这不是正确排序,因为我想我应该在排序行中使用变量 $org 和 $prot。

最后,输出应该是这样的:

snake
"Largest protein": KDMFS [1213]

monkey
"Largest protein": ASMD [700]

【问题讨论】:

  • 输出应该显示所有“总蛋白质数高于 100”,如文本所述,还是只显示最大的一个,如所需的输出示例所示?跨度>

标签: perl multidimensional-array hash protein-database perl-hash


【解决方案1】:

打印中排序的所有数据

use warnings;
use strict;
use feature 'say';

use List::Util qw(max);

my %hash;   
$hash{"snake"}{ACB2}   = [70, 120];
$hash{"snake"}{SGJK}   = [183, 120];
$hash{"snake"}{KDMFS}   = [1213, 120];
$hash{"snake"}{VCS2}   = [21, 120];
$hash{"bear"}{ACB2}   = [12, 87];
$hash{"bear"}{GASF}   = [131, 87];
$hash{"bear"}{SDVS}   = [53, 87];    
$hash{"monkey"}{ACB2}   = [70, 230];
$hash{"monkey"}{GMSD}   = [234, 230];
$hash{"monkey"}{GJAS}   = [521, 230];
$hash{"monkey"}{ASDA}   = [134, 230];
$hash{"monkey"}{ASMD}   = [700, 230];

my @top_level_keys_sorted = 
    sort {   
        ( max map { $hash{$b}{$_}->[0] } keys %{$hash{$b}} ) <=> 
        ( max map { $hash{$a}{$_}->[0] } keys %{$hash{$a}} )
    }   
    keys %hash;

for my $k (@top_level_keys_sorted) {
    say $k; 
    say "\t$_ --> @{$hash{$k}{$_}}" for 
        sort { $hash{$k}{$b}->[0] <=> $hash{$k}{$a}->[0] } 
        keys %{$hash{$k}};
}

根据要求,这首先按 arrayref 值中的第一个数字对顶级键进行排序。有了这个排序的键列表,我们然后进入每个键的 hashref 并进一步排序。该循环是我们要调整的,以根据需要限制输出(前 100 个按总数计算,仅按长度计算最大,等等)。

打印出来

蛇 KDMFS --> 1213 120 新加坡--> 183 120 ACB2 --> 70 120 VCS2 --> 21 120 猴 ASMD --> 700 230 GJAS --> 521 230 GMSD --> 234 230 阿斯达 --> 134 230 ACB2 --> 70 230 熊 天然气-> 131 87 SDVS --> 53 87 ACB2 --> 12 87

我不知道输出是应该显示所有“蛋白质总数高于 100 的有机体”(文本)还是只显示最大的一个(所需的输出),所以我将全部保留其中。根据需要切断。要仅获取最大的,请比较循环中每个键的最大值或查看this post(同样的问题)。

请注意,哈希本身不能“排序”,因为它本质上是无序的。但是我们可以像上面一样打印出排序的东西,或者在需要时生成可以排序的辅助数据结构。

【讨论】:

  • @FernandoDelgadoChaves 如果您需要 cmets 或解释,请告诉我。这会对您的完整列表进行排序,因为我不确定您到底想要什么(如我的回答中所述),但我希望很容易修改它以仅打印您想要的内容。如果没有,请澄清,我可以编辑。
  • 我不熟悉映射哈希,但我想这可行!没有外部包就没有办法吗?
  • @FernandoDelgadoChaves “映射”是在 perl 中处理列表的标准方法——类似于 out = map { ... } in,其中该块 {...} 中的代码应用于 in 的每个元素以及那些转换后的元素形成out。这使我们可以直接使用sort 解决整个问题——无需迭代元素等。
  • @FernandoDelgadoChaves List::Util 是现代 Perl 版本的核心,因此无需安装它。但是,一般来说:是的,您可以“无需外部包”来实现——可以编写一个例程来查找列表的最大元素,而不是使用现有的max(来自@987654330 @)。这很愚蠢,也是一条疯狂之路——对于许多工作来说,实际上有必要使用“外部”库(在所有编程语言中)。如果安装软件包有问题,最好解决。在这种情况下,List::Util 例程很容易自己编写。
  • @FernandoDelgadoChaves 请注意,如果您工作的地方存在管理问题,您可以以用户身份安装软件包。有一个系统如何做到这一点,这很简单,并且在许多 Stackoverflow 帖子中都有解释。我知道这样做也会分散注意力,但是如果您使用 Perl,那么这样做一次 是非常值得的。库(“包”)非常有用,而且通常是必需的。
【解决方案2】:

你在用吗

use strict;
use warnings;

在脚本的开头?这样至少会突出一些问题的根源。没有它们,Perl 会默默地做一些很容易被指出为愚蠢、无意义甚至最有可能是编程错误的事情。


任务

$hash{"snake"}{ACB2}   = (70, 120);

只会赋值120,因为赋值需要一个标量,但左边有一堆值。

要分配一个arrayref,你必须明确声明它:

$hash{"snake"}{ACB2}   = [70, 120];

你似乎不再使用印记($,@,%)。

  • 如果要处理标量或单个数组或哈希值,请使用 $
  • 如果要处理数组(或数组或哈希切片(多个值),请使用 @;例如,@array[0,2] 将返回数组 @array 中的第一项和第三项)。
  • 如果要处理哈希,请使用 %

所以

@sortedarray[-1]

应该是

$sortedarray[-1]

因为您只访问一个值。

【讨论】:

  • 嗨,Silvar,我使用的是严格和警告,并且在引用列表位置时还使用 [] 代替 (),还使用 ​​$ 代替 @。我纠正了这些错误,请注意这是实际问题的简化版本。
【解决方案3】:

如果我对您的理解正确,那么下面的代码应该符合您的预期。我将结果保存在哈希中,您可以随意以您想要的任何形式打印数据

use strict;
use warnings;

use Data::Dumper;

my $debug = 1;

my %data;
my $totalProteinsSearch = 100;

while( <DATA> ) {
    chomp;
    my @row = split ',';

    $data{$row[0]}{$row[1]} = { proteinLength => $row[2], totalProteins => $row[3] };
}

print Dumper(\%data) if $debug == 1;

my %result;

while( my($organism,$value) = each %data ) {
    while( my($proteinID, $data) = each %{$value} ) {
        next if $data->{totalProteins} < $totalProteinsSearch;
        $result{$organism} = {
                                proteinID => $proteinID,
                                proteinLength => $data->{proteinLength}, 
                                totalProteins => $data->{totalProteins} 
                            }
            if not defined $result{$organism}
                or 
            $data->{proteinLength} > $result{$organism}{proteinLength};
    }
}

print Dumper(\%result) if $debug;

__DATA__
snake,ACB2,70,120
snake,SGJK,183,120
snake,KDMFS,1213,120
snake,VCS2,21,120
bear,ACB2,12,87
bear,GASF,131,87
bear,SDVS,53,87
monkey,ACB2,70,230
monkey,GMSD,234,230
monkey,GJAS,521,230
monkey,ASDA,134,230
monkey,ASMD,700,230

您可以打印如下信息,例如 [关闭调试$debug = 0]

while( my($organism,$data) = each %result ) {
    printf "%s\nLargetst protein: %s [%d]\n\n",
            $organism, 
            $data->{proteinID},
            $data->{proteinLength};
}

【讨论】:

  • 看起来您正在使用该 while() 循环读取文件并同时创建哈希。我没有提到这样的事情,但是我从一个 .txt 文件中获取了我的哈希值,该文件创建了我在问题中提到的结构。然后你创建另一个哈希来显示结果?是不是太长了?有没有办法拥有一个唯一的哈希并显示它的特定信息?此外,生物体名称只能出现一次,生物体内的蛋白质总数不应出现。提前谢谢!
  • 您没有提供带有您读取的数据的.txt 文件样本。我已经从 __DATA__ 块重新创建了哈希 - 我的猜测是您的 .txt 文件看起来非常相似。然后我分析数据并将结果存储在新的哈希中,该哈希保存符合您要求的有机体的唯一记录。您在评论中问难道没有一种方法可以拥有唯一的哈希并显示它的特定信息吗?如果您打开变量$debug = 0,那么您将看不到任何东西。我使用print Dumper(...) 来演示读取和存储的结果。我已经提到你可以以任何形式打印%result
  • 好吧,我尝试在没有 while() 循环的情况下执行并提供哈希值,就像我在问题中写的那样。我收到以下错误:在 ejemlo1.pl 第 31 行不是 HASH 引用。这与此行相关: next if $data->{totalProteins}
【解决方案4】:

您可以使用List::Util reduce 来获取每个生物体的最大值。

我知道您可能不熟悉 List::Util 中的这个函数,但对于这个问题的示例,它非常简单。

保存数据的结构的组成是一个数组的散列,其中有机体作为键,整行存储在数组引用中作为散列的值。

数据结构的选择部分取决于所需输出的形式。

#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw/reduce/;

use constant {org_idx => 0, prot_idx => 1, len_idx => 2, cnt_idx => 3};

my %stuff;

while (<DATA>) {
    chomp;
    my @data = split /,/;

    # saving all 4 items (per line)
    # key is the organism
    push @{$stuff{$data[org_idx]}}, \@data; 
}

for my $org (sort keys %stuff) {
    my $aref = $stuff{$org}; # an array of arrays reference

    # to find the record with the max length
    # $max becomes an array reference containing all 4 of the items as a list
    my $max = reduce{$a->[len_idx] > $b->[len_idx] ? $a : $b} @$aref;

    next if $max->[cnt_idx] < 100;

    print $org, "\n";
    print "Largest protein: $max->[prot_idx] [$max->[len_idx]]\n";
}



__DATA__
snake,ACB2,70,120
snake,SGJK,183,120
snake,KDMFS,1213,120
snake,VCS2,21,120
bear,ACB2,12,87
bear,GASF,131,87
bear,SDVS,53,87
monkey,ACB2,70,230
monkey,GMSD,234,230
monkey,GJAS,521,230
monkey,ASDA,134,230
monkey,ASMD,700,230

打印

monkey
Largest protein: ASMD [700]
snake
Largest protein: KDMFS [1213]

%stuff的数据::Dumper

$VAR1 = {
          'monkey' => [
                        [
                          'monkey',
                          'ACB2',
                          70,
                          '230'
                        ],
                        [
                          'monkey',
                          'GMSD',
                          234,
                          '230'
                        ],
                        [
                          'monkey',
                          'GJAS',
                          521,
                          '230'
                        ],
                        [
                          'monkey',
                          'ASDA',
                          134,
                          '230'
                        ],
                        [
                          'monkey',
                          'ASMD',
                          700,
                          '230'
                        ]
                      ],
          'bear' => [
                      [
                        'bear',
                        'ACB2',
                        12,
                        '87'
                      ],
                      [
                        'bear',
                        'GASF',
                        131,
                        '87'
                      ],
                      [
                        'bear',
                        'SDVS',
                        53,
                        '87'
                      ]
                    ],
          'snake' => [
                       [
                         'snake',
                         'ACB2',
                         70,
                         '120'
                       ],
                       [
                         'snake',
                         'SGJK',
                         183,
                         '120'
                       ],
                       [
                         'snake',
                         'KDMFS',
                         1213,
                         '120'
                       ],
                       [
                         'snake',
                         'VCS2',
                         21,
                         '120'
                       ]
                     ]
        };

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-05
    • 1970-01-01
    • 1970-01-01
    • 2017-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    相关资源
    最近更新 更多