1

1週間前に質問を投稿しましたが、答えは単純に(joinを使用)でした。

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

通常は最初のフィールドである共通の何かを持つファイルを結合します。

私は次の2つのファイルを持っています:

gene.txt

ENSG001 ENSG002
ENSG002 ENSG001
ENSG003 ENSG004

features.txt

ENSG001 400
ENSG002 350
ENSG003 210
ENSG004 100

次のようにするには、これら2つのファイルを結合する必要があります。

output.txt

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

答えがjoinコマンドにあることは知っていますが、2つのフィールドに基づいて結合する方法がわかりません。私は試した

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

しかし、結果は次のようになります。

try1.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

try2.txtが空です

(結合)には、2つのフィールドに基づいて2つのファイルを結合する機能がありますか?いいえの場合、どうすればそれを行うことができますか?

4

6 に答える 6

3

私の知る限り、joinはこれをサポートしていません。を参照してくださいjoin manpage

ただし、これは2つの方法で実行できます。

  • ファイルの最初のスペース/タブをカレット(またはファイルに表示されない他の文字)に変えてから、前と同じように結合を使用すると、最初の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の答え:小さなファイルに丸呑みし、ハッシュに保存してから、2番目のファイルの行ごとの読み取りにルックアップを適用します)

    • 本当に巨大なファイルの場合は、線形アプローチを実行します。

      両方のファイルを並べ替え、各ファイルの1行を読み取ります。それらが一致する場合は、一致を印刷します。そうでない場合; IDが小さいファイルの1行をスキップします。

于 2012-07-22T13:54:15.573 に答える
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 $!;
于 2012-07-22T12:27:24.770 に答える
3

問題を騙して答えることができたすべての人に感謝します。

最初にファイルを通常どおりに結合し、次に1番目と2番目のフィールドの位置を変更し、次に変更された出力ファイルを機能を使用してもう一度結合し、最後にフィールドの位置を再度切り替えました。

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
于 2012-07-22T20:40:58.713 に答える
1

features.txtの「ENST」が「ENSG」の場合、次の例でうまく機能するawkソリューションを次に示します。

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

必要に応じて詳しく説明します。

于 2012-07-22T11:42:00.923 に答える
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;
于 2012-07-22T11:49:53.503 に答える
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、ほとんどのRDBMSよりも優れているはずです)。genes.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"
于 2012-07-22T17:47:15.250 に答える