在文件/数组中,搜索散列键,并将其替换为散列值,对所有散列键/值执行此操作

时间:2013-07-05 19:28:08

标签: perl search hash replace bioinformatics

我在网站上搜索过,令人惊讶的是我似乎无法找到适用于我的特定问题的东西。所以我想我会发布它,看看你们中有些经验丰富的程序员如何解决问题。

我有一个像文本文件一样的电子表格(许多带有制表符分隔列的行),我想搜索某些标签(ex scaffold1253.1_size81005.6.32799_7496)并用更简化的标签替换它们(ex scaffold1253.1a) )。这些标签仅位于文本文件的第一列。我已经编写了脚本,以便我将旧标签的哈希作为与新标签对应的键作为各自的值。这个哈希有大约26000行。所以基本上我想把哈希键1做1,在文本文件中搜索它们,并用它们各自的哈希值替换它们。

我有一个非常好的服务器可用,所以如果它太复杂,使其第一列专门加速过程那么没关系。

这就是我到目前为止所做的:

 use warnings;  



$gtf = './Hc_genome/Hc_rztk_1+2+8+9.augustus.gtf'; 
    open(FASTAFILE2, $gtf);
    @gtfarray = <FASTAFILE2>;
    #print @gtfarray;


my %hash;
while (<>)
{
   chomp;
   my ($key, $val) = split /\t/;
   $hash{$key} .= exists $hash{$key} ? ",$val" : $val;
}

#print %hash;

while (my ($find, $replace) = each %hash) {
    foreach (@gtfarray){
        $_ =~ s/$find/$replace/g;
        push @newgtf, $_;   
    }
}
print @newgtf;

此代码似乎不起作用,因为它没有完成。我很确定这是foreach循环结构的问题。对不起,我不知道有任何其他方法可以做到这一点。有没有人有更好的方法来运行这个文件并进行替换?

任何输入都将非常感谢! 谢谢,

安德鲁

@DVK

这是你的mods的完整脚本,你的while循环会遇到语法错误,不知道为什么它不接受它?再次感谢!

use warnings;  

$gtf = './Hc_genome/Hc_rztk_1+2+8+9.augustus.gtf'; 
    open(FASTAFILE2, $gtf);

my %hash;
while (<>){
    chomp;
    my ($key, $val) = split /\t/;
    $hash{$key} .= exists $hash{$key} ? ",$val" : $val;
}


while $line (<FASTAFILE2>){
    my @fields = split(/\t/, $line);
    # If you only care about first column, don't need the foreach loop below;
    #    just do the loop insides on $fields[0]
    foreach my $field (@fields) {
        $field = $hash{$field} if exists $hash{$field};
        print $outfile "$field\t"; # Small bug - will print training \t
    }
    print $outfile "\n"
}

__END__

这是语法错误: perl gtf_mod2.pl&lt; ./ Hc_genome / header_file.txt gtf_mod2.pl第14行的语法错误,靠近“while $ line” gtf_mod2.pl第23行的语法错误,靠近“}” 由于编译错误,gtf_mod2.pl的执行中止。

4 个答案:

答案 0 :(得分:1)

使用初始$find$replace键/值对,您第一次在循环中耗尽文件。

有两种可能的解决方案:

  1. 在while循环的每次迭代期间打开文件进行阅读(费用昂贵)
  2. 将foreach循环移动到while的外部并每次迭代散列(更便宜)
  3. 示例:

    REPLACE:
    for my $line (@gtfarray) {
       while(my ($find, $replace) = each %hash) {
          if($line =~ s/$find/$replace/g) {
             push @newgtf, $line;
             next REPLACE; # skip to next iteration
          }
       }
       # if there was no replacement, push the old line
       push @newgtf, $line
    }  
    

答案 1 :(得分:1)

您要替换第一列的文件有多大?

如果它是> 50,000行,你最好不要做反向

  • 迭代一次哈希文件,并将该哈希值存储在内存中

  • 通过主文件的一次,然后对每行,每列,发现在存储的哈希值,如果发现有哈希值替换,和写入。

    迭代

换句话说,删除第一个@gtfarray = <FASTAFILE2>;并用:

替换你的最后一个while循环
while my $line (<FASTAFILE2>) {
    my @fields = split(/\t/, $line);
    # If you only care about first column, don't need the foreach loop below;
    #    just do the loop insides on $fields[0]
    foreach my $field (@fields) {
        $field = $hash{$field} if exists $hash{$field};
        print $outfile "$field\t"; # Small bug - will print training \t
    }
    print $outfile "\n";
}

请注意:我做一个假设,即字段包含的散列键的全部内容(例如,您的数据文件将包含与“scaffold1253.1_size81005.6.32799_7496”一个领域,但不是可以在现场与“XYZscaffold1253.1_size81005.6.32799 _7496 ___ IOU“)。

如果这个假设是错误的,你确实需要运行一个正则表达式,因为你的脚手架串可以包含在更长的字符串,可能仍存在运行O(N * M)一个更好的解决方案一旁的正则表达式:如果你的脚手架串都是有一定知名度定义的格式(例如,“scaffoldNNNNN.NNN_sizeNNNNN.NNN.NNNN_NNNN”),你需要做的是什么:

  • 对于每一行数据文件,运行单个正则表达式查找该模式,整个模式位于捕获组括号内:

    @matches = ($line =~ m/(scaffold\d+\.\d+_size\d+\.\d+\.\d+_\d+/g );
    
  • 然后,在哈希中查找@matches数组的每个值。如果找到,则仅将匹配作为s ///正则表达式运行。

答案 2 :(得分:0)

这可能是Tie::File的工作吗?假设,也就是说,数据文件可以作为数组进行操作。

use Tie::File; 

my $file = "./Hc_genome/Hc_rztk_1+2+8+9.augustus.gtf"; 

tie @lines, 'Tie::File', $file or die ;
for (@lines) {
 s/Oldlabel/NewLable/g;   # Change this to fit
}

untie @lines ;

Tie::File做了一系列技巧来保持对文件内存的“到位”更改效率。

答案 3 :(得分:0)

查看您的previous post,在阅读文件时创建缩短的“id”不是更简单。那你就不需要你得到哈希的其他文件了吗?

以下是(未经测试的)代码。 (需要将print语句指向命令行上的输出文件或打开文件以便在脚本中写入。)

#!/usr/bin/perl
use strict;
use warnings;

my $gtf = './Hc_genome/Hc_rztk_1+2+8+9.augustus.gtf';
open my $FASTAFILE2, "<", $gtf or die "Unable to open '$gtf' for reading. $!";

my %seen;

while (<$FASTAFILE2>) {
    chomp;
    my ($id, $val) = split /\t/, $_, 2;

    # copy $id to $prefix and
    # remove everything after '.1' in $prefix
    (my $prefix = $id) =~ s/\.1\K.*//; 

    if ($seen{$id}) {
        ++$seen{$id};
    }
    else {
        $seen{$id} = 'a';   
    }
    print "$prefix$seen{$id}\t$val\n";
}

close $FASTAFILE2 or die "Unable to close '$gtf' from reading. $!";