【问题标题】:Join two files using awk使用 awk 连接两个文件
【发布时间】:2012-10-26 20:28:08
【问题描述】:

我有两个如下所示的以​​制表符分隔的文件:

文件 A

chr1   123 aa b c d
chr1   234 a  b c d
chr1   345 aa b c d
chr1   456 a  b c d
....

文件B

xxxx  abcd    chr1   123    aa    c    d    e
yyyy  defg    chr1   345    aa    e    f    g
...

我想加入基于 3 列“chr1”、“123”和“aa”的两个文件,并将文件 B 的前两列添加到文件 A,这样输出如下所示: 输出:

chr1   123    aa    b    c    d    xxxx    abcd
chr1   234    a     b    c    d
chr1   345    aa    b    c    d    yyyy    defg
chr1   456    a    b    c    d

任何人都可以帮助在 awk 中执行此操作。如果可能的话,使用 awk oneliners?

【问题讨论】:

标签: file join awk


【解决方案1】:

这是使用awk的一种方法:

$ awk 'NR==FNR{a[$3,$4]=$1OFS$2;next}{$6=a[$1,$2];print}' OFS='\t' fileb filea
chr1    123     a    b    c     xxxx    abcd
chr1    234     a    b    c 
chr1    345     a    b    c     yyyy    defg
chr1    456     a    b    c 

解释:

NR==FNR             # current recond num match the file record num i.e in filea
a[$3,$4]=$1OFS$2    # Create entry in array with fields 3 and 4 as the key
next                # Grab the next line (don't process the next block)
$6=a[$1,$2]         # Assign the looked up value to field 6 (+rebuild records)  
print               # Print the current line & the matching entry from fileb ($6)

OFS='\t'            # Seperate each field with a single TAB on output

编辑:

对于 3 字段问题,您只需添加额外字段:

$ awk 'NR==FNR{a[$3,$4,$5]=$1OFS$2;next}{$6=a[$1,$2,$3];print}' OFS='\t' fileb filea
chr1    123    aa     b      c     xxxx     abcd
chr1    234    a      b      c  
chr1    345    aa     b      c     yyyy     defg
chr1    456    a      b      c 

【讨论】:

  • 我已经修改了原来的问题。您能否为此提供解决方案。
  • 谢谢。我以以下错误的方式添加了额外的字段:awk 'NR==FNR{a[$3,$4,$5]=$1OFS$2OFS$3;next}{$6=a[$1,$2];print} 'OFS='\t' fileb filea.
  • +1 但你也应该设置 FS 以防字段中有空格。不过,OP 对脚本的误解令人震惊!
  • @sudo_O fileb 是一个非常大的文件,当我尝试运行代码时,它会给出错误:“more_nodes: nextfree: can't allocate 6400 bytes of memory (Cannot allocate memory)”。能否修改脚本,分块读取fileb来解决内存问题?
  • @user1779730 这就是命令split 的作用。阅读man split
【解决方案2】:

您可以使用join,但管道变得如此复杂,可能更容易切换到更强大的语言,如 Perl。

join -11 -21 -o1.1,1.2,1.3,1.4,1.5,2.4,2.5 \
     <(sed 's/ \+/:/' fileA | sort) \
     <(sed 's/ \+/:/' fileB | sort) \
 | join -11 -22 -a1 -o1.1,1.2,1.3,1.4,1.5,1.6,1.7,2.5,2.6 \
     - <(sed 's/ \+\([^ ]\+\) \+\([^ ]\+\)/ \1:\2/' fileC | sort -k2) \
 | sed 's/:/ /'

Perl 解决方案,使用哈希来记住所有信息:

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

#             key_start  key_end  keep_from  output
my %files = (A => [0,      1,      2,       [0 .. 3]],
             B => [0,      1,      2,       [-2, -1]],
             C => [1,      2,      3,       [-2, -1]],
            );

my %hash;

for my $file (keys %files) {
    open my $FH, '<', "file$file" or die "file$file: $!";
    while (<$FH>) {
        my @fields = split;
        $hash{"@fields[$files{$file}[0], $files{$file}[1]]"}{$file}
            = [ @fields[$files{$file}[2] .. $#fields] ];
    }
}

for my $key (sort keys %hash) {
    print $key, join(' ', q(),
                     grep defined, map {
                         @{ $hash{$key}{$_} }[@{ $files{$_}[-1] }]
                     } sort keys %files), "\n";
}

【讨论】:

  • @user1779730:已添加 Perl 解决方案。
猜你喜欢
  • 2018-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-12
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多