2017-06-14 45 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腳本。這個腳本由一個正則表達式組成,當匹配時將第二個文件中的匹配記錄追加到第一個匹配的記錄中。