根据单元格与列的匹配拆分矩阵表

根据单元格与列的匹配拆分矩阵表

我在 Linux 上的文件中有一个很大的制表符分隔矩阵:

Name    ID  ABC12   ABCD12  ABCD123 ABCD1234
ABC12   123456  XX  YY  ZZ  JJ
ABC12   123456  XX  YY  ZZ  JJ
ABCD12  123456  XX  YY  ZZ  JJ
ABCD12  123456  XX  YY  ZZ  JJ
ABCD123 123456  XX  YY  ZZ  JJ
ABCD123 123456  XX  YY  ZZ  JJ
ABCD1234    123456  XX  YY  ZZ  JJ
ABCD1234    123456  XX  YY  ZZ  JJ

我想根据第一列中的匹配将此矩阵拆分为单独的文件,[这是一个大文件,无法计算列数]

预期输出:

文件1;

Name    ID  ABC12
ABC12   123456  XX
ABC12   123456  XX

文件2;

Name    ID  ABCD12
ABCD12  123456  YY
ABCD12  123456  YY

文件3;

Name    ID  ABCD123
ABCD123 123456  ZZ
ABCD123 123456  ZZ

文件4;

Name    ID  ABCD1234
ABCD1234    123456  JJ
ABCD1234    123456  JJ

答案1

完全的awk解决方案:

awk 'NR==1{ len=split($0,a_pos); for(i=1;i<=len;i++) a_keys[a_pos[i]]=i }
     NR>1{ if(!r[$1]++) { fn="file"++c; print "Name\tID\t"$1 > fn } 
           print $1,$2,$(a_keys[$1]) > fn 
     }' OFS='\t' file
  • len=split($0,a_pos)- 将第一行拆分为“键”数组(数组a_pos用整数索引)

  • for(i=1;i<=len;i++) a_keys[a_pos[i]]=i- 翻转到将使用字符串键索引的a_pos数组(用于进一步处理)a_keys

  • fn="file"++c- 构造文件名


查看结果:

for f in file[0-9]*; do (echo "$f"; cat "$f"; echo); done

输出(file1file2file3file4连续):

file1
Name    ID  ABC12
ABC12   123456  XX
ABC12   123456  XX

file2
Name    ID  ABCD12
ABCD12  123456  YY
ABCD12  123456  YY

file3
Name    ID  ABCD123
ABCD123 123456  ZZ
ABCD123 123456  ZZ

file4
Name    ID  ABCD1234
ABCD1234    123456  JJ
ABCD1234    123456  JJ

答案2

你可以使用awk

awk 'NR>1{if ($1!=p){N="file"++C; print "Name\tID\t"$1 >N};
             print $1,$2,$(C+2)>N}{p=$1}' infile.txt

答案3

我能想到的最简单的方法是将第一行保存为变量,然后根据需要打印其余部分。然而,这需要将整个输入文件保存到内存中:

#!/bin/gawk -f
{
    if(NR==1){
        header[1]=$1;
        header[2]=$2;
        for(i=3;i<=NF;i++){
            header[$i]=i;
        }
    }
    else{
        data[$1][NR]=$2"\t"$(header[$1]);
    }
}
END{
    OFS="\t";
    for(i in data){
        print header[1],header[2],i > i".txt"
        for(k in data[i]){
            print i,data[i][k] >> i".txt"
        }
    }
}

将该脚本另存为foo.awk,使其可执行 ( chmod a+x foo.awk) 并在您的文件上运行它:

foo.awk file

答案4

用法: ./split_matrix.awk input.txt

#!/usr/bin/awk -f

BEGIN {
    cnt = 1;
}

NR == 1 { 
    for(i = 3; i <= NF; i++) {
        headers[$i] = i;            
    }   
}
NR > 1 { 
    if( ! file_names[$1]) {
        file_names[$1] = cnt++;
        printf "%s %s %s\n", "Name", "ID", $1 > "file_"file_names[$1];
    }   
    printf "%s %s %s\n", $1, $2, $headers[$1] >> "file_"file_names[$1];
}

测试

输入

Name    ID  ABC12   ABCD12  ABCD123 ABCD1234
ABC12   123456  XX  YY  ZZ  JJ
ABC12   123456  XX  YY  ZZ  JJ
ABCD12  123456  XX  YY  ZZ  JJ
ABCD12  123456  XX  YY  ZZ  JJ
ABCD123 123456  XX  YY  ZZ  JJ
ABCD123 123456  XX  YY  ZZ  JJ
ABCD1234    123456  XX  YY  ZZ  JJ
ABCD1234    123456  XX  YY  ZZ  JJ

输出(用于tail -n +1 -- file*打印文件名和文件内容。我发现了这个技巧这里

==> file_1 <==
Name ID ABC12
ABC12 123456 XX
ABC12 123456 XX

==> file_2 <==
Name ID ABCD12
ABCD12 123456 YY
ABCD12 123456 YY

==> file_3 <==
Name ID ABCD123
ABCD123 123456 ZZ
ABCD123 123456 ZZ

==> file_4 <==
Name ID ABCD1234
ABCD1234 123456 JJ
ABCD1234 123456 JJ

相关内容