2016-08-12 72 views
0

我有一個製表符分隔的文件,在第一列中有重複的值。第一列中的單個但重複的值對應於第二列中的多個值。它看起來是這樣的:根據列ID解析文件:perl

AAAAAAAAAA1  m081216|101|123 
    AAAAAAAAAA1  m081216|100|1987 
    AAAAAAAAAA1  m081216|927|463729 
    BBBBBBBBBB2  m081216|254|260489 
    BBBBBBBBBB2  m081216|475|1234 
    BBBBBBBBBB2  m081216|987|240 
    CCCCCCCCCC3  m081216|433|1000 
    CCCCCCCCCC3  m081216|902|366 
    CCCCCCCCCC3  m081216|724|193 

對於第一列中的每種類型的序列,我試圖打印到只有對應於它的序列的文件。文件的名稱應該包括第一列中的重複序列和第二列中對應於它的序列的數量。在上面的例子中,我將因此有3個文件,每個文件有3個序列。第一個文件將被命名爲類似「AAAAAAAAAA1.3.txt」,看起來像打開的時候以下幾點:

m081216|101|123 
    m081216|100|1987 
    m081216|927|463729 

我見過其他類似的問題,但他們一直在回答使用哈希。我不認爲我不能使用散列,因爲我需要保持列之間的關係數量。也許有一種方法可以使用散列哈希?我不確定。 這是我的代碼到目前爲止。

use warnings; 
    use strict; 
    use List::MoreUtils 'true'; 

    open(IN, "<", "/path/to/in_file") or die $!; 

    my @array; 
    my $queryID; 

    while(<IN>){ 
      chomp; 
      my $OutputLine = $_; 
      processOutputLine($OutputLine); 
    } 


    sub processOutputLine { 
      my ($OutputLine) = @_; 
      my @Columns = split("\t", $OutputLine); 
      my ($queryID, $target) = @Columns; 
      push(@array, $target, "\n") unless grep{$queryID eq $_} @array; 
      my $delineator = "\n"; 
      my $count = true { /$delineator/g } @array; 
      open(OUT, ">", "/path/to/out_$..$queryID.$count.txt") or die $!; 
      foreach(@array){ 
        print OUT @array; 
      } 
    } 
+0

你可能也想從循環中刪除文件的寫入,否則你會用每一行數據寫下來。 –

回答

3

我仍然會推薦一個散列。但是,您將與該ID相關的所有序列存儲在一個匿名數組中,該數組是該ID鍵的值。這實際上是兩行代碼。

use warnings; 
use strict; 
use feature qw(say); 

my $filename = 'rep_seqs.txt'; # input file name 
open my $in_fh, '<', $filename or die "Can't open $filename: $!"; 

my %seqs; 
foreach my $line (<$in_fh>) { 
    chomp $line; 
    my ($id, $seq) = split /\t/, $line; 
    push @{$seqs{$id}}, $seq; 
} 
close $in_fh; 

my $out_fh; 
for (sort keys %seqs) { 
    my $outfile = $_ . '_' . scalar @{$seqs{$_}} . '.txt'; 
    open $out_fh, '>', $outfile or do { 
     warn "Can't open $outfile: $!"; 
     next; 
    }; 
    say $out_fh $_ for @{$seqs{$_}}; 
} 
close $out_fh; 

隨着你輸入我得到想要的文件,命名爲AA..._count.txt,與他們對應的各三根線。例如,如果分開|的項目應該分開,那麼可以在寫出項目時進行此操作。

評論

  • 一鍵$seqs{$id}匿名陣列中創建,一旦我們push,如果不存在的話

  • 如果有與標籤的問題(轉換爲空格?),使用' '。看到評論。

  • 文件句柄被關閉,每open重新打開,因此沒有必要關閉每次


split的默認模式是' ',也觸發特定的行爲 - 它匹配「任何連續的空格」,也省略了前導空格。 (模式/ /匹配單個空格,關閉' '的這種特殊行爲。)有關split頁面的更精確的描述,請參閱。因此,在分割未指定數量的空格時,建議使用' ',因爲在split的情況下,這有點慣用,也許是最常用的用法,並且是其默認值。感謝Borodin提示此評論和更新(原文有相當於/\s+/)。

注意,在這種情況下,由於' '$_沿着默認情況下,我們可以縮短一點

for (<$in_fh>) { 
    chomp; 
    my ($id, $seq) = split; 
    push @{$seqs{$id}}, $seq; 
} 
+0

感謝您的幫助和有見地的評論。我注意到在你的回覆中,「close $ fh_in;」應該真的「close $ in_fh;」。另外,關於我的問題的後半部分,您是否有任何關於如何將每個文件的名稱與特定文件中包含的序列號相加的技巧?再次感謝您的巨大幫助! – Rob

+0

@Rob謝謝你的評論!我喜歡將文件句柄標記爲'$ specs_fh' - 將會更正。而且,我忘記了將計數添加到名稱的要求,現在將添加。感謝您的歸屬。 – zdim

+0

@Rob我將計數添加到文件名。 'scalar'返回列表的長度(元素數量),數組引用可以通過解除引用'@ {array_ref}'用作列表,所以它非常簡單。如果你真的希望'.',請把'_'換成''.'',我用'_'來區分文件的計數。 – zdim