我有 4 个 tsv(制表符分隔)文件,如下所示:
文件_1:
abc 1
def 2
ghi 3
文件_2:
abc 2
ghi 3
文件_3:
def 1
ghi 2
jkl 4
文件_4:
ghi 3
jkl 4
我想加入这些文件以获得 1 个 tsv 文件,如下所示:
dataset file_1 file_2 file_3 file_4
abc 1 2
def 2 4
ghi 3 3 2 3
jkl 4 4
我尝试过使用awk
$ awk '
BEGIN{OFS=FS="\t"}
FNR==1{f = f "\t" FILENAME}
NR==FNR{a[$1] = $2}
NR!=FNR{a[$1] = a[$1] "\t" $2}
END{printf "dataset%s\n", f; for(i in a) print i, a[i]}
' file_{1..4}
这个命令是有效的,但我得到了转移的值。假设,如果第一列和第二列具有空值,第三列和第四列具有值 4 和 4,则我从该命令获得的输出是第一列和第二列具有值 4,但第三列和第四列具有空值。所以我尝试使用awk
我提到的单独加入我的 tsv 文件。先只为file_1
并file_2
得到output_1
,然后加入file_3
并file_4
得到output_2
。之后,我使用$ join output_1 output_2
合并output_1和output_2,但我只获得4个文件中存在的值。我丢失了仅存在于一个文件中的数据。
如果您能给我建议,我将非常感激。
谢谢
答案1
$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ datasets[$1]; fnames[FILENAME]; vals[$1,FILENAME] = $2 }
END {
printf "%s", "dataset"
for (fname in fnames) {
printf "%s%s", OFS, fname
}
print ""
for (dataset in datasets) {
printf "%s", dataset
for (fname in fnames) {
printf "%s%s", OFS, vals[dataset,fname]
}
print ""
}
}
$ tail -n +1 file?
==> file1 <==
a 1
b 2
c 3
==> file2 <==
a 2
c 3
$ awk -f tst.awk file1 file2
dataset file1 file2
a 1 2
b 2
c 3 3
根据需要将任意数量的文件添加到列表中。
答案2
通过join (GNU coreutils) 8.30
“进程替换”,您可以尝试
join -a1 -a2 -t" " -oauto -e " " <(join -a1 -a2 -t" " -oauto -e "" file[12]) <(join -a1 -a2 -t" " -oauto -e " " file[34])
abc 1 2
def 2 1
ghi 3 3 2 3
jkl 4 4
这些-t
选项具有<TAB>
字符值。
答案3
现在的建议:如果您需要的话,可以稍后编写代码。
当您读取所有文件时,我会保留三个数组。
(a) 对于每个新文件,文件名的哈希列表。
(b) 对于每个新数据集,哈希列表为 $1。
(c) 对于每一行,都有一个值的哈希列表
FNR == 1 { ++htFile[FILENAME]; }
! ($1 in htSet) { ++htSet[$1]; }
{ htVal [FILENAME, $1] = $2; }
在 End 函数中,迭代 htFile 和 htSet。
function Table (r, c, buf) {
buf = "dataset";
for (c in htFile)
buf = sprint ("%s\t%s", buf, htFile[c]);
print buf;
for (r in htSet) {
buf = "";
for (c in htFile)
buf = sprint ("%s\t%s", buf, htVal[c, r]);
print substr (buf, 2);
}
}
END { Table( ); }
这不会维护输出表中文件和数据集的顺序。如果这很重要,您可以保留表的排序版本并按原始顺序进行迭代。