我向您展示了我需要对数据做些什么的示例。我有两个用标签分隔的文本文件。
cat in1.tsv
111 A B C
111 D E F
111 G H I
222 A B C
333 A B C
333 D E F
此表可以有大约数千行。列数小于100.第一列可以有重复的值(如111和333)。
cat in2.tsv
111 a b c
222 a b c
333 d e f
在此文件中,仅在第1列中显示一次值。我需要根据第一列匹配合并这两个文件。
cat output.tsv
111 A B C 111 a b c
111 D E F 111 a b c
111 G H I 111 a b c
222 A B C 222 a b c
333 A B C 333 d e f
333 D E F 333 d e f
如果矩阵的大小相同,我的解决方案是有效的:
paste <(sort in1.tsv) <(sort in2.tsv) > output.tsv
我很感激awk,bash或其他程序的任何帮助,这些程序可以快速运行很多行。
答案 0 :(得分:3)
Awk
救援!
awk 'BEGIN{FS=OFS="\t"}FNR==NR{for(i=2;i<=NF;i++) map[$1]=(map[$1] FS $i); next}$1 in map{print $0,$1,map[$1]}' in2.tsv in1.tsv
按照预期以制表符分隔格式生成输出。如果您不希望o / p标签分离,请删除OFS="\t"
。
就逻辑而言,创建一个地图,其中包含in2.csv
上每列1的值到哈希地图map[]
,然后在in1.csv
上选择包含$1
的行与形成的地图相同并打印行内容。
答案 1 :(得分:2)
以下是bash
方法:
首先让我们对每个文件进行排序:
LC_ALL=C sort init1.tsv -S75% -t$'\t' -k1,1 > init1.tsv.sorted
LC_ALL=C sort init2.tsv -S75% -t$'\t' -k1,1 > init2.tsv.sorted
然后,而不是pasting
让join
在第一列,
join init1.tsv.sorted init2.tsv.sorted -1 1 -2 2 -t$'\t'
如果您需要特定类型的连接,这似乎是左外连接,那么我会这样做:
join init1.tsv.sorted init2.tsv.sorted -1 1 -2 2 -t$'\t' -a1
快速说明,-S
指定您想要使用多少RAM,您希望此操作的速度越快,您应该使用的越多。
答案 2 :(得分:2)
join
命令似乎几乎可以满足您的需求:
$ join in1.tsv in2.tsv
111 A B C a b c
111 D E F a b c
111 G H I a b c
222 A B C a b c
333 A B C d e f
333 D E F d e f
默认行为是基于第一列连接行,空格为分隔符。使用格式选项-o
可以得到相同的结果。 Dmitry Polonskiy在评论中说:
join -o 1.1,1.2,1.3,1.4,2.1,2.2,2.3,2.4 <(sort in1.tsv) <(sort in2.tsv)
答案 3 :(得分:2)
在Python中,不依赖于正在排序的文件:
#!/usr/bin/env python
with open("in1.tsv") as in1, open("in2.tsv") as in2:
d = {line.split()[0]: line for line in in2}
for line in in1:
print(line.strip(), d[line.split()[0]], sep="\t", end="")
这基本上创建了从第一列的值到in2.tsv
的行的映射,然后在in1.tsv
的行上循环,并使用映射将它们与in2.tsv
的相应行组合
答案 4 :(得分:2)
这可能适合你(GNU sed):
sed -r 's#^(\S+)\s.*#/^\1/s/$/ &/#' file2 | sed -f - file
从第二个文件创建一个sed脚本。此脚本包含一个正则表达式,匹配时将第二个文件中的匹配记录附加到第一个匹配的记录中。