次のファイルがあります。
a AGTACTTCCAGGAACGGTGCACTCTCC
b ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA
a.seq
シーケンスを含むファイルを作成したいですAGTACTTCCAGGAACGGTGCACTCTCC
。またb.seq
含まれますATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
。つまり、Column1は拡張子を持つ出力ファイル名として使用する必要があり、.seq
その列2の順序が必要です。 Perlスクリプトを書くことでこれを行うことができますが、コマンドラインのすべてが役に立ちます。すぐに聞きたいです。
答え1
私の最初の反応は次のようになりました。awk
しかし、多くの行(何百万もの行を話すもの)を処理する場合は、「実際の」プログラミング言語に切り替えることで実質的な利点を得ることができます。
これを念頭に置いて(そしてすでにawk
答えを考慮して)、さまざまな言語でいくつかの実装を作成し、PCI-E SSDの同じ10,000行のデータセットでベンチマークしました。
me* (C) 0m1.734s
me (C++) 0m1.991s
me (Python/Pypy) 0m2.390s
me (perl) 0m3.024s
Thor+Glenn (sed|sh) 0m3.353s
me (python) 0m3.359s
jasonwryan+Thor (awk) 0m3.779s
rush (while read) 0m6.011s
Thor (sed) 1m30.947s
me (parallel) 4m9.429s
一見するとCが一番良く見えるが、そんなに早く走れば豚だ。 PypyとC ++は作成と実行が簡単です。十分何十億もの行について話さない限り。もしそうなら、RAMまたはSSDですべてのタスクを実行するようにアップグレードすることは、コードの改善よりも優れた投資かもしれません。
明らかに私がこれを見ている間、あなたはおそらく何億ものレコードを処理したでしょう。最も遅いオプションのうち。作成またはBashループしかできない場合は、awk
そうして人生を続けてください。今日は間違いなく時間が多すぎると思います。
また、いくつかのマルチスレッドオプション(C ++とPythonとGNUとの混合parallel
)をテストしましたが、これらの単純な操作(文字列分割、書き込み)の利点よりもスレッドのオーバーヘッドがはるかに大きかったです。
パール
awk
(gawk
ここ)は正直なところ、この種のデータをテストするための最初の呼び出しポートですが、Perlでもかなり似たようなことができます。構文は似ていますが、書き込み処理が少し優れています。
perl -ane 'open(my $fh, ">", $F[0].".seq"); print $fh $F[1]; close $fh;' infile
Python
私良いPython。それは私が毎日使用している仕事の言語で、素晴らしい、信頼できる、非常に読みやすい言語です。初心者でもここで何が起こっているのか推測できます。
with open("infile", "r") as f:
for line in f:
id, chunk = line.split()
with open(id + ".seq", "w") as fw:
fw.write(chunk)
ディストリビューションのpython
バイナリがPythonの唯一の実装ではないことを覚えておく必要があります。Pypyで同じテストを実行すると、Cより速いもはやロジックの最適化は必要ありません。 Pythonを「遅い言語」と考える前にこの点に留意してください。
氏
私は私のCPUで実際に何ができるかを知るためにこの例を始めましたが、正直に長い間Cに触れていない場合は、コーディングするのは悪夢です。これには100文字の行に制限されているという追加の欠点があり、それを拡張することは非常に簡単ですが必要ありません。
私の元のバージョンはC ++とpypyより遅かったが、ブログの後私は持っていますJulien Claudeの助け。このバージョンは、IOバッファの調整のために最も高速です。これもたくさんどんなものよりも長く深い。
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#define BUFLEN (8 * 1024)
int main(void) {
FILE *fp;
FILE *fpout;
char line[100];
char *id;
char *token;
char *buf = malloc(BUFLEN);
fp = fopen("infile", "r");
setvbuf ( fp , buf , _IOLBF, BUFLEN );
while (fgets(line, 100, fp) != NULL) {
id = strtok(line, "\t");
token = strtok(NULL, "\t");
char *fnout = malloc(strlen(id)+5);
fnout = strcat(fnout, id);
fnout = strcat(fnout, ".seq");
fpout = fopen(fnout, "w");
setvbuf ( fpout , NULL , _IONBF , 0 );
fprintf(fpout, "%s", token);
fclose(fpout);
}
fclose(fp);
return 0;
}
C++
よく行動しているたくさん実際のCよりも書き込みが簡単です。役に立つすべての種類の資料があります(特に文字列と入力に関して)。これが意味するのは、実際に論理を単純化できることです。strtok
Cでは、文字列全体を処理し、退屈なメモリ割り当てをすべて実行する必要があるため、大きな問題です。ラベルに触れるまで線に沿ってスライドし、必要に応じてセグメントを引き出します。
#include <fstream>
#include <string>
using namespace std;
int main(void) {
ifstream in("infile");
ofstream out;
string line;
while(getline(in, line)) {
string::size_type tab = line.find('\t', 0);
string filename = line.substr(0, tab) + ".seq";
out.open(filename.c_str());
out << line.substr(tab + 1);
out.close();
}
in.close();
}
GNUパラレル
(moreutilsバージョンではありません)これは簡潔な構文ですが、遅すぎます。私が間違って使用したかもしれません。
parallel --colsep '\t' echo {2} \> {1}.seq <infile
テストハーネスジェネレータ
これは100000行[ATGC]*64のデータジェネレータです。スピードは速くなく、改善は非常に歓迎されています。
cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile
答え2
使用awk
:
awk '{printf "%s\n", $2>$1".seq"}' file
指定されたfile
各レコード()の2番目のフィールドを、名前$2
に追加された最初のフィールド()の名前付きファイルとして印刷します。$1
.seq
〜のようにトルが指摘した説明内の大規模なデータセットの場合、ファイル記述子が不足している可能性があるため、次のことをお勧めします。書き込み後、各ファイルを閉じます。:
awk '{printf "%s\n", $2>$1".seq"; close($1".seq")}' file
答え3
純粋なシェルの実装:
while read -r filename content ; do
printf '%s\n' "$content" >> "${filename}.seq"
done < /source/file
答え4
GNU sedを使用してこれを達成する1つの方法は次のとおりです。
<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:e; d'
または提案されているように、より効率的にグレンジャックマン:
<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:' | sh