Perl 使用多个条件对嵌套哈希进行排序

Perl 使用多个条件对嵌套哈希进行排序,perl,multidimensional-array,hash,protein-database,perl-hash,Perl,Multidimensional Array,Hash,Protein Database,Perl Hash,我对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]; $

我对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];
snake
"Largest protein": KDMFS [1213]

monkey
"Largest protein": ASMD [700]
哈希的结构总结如下:

%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"; 
            }
        }
    }
你在用什么

use strict;
use warnings;
在你剧本的开头?至少一些问题的根源会以这种方式突出。如果没有它们,Perl将默默地做一些很容易被指出是愚蠢、毫无意义甚至最有可能是编程错误的事情

任务

$hash{"snake"}{ACB2}   = (70, 120);
将仅赋值120,因为赋值需要标量,但左侧有一组值

要分配arrayref,必须显式声明它:

$hash{"snake"}{ACB2}   = [70, 120];
您对符号$、@、%的使用似乎不正确

如果要处理标量、单个数组或散列值,请使用$。 如果要处理数组或数组或哈希切片多个值,请使用@;e、 g.@array[0,2]将返回数组@array中的第一项和第三项。 如果要处理哈希,请使用%。 所以

应该是

$sortedarray[-1]
因为您只访问一个值。

是否使用

use strict;
use warnings;
在你剧本的开头?至少一些问题的根源会以这种方式突出。如果没有它们,Perl将默默地做一些很容易被指出是愚蠢、毫无意义甚至最有可能是编程错误的事情

任务

$hash{"snake"}{ACB2}   = (70, 120);
将仅赋值120,因为赋值需要标量,但左侧有一组值

要分配arrayref,必须显式声明它:

$hash{"snake"}{ACB2}   = [70, 120];
您对符号$、@、%的使用似乎不正确

如果要处理标量、单个数组或散列值,请使用$。 如果要处理数组或数组或哈希切片多个值,请使用@;e、 g.@array[0,2]将返回数组@array中的第一项和第三项。 如果要处理哈希,请使用%。 所以

应该是

$sortedarray[-1]

因为您只访问一个值。

如果我正确理解了您的意思,那么下面的代码应该满足您的期望。我将结果保存在散列中,可以随心所欲地以任何形式打印数据

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

如果我正确地理解了您的意思,那么下面的代码应该满足您的期望。我将结果保存在散列中,可以随心所欲地以任何形式打印数据

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

打印中排序的所有数据

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个,长度的最大值,等等

它打印

snake KDMFS --> 1213 120 SGJK --> 183 120 ACB2 --> 70 120 VCS2 --> 21 120 monkey ASMD --> 700 230 GJAS --> 521 230 GMSD --> 234 230 ASDA --> 134 230 ACB2 --> 70 230 bear GASF --> 131 87 SDVS --> 53 87 ACB2 --> 12 87 我不知道输出是应该显示所有蛋白质总数超过100个文本的生物体,还是只显示最大的一个所需输出,所以我将全部保留。根据需要切断电源。要仅获取最大值,请比较循环中每个关键点的最大值,或者查看相同的问题


请注意,哈希本身无法排序,因为它本身就是无序的。但是,我们可以像上面那样将排序后的数据打印出来,或者根据需要生成可以排序的辅助数据结构。

所有以打印方式排序的数据

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个,长度的最大值,等等

它打印

snake KDMFS --> 1213 120 SGJK --> 183 120 ACB2 --> 70 120 VCS2 --> 21 120 monkey ASMD --> 700 230 GJAS --> 521 230 GMSD --> 234 230 ASDA --> 134 230 ACB2 --> 70 230 bear GASF --> 131 87 SDVS --> 53 87 ACB2 --> 12 87 我不知道输出是应该显示所有蛋白质总数超过100个文本的生物体,还是只显示最大的一个所需输出,所以我将全部保留。根据需要切断电源。要仅获取最大值,请比较循环中每个关键点的最大值,或者查看相同的问题

请注意,哈希本身无法排序,因为它本身就是无序的。但是,我们可以像上面那样打印出已排序的内容,或者生成辅助数据结构,如果需要,可以对其进行排序。

您可以使用它来获得每个生物体的最大值

我知道您可能不熟悉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
数据::%stuff的转储程序

你可以用它来获得每个有机体的最大值

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

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

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

#!/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
数据::%stuff的转储程序


嗨,西尔瓦,我在引用一个列表帖子时使用了strict和warnings,还有[]代替,还有$代替@。我纠正了这些错误,请注意,这是实际问题的简化版本。嗨,西尔瓦,我在引用列表位置时使用了strict和warnings,还有[]代替,还有$代替@。我纠正了这些错误,请注意这是实际问题的简化版本。看起来您正在使用while循环读取一个文件,并在此期间创建哈希。我没有提到这样的事情,但是我从一个.txt文件中得到了哈希值,该文件创建了我在问题中提到的结构。然后创建另一个哈希来显示结果?那不是太长了吗?难道没有一种方法可以拥有一个唯一的散列并显示它的特定信息吗?此外,生物体名称只应出现一次,生物体内的蛋白质总数不应出现。提前谢谢你!您没有提供包含所读取数据的.txt文件示例。我已经从_数据_块重新创建了散列-我猜你的.txt文件看起来很相似。然后我分析数据并将结果存储在新的散列中,该散列保存符合你要求的生物体的唯一记录。你在评论中问,这不是一种拥有唯一散列并显示其特定信息的方法吗?如果将变量$debug=0设置为0,则不会看到任何内容。我使用打印转储程序。。。演示读取和存储的结果。我已经提到你可以以任何形式打印%result。我试着在没有while循环的情况下执行,并在问题中编写时提供哈希。我得到以下错误:在ejempo1.pl第31行没有哈希引用。whcih与该行相关:下一个if$data->{totalProteins}<$totalProteinsSearch;问题在于totalProteins部分…看起来您正在使用while循环读取一个文件,并在此期间创建哈希。我没有提到这样的事情,但是我从一个.txt文件中得到了哈希值,该文件创建了我在问题中提到的结构。然后创建另一个哈希来显示结果?那不是太长了吗?难道没有一种方法可以拥有一个唯一的散列并显示它的特定信息吗?此外,生物体名称只应出现一次,生物体内的蛋白质总数不应出现。提前谢谢你!您没有提供包含所读取数据的.txt文件示例。我已经从_数据_块重新创建了散列-我猜你的.txt文件看起来很相似。然后我分析数据并将结果存储在新的散列中,该散列保存符合你要求的生物体的唯一记录。你在评论中问,这不是一种拥有唯一散列并显示其特定信息的方法吗?如果将变量$debug=0设置为0,则不会看到任何内容。我使用打印转储程序。。。演示读取和存储的结果。我已经提到你可以以任何形式打印%result。我试着在没有while循环的情况下执行,并在问题中编写时提供哈希。我得到以下错误:在ejempo1.pl第31行没有哈希引用。whcih与该行相关:下一个if$data->{totalProteins}<$totalProteinsSearch;问题在于totalProteins部分…输出是显示文本中蛋白质总数大于100的all,还是仅显示最大的all,如所需输出示例中所示?输出是显示文本中蛋白质总数大于100的all,还是仅显示最大的all,如所需的输出示例所示?@Fernandodelgadocaves如果您需要此处的注释或解释,请告诉我。这将对您的完整列表进行排序,因为我不确定您在我的回答中到底想要什么,但我希望很容易修改它以只打印您想要的内容。如果没有,请澄清,我可以编辑。我不熟悉映射哈希,但我想这是可行的!在没有外部包的情况下,难道没有办法做到这一点吗?@Fernandodelgadocaves映射是用perl处理列表的一种标准方法——类out=map{…},其中块{…}中的代码应用于in的每个元素,而那些转换后的元素形成out。这使我们能够直接使用sort解决整个问题,而不必迭代元素等。@FernandoDelgadoChaves List::Util是现代Perl版本的核心,因此无需安装它。但是,一般来说:是的,您可以不使用外部包就可以完成这项工作—可以编写一个例程来查找列表中最大的元素,而不是使用现有的max-from-list::Util。这很愚蠢,而且是一条疯狂之路——对于许多工作来说,在所有编程语言中使用外部库实际上是必要的。如果安装软件包时出现问题,最好解决。在这个
case,List::Util例程很容易自己编写。@FernandoDelgadoChaves注意,如果您工作的地方存在管理问题,您可以作为用户安装软件包。有一个系统如何做到这一点,这是很容易的,并解释了许多Stackoverflow职位。我知道这样做也会分散注意力,但如果您使用Perl,那么这一次就值得一试。库软件包非常有用,而且经常是必需的。@FernandoDelgadoChaves如果您需要这里的评论或解释,请告诉我。这将对您的完整列表进行排序,因为我不确定您在我的回答中到底想要什么,但我希望很容易修改它以只打印您想要的内容。如果没有,请澄清,我可以编辑。我不熟悉映射哈希,但我想这是可行的!在没有外部包的情况下,难道没有办法做到这一点吗?@Fernandodelgadocaves映射是用perl处理列表的一种标准方法——类out=map{…},其中块{…}中的代码应用于in的每个元素,而那些转换后的元素形成out。这使我们能够直接使用sort解决整个问题,而不必迭代元素等。@FernandoDelgadoChaves List::Util是现代Perl版本的核心,因此无需安装它。但是,一般来说:是的,您可以不使用外部包就可以完成这项工作—可以编写一个例程来查找列表中最大的元素,而不是使用现有的max-from-list::Util。这很愚蠢,而且是一条疯狂之路——对于许多工作来说,在所有编程语言中使用外部库实际上是必要的。如果安装软件包时出现问题,最好解决。在这种情况下,List::Util例程很容易自己编写。@Fernandodelgadocaves注意,如果您工作的地方存在管理问题,您可以作为用户安装软件包。有一个系统如何做到这一点,这是很容易的,并解释了许多Stackoverflow职位。我知道这样做也会分散注意力,但如果您使用Perl,那么这一次就值得一试。库包非常有用,而且经常是必需的。