2017-06-14 30 views
3

我向您展示了我需要用我的数据做的一个示例。我有两个由tab分隔的文本文件。在bash中将数据粘贴在一起

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

这是通过在Python或R中使用pandas模块看起来可以解决的问题。如果需要缩放,可能转到Dask(另一个Python库)。 –

+0

感谢您的评论。我首先想到使用一些awk或bash解决方案,因为我对python不是很熟悉。 – Geroge

回答

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与从形成在地图那些线和打印行内容。

+0

谢谢伊安 - 这是非常好的解决方案! – Geroge

+0

我正在测试你的脚本,它能正常工作,但总是会放空行 - 例如:111 A B C 111 EMPTY a b c ..这是正常的吗? – Geroge

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,您希望此操作越快,您应该使用的越多。

+0

很好的解决方案谢谢你。你能解释一下排序的第一个操作 - LC_ALL = C? – Geroge

+0

它将所有本地设置强制输出设置为默认语言。我也相信它会迫使排序按位进行。 –

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可以给我们相同的结果。由于梅德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) 
+0

嗨,谢谢你加入是非常有用的。如果数据未排序,我是否需要对数据进行排序? – Geroge

+2

是的,除非两个文件都先排序,否则连接将不起作用 –

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的相应行结合。

+0

感谢您的回复。将有可能运行这种形式的终端像python your_solution.py int1.tsv int2.tsv?对不起,也许愚蠢的问题。 – Geroge

+0

示例中的文件名是硬编码的。如果您想传递参数,请改用['sys.argv'](https://docs.python.org/3.6/library/sys.html#sys.argv)。 –

2

这可能为你工作(GNU SED):

sed -r 's#^(\S+)\s.*#/^\1/s/$/ &/#' file2 | sed -f - file 

创建第二个文件sed脚本。这个脚本由一个正则表达式组成,当匹配时将第二个文件中的匹配记录追加到第一个匹配的记录中。