2つのテキストファイルがあります。最初のファイルは、次のようにタブ区切りのファイルです。
chrom pos ref alt a1 a2 a3 a4
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
2番目のファイルは次のとおりです。
a1
a4
2番目のファイルの最初のファイルの列と最初のファイルの最初の4列を抽出したいと思います。したがって、上記の場合、出力は次のようになります。
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
シェルでこれを行いたいです。どうすればいいですか?私のファイルはここに示されているよりも大きいので、最初のファイルには多くの列があります。
cut -f 1-4,$(grep -Fwf file2.txt <(head -1 file1.txt)) file1.txt
答え1
perl
問題がない場合:
$ perl -F'\t' -lane 'if(!$#ARGV){ $h{$_}=1; close ARGV if eof; next }
@i = grep { exists $h{$F[$_]} } 4..$#F if $.==1;
print join "\t", @F[0..3, @i]' f2.txt f1.tsv
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
ハッシュ変数は、2番目のファイルの行をキーとして使用します。
その後、grep
ハッシュキーのフィールド名をテストして、TSVファイルのヘッダー行からインデックスを取得するために使用されます。
最後に、最初の4列とフィルタ処理されたインデックス値が印刷に使用されます。
答え2
標準コマンドを使用して、2番目のファイル(ここで呼び出す)paste
にカンマ区切りのフィールド名のリストを作成できます。file2
$ paste -s -d , - <file2
a1,a4
我々はそれを使用することができますミラー( mlr
)、構造化文書(TSVファイルなど)を処理するためのユーティリティ、および最初のファイルcut
(ここで呼び出される)から最初の4つのフィールドと次のフィールドを抽出するサブコマンド。file2
file1
$ mlr --tsv cut -f "chrom,pos,ref,alt,$(paste -s -d , - <file2)" file1
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
Millerのサブコマンドを使用すると、cut
フィールド名を使用して抽出するフィールドを選択できます。ここでは、ファイル名だけでなく、名前で最初の4つのフィールドを選択しますfile2
。
このcut
サブコマンドはカンマで終わるフィールド名のリストも受け入れるため、前述のコマンドのtr '\n' ',' <file2
代わりにそれを使用することを選択できます。paste
file3
たとえば、次のファイルで保持したいフィールドインデックスを取得した場合:
5
8
cut
...次の標準コマンドを使用して必要なデータを抽出できます。
$ cut -f "1-4,$(paste -s -d , - <file3)" file1
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
多少複雑なパイプラインを使用して、ソースファイルから数値フィールドインデックスのリストを作成できます。
$ head -n 1 file1 | tr '\t' '\n' | grep -xF -f file2 -n | cut -d : -f 1 | paste -s -d , -
5,8
これにより、データからヘッダー行が抽出され、タブ文字が改行に変わり、各フィールド名が別々の行に表示されます。次に、grep
2番目のファイルのフィールド名に対応する行番号を出力します。これはフォームに出力されます5:a1
。ここで、その前の数字:
は行番号、末尾のテキストは一致するフィールド名です。
その番号を区切るために使用し、cut
コマンドpaste
を使用してすべてのフィールドインデックスをカンマ区切りリストに配置します。
したがって、この回答の上部にあるコマンドの機能をエミュレートする完全なコマンドはmlr
次のとおりです。
cut -f "1-4,$(
head -n 1 file1 | tr '\t' '\n' |
grep -xF -f file2 -n | cut -d : -f 1 |
paste -s -d , -
)" file1
答え3
awkを使用してください。
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR == FNR {
a[$1]
next
}
FNR == 1 {
for ( inFldNr=1; inFldNr<=NF; inFldNr++ ) {
if ( (inFldNr <= 4) || ($inFldNr in a) ) {
out2in[++numOutFlds] = inFldNr
}
}
}
{
for ( outFldNr=1; outFldNr<=numOutFlds; outFldNr++ ) {
inFldNr = out2in[outFldNr]
printf "%s%s", $inFldNr, (outFldNr<numOutFlds ? OFS : ORS)
}
}
$ awk -f tst.awk file2 file1
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
効率のために、上記file1
の各行は印刷したい出力フィールドの数だけ読み取られるため、入力に1000のフィールドがありますが、10のフィールドだけを印刷したい場合は、各行はそれ以上ではなく10回繰り返されます。 1,000個以上。
答え4
使用幸せ(以前のPerl_6)
~$ raku -e 'my ($header,@a) = lines.map: *.split(/ \s+ /);
$header .= list; my @ind = <a1 a4>;
my @col = (0...3, $header.grep( / @ind /, :k ).Slip);
put $header[@col].join("\t");
say $_.join("\t") for @a.map: *.[@col];' data.csv
上記は、Perlシリーズのプログラミング言語であるRakuで書かれた答えです。 Perlと同様に、Rakuは生物情報学などの分野のテキスト構成に非常に適しています。
まず、Rakuのルーチンを使用してデータを読み込みますlines
。この行はsplit
空のスペースにあります。データを$header
スカラーと@a
配列に保存すると、Rakuはそのデータが$header
最初の行を占有し、次の文が軽量から$header
アップグレードされることを知っています。次に、必要な列名をインラインで取得し、配列に保存します。Seq
list
@ind
grep
必要な列名のヘッダーを指定して、対応するインデックス位置(値の代わりに)を返します:k
。次に、@col
必要な最初の4列とその結果を使用して配列を作成します。0...3
このgrep
結果Slip
は結合(つまり平坦化)され、単純なインデックスを作成します。
@array[@index]
行を処理する場合は、イディオム(1行ヘッダーの場合)を使用してください。我々は列を扱っているので、map
配列要素(例えば)に入る必要があります@a.map: *.[@col]
。最後に、データがjoin
タブに再び表示され、出力されますput
。
入力例:
chrom pos ref alt a1 a2 a3 a4
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
10 12345 C T aa bb cc dd
出力例:
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
両方のファイルを処理するようにコードをアップグレードするのは簡単です。単純にパスを使用して@ind
インデックスを作成すると、列インデックスファイルをデータファイルから概念的に分離するのに役立ちます。
my @ind = "/path/to/index.csv".IO.lines;
コマンドラインからデータを取得することを好む場合、Rakuは@*ARGS
そのための配列を提供します。 RakuのIO
ルーチンを使用して、ファイルが必要な順序で入力されていることを確認してください。
~$ raku -e 'my ($header,@a) = @*ARGS[0].IO.lines.map: *.split(/ \s+ /);
$header .= list; my @ind = @*ARGS[1].IO.lines;
my @col = (0...3, $header.grep( / @ind /, :k ).Slip);
put $header[@col].join("\t");
say $_.join("\t") for @a.map: *.[@col];' data.csv index.csv
chrom pos ref alt a1 a4
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd
10 12345 C T aa dd