在一个字段上连接两个文件

在一个字段上连接两个文件

我需要比较两个文件,并根据比较我需要在第一个文件中添加新列。 C 列是主键

A,B,C,D
1,1990,I001,2473264
2,1991,I002,2473265
3,1992,I004,2473266
4,1993,6050,912432
5,1994,6003,912433

第二个文件

A,B
I001,2.3 GHz
I002,2.3 GHz
I004,2.3 GHz
6050,1.8 GHz
6003,850 MHz

预期产出

A,B,C,D,E
1,1990,2.3 GHz,I001,2473264
2,1991,2.3 GHz,I002,2473265
3,1992,2.3 GHz,I004,2473266
4,1993,1.8 GHz,6050,912432
5,1994,850 MHz,6003,912433

此代码工作正常,但如果记录不匹配,它将跳过第一个文件中的行。但我不希望它被跳过并在该列中附加 0 或 NA。

awk -F, 'NR==FNR{a[$1]=$2;next}a[$3]{print $0","a[$3]}' test2 test1
A,B,C,D
1,1990,I001,2473264
2,1991,I002,2473265
3,1992,I004,2473266
4,1993,6050,912432
5,1994,6003,912433
6,1995,6004,21234

预期输出:

1,1990,I001,2473264,2.3 GHz
2,1991,I002,2473265,2.3 GHz
3,1992,I004,2473266,2.3 GHz
4,1993,6050,912432,1.8 GHz
5,1994,6003,912433,850 MHz
6,1995,6004,21234,0

答案1

将您的更改awk

$ awk -F, 'NR==FNR{a[$1]=$2;next} FNR!=1{print $0","(a[$3]?a[$3]:"NA")}' SECOND FIRST
1,1990,I001,2473264,2.3 GHz
2,1991,I002,2473265,2.3 GHz
3,1992,I004,2473266,2.3 GHz
4,1993,6050,912432,1.8 GHz
5,1994,6003,912433,850 MHz
6,1995,6004,21234,NA

要获得您第一个想要的结果:

$ awk -F, 'NR==FNR{a[$1]=$2;next} FNR!=1{$3=(a[$3]?a[$3]:"NA")","$3; print}' OFS=, SECOND FIRST
1,1990,2.3 GHz,I001,2473264
2,1991,2.3 GHz,I002,2473265
3,1992,2.3 GHz,I004,2473266
4,1993,1.8 GHz,6050,912432
5,1994,850 MHz,6003,912433
6,1995,NA,6004,21234

答案2

awk如果我们将其修改为,您的程序是正确的无条件地使用

print $0","( a[$3] ? a[$3] : 0 )

而不是使用

print $0","a[$3]

a[$3]非零或空时。也就是说,如果a[$3]字段为零或空,则使用零,否则使用a[$3]

换句话说,

awk -F, -v OFS=',' 'FNR==NR { a[$1]=$2; next } FNR > 1 { print $0, (a[$3] ? a[$3] : 0) }' fileB fileA

我们FNR > 1在这里使用来跳过标题。


使用join

$ join -t , -1 3 -o 1.1,1.2,1.3,1.4,2.2 fileA fileB
1,1990,I001,2473264,2.3 GHz
2,1991,I002,2473265,2.3 GHz
3,1992,I004,2473266,2.3 GHz
4,1993,6050,912432,1.8 GHz
5,1994,6003,912433,850 MHz

如果文件未按连接键排序,则(在支持进程替换的 shell 中):

$ join -t , -1 3 -o 1.1,1.2,1.3,1.4,2.2 <( sort -t, -k3,3 fileA ) <( sort fileB )
5,1994,6003,912433,850 MHz
4,1993,6050,912432,1.8 GHz
1,1990,I001,2473264,2.3 GHz
2,1991,I002,2473265,2.3 GHz
3,1992,I004,2473266,2.3 GHz

将对这两个文件join执行操作。INNER JOIN告诉-t ,我们join逗号是字段分隔符,我们选择第三个字段作为第一个文件中的连接键-1 3(假定第二个文件中的第一个字段是键,除非我们在该文件中使用-2 N相同的其他字段N)。

-o标志告诉join我们要在输出中包含哪些字段以及来自哪个文件(表示文件中的x.y列)。yx

join是一个非常快的操作,但是它需要输入文件按连接键排序(我们很幸运看到上面的第一个示例)。上面第二个例子中的最终输出未排序第一的字段,但您可以通过管道将其轻松解决sort -k1,1n


对于第二种情况,由于不匹配,

$ join -t, -1 3 -o1.1,1.2,1.3,1.4,2.2 -a 1 -e 0  <( sort -t, -k3,3 fileA ) <( sort fileB )
5,1994,6003,912433,850 MHz
6,1995,6004,21234,0
4,1993,6050,912432,1.8 GHz
A,B,C,D,0
1,1990,I001,2473264,2.3 GHz
2,1991,I002,2473265,2.3 GHz
3,1992,I004,2473266,2.3 GHz

通过添加,-a 1 -e 0我们要求join始终输出第一个文件中的所有字段(-a 1,并为第二个文件中缺少的每个字段插入零。

正如您所看到的,我们现在得到了正确的结果,但也包含了第一个文件的标头(因为我们要求它)。如果您想从两个文件的数据中删除标题(并对结果进行排序),那么

$ join -t, -1 3 -o1.1,1.2,1.3,1.4,2.2 -e 0 -a1  <( tail -n +2 fileA | sort -t, -k3,3 ) <( tail -n +2 fileB | sort ) | sort -k1,1n
1,1990,I001,2473264,2.3 GHz
2,1991,I002,2473265,2.3 GHz
3,1992,I004,2473266,2.3 GHz
4,1993,6050,912432,1.8 GHz
5,1994,6003,912433,850 MHz
6,1995,6004,21234,0

该命令实际上适用于这两种情况。

答案3

使用GNU join与扩展数据和第一的输出格式,加上echotail来替换标头(因为join会被不一致的标头混淆):

echo A,B,C,D,E
join -t ',' --header --nocheck-order test1 test2 \
     -a 1 -e NA -1 3 -2 1 -o 1.1,1.2,2.2,1.3,1.4 | tail -n +2

输出:

A,B,C,D,E
1,1990,2.3 GHz,I001,2473264
2,1991,2.3 GHz,I002,2473265
3,1992,2.3 GHz,I004,2473266
4,1993,1.8 GHz,6050,912432
5,1994,850 MHz,6003,912433
6,1995,NA,6004,21234

相关内容