Perl:比较多个哈希值的哈希键

时间:2013-07-18 11:00:49

标签: perl hash

我不知道我是否正好面对这个问题。我有一个带有标识的文件,然后有10个文件,其中一些带有数据库名称的标识(每个标识相同但文件之间不同)。我要做的是将这10个文件的所有ID与只有标识的文件匹配,除非先前已经匹配了标识。

这10个文件是这样的:

File 1:
Id   Data Data Data Database_name 
Id1  ...  ...  ...    GenBank
...
Id20 ...  ...   ...   GenBank

File 2: 
Id   Data  Data Data Database_name
Id2  ...   ...  ...     IMG
Id30 ...   ...  ...     IMG
...

对于每个文件,我将这两个值(Id和Database_name)放在双键哈希中。使用此代码:

if ( -e "result_GenBank" ){
    print "Yes, it exist!!!! \n";
    open FILE,'<', "result_GenBank" or die "Error Importing GenBank";
    while (my $line=<FILE>){
        chomp ($line);
        my($ClustId, $M5, $Identity, $Evalue, $Bit_score, $Id, $Protein, $Specie, $DB ) = split /\t/g, $line; 

        $GenBank{$ClustId}{$DB}=1;
    }
    close FILE;
}

if ( -e "result_KEEG" ){
    print "Yes, it exist!!!! \n";
    open FILE,'<', "result_KEEG" or die "Error Importing KEEG";
    while (my $line=<FILE>){
        chomp ($line);
        my($ClustId, $M5, $Identity, $Evalue, $Bit_score, $Id, $Protein, $Specie, $DB ) = split /\t/g, $line; 

        $KEEG{$ClustId}{$DB}=1;
    }
    close FILE;
}

对于只有Ids的文件,我也把它放在哈希:

 open FILE,'<', "Ids" or die "No Input";
while (my $line=<FILE>){
    chomp ($line);
    $key=$line;
    $total_ID{$key} = 1;

}
close FILE;

现在,我需要一个循环,将每个双键哈希(Id和DB_name)与只有一个键(Id)的哈希进行比较。如果Id匹配,则打印Id和Db_name,除非之前已匹配Id,以避免与两个不同的Db_names具有相同的Id。

1 个答案:

答案 0 :(得分:1)

首先,您声明要对ID-DB对进行重复数据删除,以便每个ID仅与一个DB关联。因此我们可以采取捷径并做

$GenBank{$ClustId} = $DB;

在建立哈希时。

其次,%GenBank%KEEG哈希基本上是同一数据结构的一部分。这些变量的命名表明您实际上希望它们是更大哈希中的条目。然后,我们还可以删除那个可怕的代码重复:

use feature 'say'; use autodie;

my @files = qw/GenBank KEEG/; # the physical files have e "result_" prefix

my %tables;
for my $file (grep { -e "result_$_" } @files ) {
    say STDERR "The $file file was found";
    open my $fh, '<', "result_$file";

    while (<$fh>){
        chomp;
        my($ClustId, $M5, $Identity, $Evalue, $Bit_score, $Id, $Protein, $Specie, $DB ) = split /\t/; 
        $table{$file}{$ClustId} = $DB;
    }
}

但是等一下:如果我们想稍后统一ID,我们可以将它们保存在同一个哈希中!此外,当前代码允许给定ID的最后一个DB条目胜出;我们想要改变它,以便记住第一个条目。自perl5 v10以来//已定义或运算符可用,这很容易。

my %DB_by_ID;
for my $file (grep { -e "result_$_" } qw/GenBank KEEG/ ) {
    ...;
    while (<$fh>){
        ...;
        $DB_by_ID{$ClustId} //= $DB;
    }
}

我的第三点是你的ID文件代表一个数组,而不是一个哈希。如果您要对Ids文件中的条目进行重复数据删除,那么通常最好使用uniq中的List::MoreUtils

use List::MoreUtils 'uniq';

my @IDs;

open my $fh, "<", "Ids"; # no error handling neccessary with autodie
while (<$fh>) {
  chomp;
  push @IDs, $_;
}

@IDs = uniq @IDs;

我必须承认上面的代码看起来非常愚蠢。这就是我们使用File::Slurp

的原因
use List::MoreUtils 'uniq';
use File::Slurp;

my @IDs = uniq read_file('Ids', chomp => 1);

现在剩下要做的就是使用%DB_by_ID中给出的ID迭代@IDs表,然后打印出结果。这看起来像

for my $id (@IDs) {
  if (not exists $DB_by_ID{$id}) {
    warn "no entry for ID=$id";
    next;
  }
  say join "\t", $id, $DB_by_ID{$id};
}