bash 根据唯一 ID 合并表

bash merging tables on unique id

我有两个相似的 'table format' 文本文件,每个都有几百万条记录。在 inputfile1 中,唯一标识符是其他两个列中值的合并(它们本身都不是唯一标识符)。在inputfile2中,唯一标识符是两个字母后跟一个随机的四位数字。

如何将 inputfile1 中的唯一标识符替换为 inputfile2 中相应的唯一标识符?第一个 table 中的所有记录都出现在第二个中,但反之则不然。以下是文件的玩具示例。

输入文件 1:

Grp Len ident   data
A   20  A_20    3k3bj52
A   102 A_102   3k32rf2
A   352 A_352   3w3bj52
B   60  B_60    3k3qwrg
B   42  B_42    3kerj52
C   89  C_89    3kftj55
C   445 C_445   fy5763b

输入文件 2:

Grp Len ident
A   20  fz2525
A   102 fz5367
A   352 fz4678
A   356 fz1543
B   60  fz5732
B   11  fz2121
B   42  fz3563
C   89  fz8744
C   245 fz2653
C   445 fz2985
C   536 fz8983

期望的输出:

Grp Len ident   data
A   20  fz2525  3k3bj52
A   102 fz5367  3k32rf2
A   352 fz4678  3w3bj52
B   60  fz5732  3k3qwrg
B   42  fz3563  3kerj52
C   89  fz8744  3kftj55
C   445 fz2985  fy5763b

我的临时计划是:

  1. 以 input1 的样式(简单)为 input2 生成额外的标识符
  2. 过滤掉 input2 中不出现 input1 的行(困难)
  3. 然后坚持输入 1 的数据(简单)

我也许可以在 R 中做到这一点,但数据又大又复杂,我想知道在 bash 或 perl 中是否有办法。任何正确方向的提示都会很好。

这应该对你有用,假设 GrpLen 值在两个文件中的顺序相同,根据我的评论

本质上它从第一个文件读取一行,然后从第二个文件读取,从每个记录形成 Grp_Len 键,直到找到匹配的条目。那么这只是构建新输出记录的问题

use strict;
use warnings;

open my $f1, '<', 'file1.txt';
print scalar <$f1>;
open my $f2, '<', 'file2.txt';
<$f2>;

while ( <$f1> ) {

    my @f1 = split;

    my @f2;
    while () {
        @f2 = split ' ', <$f2>;
        last if join('_', @f2[0,1]) eq $f1[2];
    }

    print "@f2 $f1[3]\n";
}

输出

Grp Len ident   data
A 20 fz2525 3k3bj52
A 102 fz5367 3k32rf2
A 352 fz4678 3w3bj52
B 60 fz5732 3k3qwrg
B 42 fz3563 3kerj52
C 89 fz8744 3kftj55
C 445 fz2985 fy5763b



更新

这是另一个相同的版本,除了它根据第一个文件中 headers 列的间距构建 printf 格式字符串。这导致更整洁的输出

use strict;
use warnings;

open my $f1, '<', 'file1.txt';
my $head = <$f1>;
print $head;
my $format = create_format($head);

open my $f2, '<', 'file2.txt';
<$f2>;

while ( <$f1> ) {

    my @f1 = split;

    my @f2;
    while () {
        @f2 = split ' ', <$f2>;
        last if join('_', @f2[0,1]) eq $f1[2];
    }

    printf $format, @f2, $f1[3];
}

sub create_format {
    my ($head) = @_;
    my ($format, $pos);

    while ( $head =~ /\b\S/g ) {
        $format .= sprintf("%%-%ds", $-[0] - $pos) if defined $pos;
        $pos = $-[0];
    }

    $format . "%s\n";
}

输出

Grp Len ident   data
A   20  fz2525  3k3bj52
A   102 fz5367  3k32rf2
A   352 fz4678  3w3bj52
B   60  fz5732  3k3qwrg
B   42  fz3563  3kerj52
C   89  fz8744  3kftj55
C   445 fz2985  fy5763b