我在一周之前发布了一个问题,答案很简单(使用加入):
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命令中,但我无法弄清楚如何基于两个字段加入。我试过了
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为空
(join)是否能够基于两个字段连接两个文件?如果不是那我该怎么办?
答案 0 :(得分: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 $!;
答案 1 :(得分: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个文件中啜饮);如果两个文件都很大,这会占用大量内存
更多的内存保存方法(cdtits的答案:在较小的文件中啜食,存储在哈希中,然后将查找应用于第二个文件的逐行读取)
对于真正的gynormous文件,做一个线性方法:
对两个文件进行排序,读取每个文件的1行;如果匹配,打印匹配;如果不;在ID较小的文件中跳过1行。
答案 2 :(得分: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
答案 3 :(得分:1)
如果features.txt中的“ENST”为“ENSG”,则此处是 awk 解决方案,该解决方案在给定示例中运行良好:
awk 'BEGIN {while(getline <"features.txt") f[$1]=$2} {print $1,f[$1],$2,f[$2]}' < genes.txt
如果需要,我可以详细解释。
答案 4 :(得分: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;
答案 5 :(得分: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
代替ENST004
放入features.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)
如果你要处理非常大的数据,它应该对数十GB有效(如果features.txt
和genes.txt
的大小相当,那么它应该比大多数RDBMS更好):< / p>
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"