【问题标题】:Merge many TSV files by first column按第一列合并多个 TSV 文件
【发布时间】:2016-11-28 16:12:59
【问题描述】:

我在一个只有两列的目录中有许多(几十个)TSV 文件,我想根据第一列的值合并所有文件(两列都有我需要维护的标题);如果存在此值,则它必须添加相应的第二列的值,依此类推(参见示例)。文件可能有不同的行数并且不按第一列排序,尽管这可以通过排序轻松完成。

我尝试过 join 但这仅适用于两个文件。可以为目录中的所有文件扩展加入吗?我认为 awk 可能是一个更好的解决方案,但我对 awk 的了解非常有限。有什么想法吗?

以下是三个文件的示例:

S01.tsv

Accesion    S01  
AJ863320    1  
AM930424    1  
AY664038    2

S02.tsv

Accesion    S02  
AJ863320    2  
AM930424    1  
EU236327    1  
EU434346    2 

S03.tsv

Accesion    S03  
AJ863320    5  
EU236327    2  
EU434346    2

输出文件应该是:

    Accesion    S01   S02   S03  
    AJ863320    1     2     5  
    AM930424    1     1
    AY664038    2  
    EU236327          1     2  
    EU434346          2     2

好的,感谢 James Brown,我让这段代码正常工作(我将其命名为 compile.awk),但有一些小故障:

BEGIN { OFS="\t" }                            # tab separated columns
FNR==1 { f++ }                                # counter of files
{
    a[0][$1]=$1                               # reset the key for every record 
    for(i=2;i<=NF;i++)                        # for each non-key element
        a[f][$1]=a[f][$1] $i ( i==NF?"":OFS ) # combine them to array element
}
END {                                         # in the end
    for(i in a[0])                            # go thru every key
        for(j=0;j<=f;j++)                     # and all related array elements
            printf "%s%s", a[j][i], (j==f?ORS:OFS)
}                                             # output them, nonexistent will output empty

当我使用实际文件运行它时

awk -f compile.awk 01.tsv 02.tsv 03.tsv

我得到的输出为:

LN854586.1.1236         1
JF128382.1.1303     1   
Accesion    S01 S02 S03
JN233077.1.1420 1       
HQ836180.1.1388     1   
KP718814.1.1338         1
JQ781640.1.1200         2

前两行不属于那里,因为文件应该以所有文件的标题(第三行)开头。 任何想法如何解决这个问题?

【问题讨论】:

  • 您能否展示(在问题中)您到目前为止所尝试的内容?
  • 基本上加入,尝试了一些 grep,并大量搜索类似的东西,但没有什么我可以实现或修改的,可能是因为我缺乏编码知识。加入正是我想要的,但只适用于两个文件。
  • 您可以使用以下链接中的program.awk。根据您的需要修改OFS(我假设是OFS="\t")。此外,输出记录顺序是随机的。 stackoverflow.com/questions/40373180/…
  • 加入 3 个文件 join -a 1 -a 2 -e "" -o 0,1.2,2.2 S01.tsv S02.tsv | join -a 1 -a 2 -e "" -o 0,1.2,1.3,2.2 - S03.tsv ..... view join-multiple-files
  • 不按顺序输出,需要使用sort输出或控制for的扫描,见hereawk -f compile.awk S*.tsv | sort。另外,如果你的第一个字段很长,标签不会这样做,你需要printf

标签: bash perl awk


【解决方案1】:

我可能会这样处理它:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

my @header; 
my %all_rows;
my %seen_cols;


#read STDIN or files specified as args. 
while ( <> ) {
   #detect a header row by keyword. 
   #can probably do this after 'open' but this way
   #means we can use <> and an arbitrary file list. 
   if ( m/^Accesion/ ) { 
      @header = split;       
      shift @header; #drop "accession" off the list so it's just S01,02,03 etc. 
      $seen_cols{$_}++ for @header; #keep track of uniques. 
   }
   else {
      #not a header row - split the row on whitespace.
      #can do /\t/ if that's not good enough, but it looks like it should be. 
      my ( $ID, @fields ) = split; 
      #use has slice to populate row.

      my %this_row;
      @this_row{@header} = @fields;

      #debugging
      print Dumper \%this_row; 

      #push each field onto the all rows hash. 
      foreach my $column ( @header ) {
         #append current to field, in case there's duplicates (no overwriting)
         $all_rows{$ID}{$column} .= $this_row{$column}; 
      }
   }
}

#print for debugging
print Dumper \%all_rows;
print Dumper \%seen_cols;

#grab list of column headings we've seen, and order them. 
my @cols_to_print = sort keys %seen_cols;

#print header row. 
print join "\t", "Accesion", @cols_to_print,"\n";
#iteate keys, and splice. 
foreach my $key ( sort keys %all_rows ) { 
    #print one row at a time.
    #map iterates all the columns, and gives the value or an empty string
    #if it's undefined. (prevents errors)
    print join "\t", $key, (map { $all_rows{$key}{$_} // '' } @cols_to_print),"\n"
}

鉴于您的输入 - 排除调试 - 打印:

Accesion    S01 S02 S03 
AJ863320    1   2   5   
AM930424    1   1       
AY664038    2           
EU236327        1   2   
EU434346        2   2   

【讨论】:

  • 这个 perl 脚本就像一个魅力!我只是注释 (#) 最终版本的调试打印行,谢谢。
猜你喜欢
  • 1970-01-01
  • 2023-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-21
  • 2023-01-18
  • 1970-01-01
  • 2020-05-29
相关资源
最近更新 更多