即使 IDS 不匹配且列数不定,也可以根据 ID 合并 CSV 文件

即使 IDS 不匹配且列数不定,也可以根据 ID 合并 CSV 文件

我正在编写一个 bash 脚本,该过程的一部分需要将 csv 文件拼凑在一起,同时保留列的完整性;例如我有两个格式如下的文件:

F1

ID,MD,L1,L2,L3,GD,L4
12,OB,AA,PP,AA,TT,AA
15,OB,PP,PP,PP,TT,AA

F2

ID,MD,L7,L8,L9,L10,GD
13,OB,PP,AA,AA,AA,AA
15,OB,PP,PP,PP,AA,AA

输出如下所示,其中不匹配的 ID 返回值 NM,重复列(在本例中为“GD”)根据每个文件的值显示:

ID,MD,L1,L2,L3,GD,L4,L7,L8,L9,L10,GD
12,OB,AA,PP,AA,TT,AA,NM,NM,NM,NM,NM
13,NM,NM,NM,NM,NM,NM,PP,AA,AA,AA,AA
15,OB,PP,PP,PP,TT,AA,PP,PP,PP,AA,AA

我一直在努力join工作,因为它看起来很有前途,即 join -t, -eNM -a1 -a2 -o 0,1.2,1.3,1.4,1.5,1.6,1.7,1.8,2.3,... F1 F2

但我遇到了一些问题。

  1. 如果我使用该-o选项,则假定我知道每个文件的确切列数,而这些列数是独立变化的。

  2. 结果中出现错误: join: REPORT_2|15-10-2019|15:39:25.csv:5: is not sorted: 04181646

如果有其他替代方案,join我愿意接受建议。谢谢。

答案1

您可以使用 shell 脚本来创建必要的-o选项并对输入文件的数据进行排序。

这假设第二个字段MD存在于两个输入文件中,并且仅在输出中打印一次(在第二个文件的选项中跳过)。

#!/bin/bash

opts="0,"

# file1: get number of columms - 1 from the first line
numcols=$(awk -F',' '(NR==1) {print NF-1}' "$1")

# file1: add options
for i in $(seq "$numcols"); do
  opts+=$(printf '1.%s,' "$((i+1))")
done

# file2: get number of columms - 2 from the first line
numcols=$(awk -F',' '(NR==1) {print NF-2}' "$2")

# file2: add options
for i in $(seq "$numcols"); do
  opts+=$(printf '2.%s,' "$((i+2))")
done

opts=${opts:0:-1} # remove the last `,`

join -t, --header -eNM -a1 -a2 -o "$opts"\
  <(head -n1 "$1"; tail -n+2 "$1" | sort -nk1,1)\
  <(head -n1 "$2"; tail -n+2 "$2" | sort -nk1,1)

我添加了--header将第一行视为标题行的选项。用于<(head -n1 "$1"; tail -n+2 "$1" | sort -nk1,1)打印标题行并按第一个字段对其余行进行数字排序。

使脚本可执行

chmod +x join.sh

并将其运行为

./join.sh file1 file2

答案2

使用米勒(https://github.com/johnkerl/miller),你可以运行

mlr --csv join  -u --ul --ur -j ID -f input_01.csv  \
then unsparsify \
then put -S 'for (k in $*) { if ($[k] =~ "^$") { $[k]="NM" }}' \
input_02.csv

并获得

ID,MD,L7,L8,L9,L10,L1,L2,L3,L4
13,OB,PP,AA,AA,AA,NM,NM,NM,NM
15,OB,PP,PP,PP,AA,PP,PP,PP,AA
12,OB,NM,NM,NM,NM,AA,PP,AA,AA

相关内容