按第一列合并多个文件

按第一列合并多个文件

我的问题与此类似:根据第一列值合并多列

我有多个文件(10+),我想将它们合并/加入到一个输出文件中,例如:

文件1

2000 0.0202094
2001 0.0225532
2002 0.02553
2003 0.0261099
2006 0.028843

文件2

2000 0.0343179
2003 0.039579
2004 0.0412106
2006 0.041264

文件3

2001 0.03
2004 0.068689
2006 0.0645474

所有文件都有相同的两列并且长度不等。如果没有列条目(在一个或多个文件中缺失),我需要 1。如果任何文件中都没有条目(例如 2005),我不需要任何输出。

期望的输出是:

        file1       file2       file3
2000    0.0202094   0.0343179   1
2001    0.0225532   1           0.03
2002    0.02553     1           1
2003    0.0261099   0.0395799   1
2004    1           0.0412106   0.0686893
2006    0.028843    0.041264    0.0645474

我尝试修改另一个问题的答案提供的 awk 代码,但我觉得该解决方案不可能实现。

答案1

使用join

join -a1 -a2 -e 1 -o auto <(join -a1 -a2 -e 1 -o auto file1 file2) file3

见于男人加入

-a 文件号
还打印文件 FILENUM 中不可配对的行,其中 FILENUM 为 1 或 2,对应于 FILE1 或 FILE2

-e 空
将缺失的输入字段替换为 EMPTY

-o 格式
构建输出行时遵循 FORMAT

如果 FORMAT 是关键字“auto”,则每个文件的第一行决定每行输出的字段数。

注意:连接需要排序的输入,因此如果这些输入未排序(它们在给定的样本中),请首先对它们进行排序,例如:

join -a1 -a2 -e 1 -o auto \
    <(join -a1 -a2 -e 1 -o auto <(sort file1) <(sort file2)) \
    <(sort file3)

要将其应用于多个文件:

  1. 连接前两个文件并将输出保存到第三个文件join.tmp:

    join -a1 -a2 -e 1 -o auto file1 file2 >join.tmp
    
  2. next 循环遍历其余文件并更新join.tmp每次运行的文件:

    for file in rest_files*; do
        join -a1 -a2 -e 1 -o auto join.tmp "$file" >join.tmp.1
        mv join.tmp.1 join.tmp
    done
    
  3. 最后你的join.tmp将是您的最终连接结果。


带标题打印:

$ hdr() { awk 'FNR==1{ print "\0", FILENAME }1' "$1"; }
$ join -a1 -a2 -e 1 -o auto \
      <(join -a1 -a2 -e 1 -o auto <( hdr file1) <(hdr file2)) \
      <(hdr file3) |tr -d '\0'

对于多文件版本:

$ hdr() { awk 'FNR==1{ print "\0", FILENAME }1' "$1"; }
$ join -a1 -a2 -e 1 -o auto <(hdr file1) <(hdr file2) >join.tmp
$ for file in rest_files*; do
     join -a1 -a2 -e 1 -o auto join.tmp <(hdr "$file") >join.tmp.1
     mv join.tmp.1 join.tmp
  done
$ tr -d '\0' <join.tmp >final.file

答案2

有点笨拙,但是这个awk代码可以工作。它使用的选项伪多维数组与数组索引的 SUBSEP 串联。将所有数据保存在 RAM 中,因此在这种情况下受到限制。

{x[$1]=$1 ; file[FILENAME]=FILENAME ; y[$1,FILENAME]=$2}

END { for (i in file) { printf "\t%s",file[i] } ; printf "\n",""
      for (i in x) { printf "%s",x[i]
        for (j in file) { if (y[x[i],file[j]] != "")
                             { printf"\t%s",y[x[i],file[j]] }
                          else { printf"\t%s","1"}
        }
        printf "\n",""
      }
    }

输出仅以制表符分隔,对于固定格式,需要相应调整 printf 命令:

    file1   file2   file3
2000    0.0202094   0.0343179   1
2001    0.0225532   1   0.03
2002    0.02553 1   1
2003    0.0261099   0.039579    1
2004    1   0.0412106   0.068689
2006    0.028843    0.041264    0.0645474

相关内容