2012-07-22 89 views
1

兩個文件我張貼的問題,一個星期前,得到的答案是簡單的(使用連接):加入基於兩個領域

join <(sort file1) <(sort file2) >output 

加入有共同的東西通常是第一個字段的文件。

我有以下兩個文件:

genes.txt

ENSG001 ENSG002 
ENSG002 ENSG001 
ENSG003 ENSG004 

features.txt

ENSG001 400 
ENSG002 350 
ENSG003 210 
ENSG004 100 

我需要加入這兩個文件是這樣的:

output.txt

ENSG001 400 ENSG002 350 
ENSG002 350 ENSG001 400 
ENSG003 210 ENSG004 100 

我知道答案是在聯接命令,但我不知道如何加入基於兩個領域。我試圖

join -j 1 <(sort genes.txt) <(sort features.txt) >attempt1.txt

但結果會看起來像這樣:

attempt1.txt

ENSG001 ENSG002 400 
ENSG002 ENSG001 350 
ENSG003 ENSG004 210 

我又試圖

join -j 2 <(sort -k 2 genes.txt) <(sort -k 2 features.txt) >attempt2.txt

attempt2.txt是空的

(連接)是否可以根據兩個字段連接兩個文件?如果沒有,那我該怎麼辦呢?

+1

在features.txt中ENST應該是ENSG,也許? – igustin 2012-07-22 11:40:06

+0

這個問題爲什麼用'Perl'標記?你正在問一個關於'join'的問題。 – Borodin 2012-07-22 14:03:06

+0

是的,這是ENSG不ENST – 2012-07-22 20:03:17

回答

3

謝謝你,我已成功通過欺騙的問題來回答這一切的傢伙。

首先我正常加入文件,然後我改變了第一個和第二個字段的位置,然後我再次加入了帶有特徵的修改後的輸出文件,最後我再次切換了字段的位置。

join <(sort genes.txt) <(sort features.txt) >tmp 

cat tmp | awk '{ print $2, $1, $3 }' >tmp2 

join <(sort tmp2) <(sort features.txt) >tmp3 

cat tmp3 | awk '{ print $2, $3, $1, $4 }' >output.txt 
1

在情況下「ENST」在features.txt是「ENSG」,這裏是一個AWK的解決方案,在給定的例子行之有效:

awk 'BEGIN {while(getline <"features.txt") f[$1]=$2} {print $1,f[$1],$2,f[$2]}' < genes.txt 

我可以詳細,如果你需要解釋。

+0

+1 - 或'awk'FNR == NR {f [$ 1] = $ 2; next} {print $ 1,f [$ 1],$ 2,f [$ 2]}'features.txt genes.txt'。請注意,您最後的重定向不是必需的。 – 2012-07-22 23:56:52

1

使用perl:

use strict; 
use warnings; 
open GIN, "<genes.txt" or die("genes"); 
open FIN, "<features.txt" or die("features"); 
my %relations; 
my %values; 
while (<GIN>) { 
    my ($r1, $r2) = split; 
    $relations{$r1} = $r2; 
} 
while (<FIN>) { 
    my ($k, $v) = split; 
    $values{$k} = $v; 
} 
for my $r1 (sort keys %relations) { 
    my $r2 = $relations{$r1}; 
    print "$r1 $values{$r1} $r2 $values{$r2}\n"; 
} 
close FIN; close GIN; 
3
%features; 
open $fd, '<', 'features.txt' or die $!; 
while (<$fd>) { 
    ($k, $v) = split; 
    $features{$k} = $v; 
} 
close $fd or die $!; 

open $fd, '<', 'genes.txt' or die $!; 
while (<$fd>) { 
    s/(\w+)/$1 $features{$1}/g; 
    print; 
} 
close $fd or die $!; 
3

據我所知,加盟不支持此。見join manpage

但是,您可以通過2種方式實現這一點:

  • 打開文件中的第一個空間/卡口插入符號(或其它字符,你將永遠不會在文件中看到),然後用加入作爲在此之前將前兩個字段視爲1字段:

    perl -pi -e 's/^(\S+)\s+/$1#/' file1 
    perl -pi -e 's/^(\S+)\s+/$1#/' file2 
    join <(sort file1) <(sort file2) >output 
    tr "#" " " output > output.final 
    
  • 在Perl中執行此操作。你可以做

    • 鈍的方法(perreal的回答:一次在2個文件中sl));如果兩個文件都很大,這需要很多內存

    • 更多的內存保留方法(cdtits的回答:在較小的文件中進行slurp,存儲在散列中,然後將查找應用到逐行讀取第二個文件)

    • 對於真正gynormous文件,做一個線性的方法:

      排序兩個文件,讀取1號線的每個文件的;如果它們匹配,則打印匹配;如果不;在ID較小的文件中跳過1行。

1

你的做法是正確的一般。這應該是可以實現的東西像

join -o '1.1 2.2 1.2 1.3' <(
    join -o '1.1 1.2 2.2' -1 2 <(sort -k 2 genes.txt) <(sort features.txt) | 
    sort 
) <(sort features.txt) 

如果我把ENSG004,而不是ENST004features.txt我會得到什麼,你正在尋找:

$ join -o '1.1 2.2 1.2 1.3' <(
     join -o '1.1 1.2 2.2' -1 2 <(sort -k 2 genes.txt) <(sort features.txt) | 
     sort 
) <(sort features.txt) 
ENSG001 400 ENSG002 350 
ENSG002 350 ENSG001 400 
ENSG003 210 ENSG004 100 

有更簡潔的版本,但還有更難跟蹤字段:

join -o '1.2 2.2 1.1 1.3' -1 2 <(
    join -1 2 <(sort -k 2 genes.txt) <(sort features.txt) | 
    sort -k 2 
) <(sort features.txt) 

如果您要處理真正的大數據,它應該會工作得非常有效, ˚FGB(也應該是優於大多數的RDBMS的,如果features.txtgenes.txt是比較大小):

TMP=`mktemp` 
sort features.txt > "$TMP" 
sort -k 2 genes.txt | join -o '1.1 1.2 2.2' -1 2 - "$TMP" | sort | 
    join -o '1.1 2.2 1.2 1.3' - "$TMP" 
rm "$TMP" 
+0

加入-o'1.1 2.2 1.2 1.3'是什麼意思? – 2012-07-22 20:45:22

+0

@LocaToney:根據[文檔](http://www.gnu.org/software/coreutils/manual/html_node/join-invocation.html),它定義了輸出格式。特別是'-o'1.1 2.2 1.2 1.3'表示第一個輸出列將成爲第一個輸入文件的第一列,第二列將成爲第二個文件的第二列,第三列將成爲第二列第一個文件和最後一個第四列將成爲第一個文件的第三列。 – 2012-07-22 21:34:11