各行の一部を個別のファイルに出力します


14

このようなファイルがあります:

a   AGTACTTCCAGGAACGGTGCACTCTCC
b   ATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT
c   ATATTAAATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCATCCACTCCACAC
d   ATCAGTTTAATATCTGATACGTCCTCTATCCGAGGACAATATATTAAATGGA
e   TTTGGCTAAGATCAAGTGTAGTATCTGTTCTTATAAGTTTAATATCTGATATGTCCTCTATCTGA

a.seqsequenceを含むファイルを作成したいAGTACTTCCAGGAACGGTGCACTCTCC。同様にb.seqが含まれていますATGGATTTTTGGAGCAGGGAGATGGAATAGGAGCATGCTCCAT。つまり、Column1を拡張子付きの出力ファイル名として使用し.seq、それに対応するcolumn2シーケンスを含める必要があります。perlスクリプトを書くことでこれを行うことができますが、コマンドラインに何かあれば役立ちます。すぐに聞いてほしい。

回答:


16

私のスナップ応答はそうだったでしょうが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で実行するようにアップグレードする方が、コードを改善するよりも良い投資になる可能性があります。

明らかに、これらの処理に費やした時間では、おそらく最も遅いオプションで数億件のレコードを処理できたでしょう。awkループの記述またはBash のみが可能な場合は、それを実行して、作業を続けます。今日は明らかに余暇が多すぎた。

また、いくつかのマルチスレッドオプション(C ++とPython、およびGNUとのハイブリッドparallel)をテストしましたが、スレッドのオーバーヘッドは、このような単純な操作(文字列分割、書き込み)の利点を完全に上回ります。

Perl

awkgawkここ)正直に言って、このようなデータをテストするための最初の呼び出しポートですが、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を「遅い言語」として書く前に、そのことを覚えておいてください。

C

CPUに実際に何をさせることができるかを確認するためにこの例を始めましたが、率直に言って、長い間触れなかった場合、Cはコードの悪夢です。これには100文字の行に制限されるというマイナス面がありますが、展開するのは非常に簡単ですが、必要ありませんでした。

私の元のバージョンはC ++やpypyよりも遅かったのですが、それについてブログを書いた、Julian Klodeから助けを得ました。このバージョンは、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よりもはるかに簡単に書くことができます。特に、文字列や入力に関しては、あらゆる種類のものがあります。つまり、実際にはロジックを単純化できます。strtokCでは、文字列全体を処理し、面倒なメモリ割り当てをすべて行う必要があるため、豚です。これは、タブに到達するまで線に沿って点滅し、必要に応じてセグメントを引き出します。

#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 Parallel

(moreutilsバージョンではありません)。簡潔でわかりやすい構文ですが、OMGSLOWです。間違って使用している可能性があります。

parallel --colsep '\t' echo {2} \> {1}.seq <infile

テストハーネスジェネレーター

[ATGC] * 64の100000行のデータジェネレーターを次に示します。それは高速ではなく、改善は大歓迎です。

cat /dev/urandom | tr -dc 'ATGC' | fold -w 64 | awk 'NR>100000{exit}{printf NR"\t"$0"\n"}' > infile

2
パフォーマンスに関するすべてのオプションを列挙することは、頭に浮かぶ最初のことを行うのと同じくらい無駄になる可能性があることを指摘する必要があります。awk数千万未満の場合でもまだ良い答えです。これを[直線的に] 10億行に拡大しても、CはPerlで1.5時間、awkで3.6時間しか節約できません。
オリ

現在、私のC ++バージョンは非常に高速になっています。たぶん、巨大なデータセットのより単純なテキスト処理のためにC ++を検討するでしょう。ほぼ2倍の速さで、数十億行に達すると時間も違います。
オリ



1
テストハーネスの生成速度は乱数ジェネレーターによって制限されると思います。与えられたすべての数値を使用するか、同種の分布を生成することで、高速化できますpaste <(yes A) <(yes T) <(yes G) <(yes C) | head -n1600000 | tr '\t' '\n' | shuf | tr -d \\n | fold -w64 | cat -n > infile。例:。
トール

13

純粋なシェルの実装:

while read -r filename content ; do
    printf '%s\n' "$content" >> "${filename}.seq"
done < /source/file

12

を使用して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

こんにちは、これはうまくいきます。コードについて少し説明してもらえますか?
user3138373 14年

助け@ user3138373希望...
jasonwryan

それは役立ちます。ありがとうprintfの代わりに作品を印刷しないのはなぜですか?
user3138373 14年

3
行が多い場合、利用可能なすべてのファイル記述子が使用されるため、おそらくを追加する必要がありますclose($1".seq")
トール

1
@Thor、本当。awkただし、GNUのような一部の実装は、その回避方法を知っています。
ステファンシャゼル14年

3

GNU sedでできる方法の1つを次に示します。

<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:e; d'

または、より効率的に、glenn jackmanが示唆するように

<infile sed -r 's:(\w+)\s+(\w+):echo \2 > \1.seq:' | sh

1
それはクールですが、すべての行に対して外部コマンドを生成する必要があるため、非常に非効率的です。sedがすべての生のコマンドを出力し、出力を「sh」にパイプするのが少し良いでしょう
グレンジャックマン14年

1
@glennjackman:これは、それを行うための興味深い代替方法に過ぎませんでした。入力が大きい場合、awkおそらく最も効率的なツールです。もちろんsh、各行にスポーンしないことは正しいです。代わりにpipeオプションを追加しました。
トール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.