2017-01-26 28 views
-3

私は約4億行と3列のファイルを扱っています。最初の2つの列は英数字の文字列で、最後は数値です。このようなもの:連想配列:PythonとPerlとAwk Performaceとの比較

NM_001011874.1,NM_011441.4,-0.131672299779153 

私はほぼ同じ数の行と3つの列を持つ複数のファイルがあります。これらは必ずしも3つの列のいずれかに基づいてソートされるとは限りません。私は最初の2つの列の組み合わせに基づいてこれらのファイルを結合しようとしています。例えば:私は何をしようとしている

File 1 

NM_001011874.1,XR_104537.1,-0.929524370261122 
NM_001011874.1,XM_003084433.1,-0.603098764428879 

File 2 

NM_001011874.1,XR_104537.1,-0.11254525414 
NM_001011874.1,NM_005040.1,-0.20509876488 

File 3 

NM_001011874.1,XR_104537.1,-0.41254525414 
NM_001011874.1,NM_005040.1,-0.60509876488 

は、最初の2つの列の値の組み合わせを使用し、そのペアの3番目の列から対応する値を取得することにより、キーを作成することです。私が手に最終的な出力は次のようになります。

Output2 

NM_001011874.1,XR_104537.1,-0.11254525414,-0.929524370261122,-0.41254525414 
NM_001011874.1,NM_005040.1,-0.20509876488,,-0.60509876488 

私は上記を行うためにはawkを使用しています:

awk -F',' 'NR==FNR{a[$1,$2]=$3;next}{$4=a[$1,$2];print}' OFS=',' file1.txt file2.txt 

は私が仕事のために256ギガバイトを割り当てています。各ファイルの行数が4億〜3列の2つのファイルを組み合わせて出力を生成するには、上記のコマンドを使用すると約90分かかります。出力ファイルには、400億行がありますが、4列あります。追加される列ごとに、出力ファイルを生成するためにかかる時間が長くなります。

私はこれを順番に実行しています。つまり、file1とfile2をマージして、4つの列を持つoutput1を生成します。次に、file3とoutput1をマージして5列のoutput2を作成し、file4とoutput2を作成して6列のoutput3を生成します。最後の出力が22列になるまで続きます。

PythonやPerlでこれを行うのがスピードと自動化の面で効率的かどうか疑問に思っていますか?私は3列のファイルを20個ほど持っていますが、行は1億〜4億までさまざまです。 PythonやPerlでこれを行う方が良いと思うなら、awkスクリプトがどのようにPythonやPerlに変換されるかを説明する例を挙げてください。

編集: コメント3に加えて、最終的な出力はコメントに基づいています。

+0

パンダやナンシーのような感じです。 – TigerhawkT3

+0

もちろん、あらゆる目的のためにスクリプトを用意する方がよいでしょう。なぜあなたはそれを書いていないのですか?私はまっすぐなPerlスクリプトでは2桁も速くなると思っています。 – zdim

+0

@zdimなぜあなたはawkよりも速いと思いますか? gkandoi '$ 4 = a [$ 1、$ 2]; print'を実行し、awkに' print $ 0、a [$ 1、$ 2] 'の代わりに$ 0を再コンパイルするように強制する点は何ですか? –

答えて

4

巨大なデータファイルがあり、効率的に処理したいときは、SQLiteデータベースに投げ込んでインデックスを作成してからクエリを実行するのがよいでしょう。詳しくは、my answer about CSV vs SQLite performanceを参照してください。

データ用のテーブルを作成します(このデータは何か分かりませんが、そのデータは「もの」です)。

create table stuff (
    key1 text, 
    key2 text, 
    value real 
); 

SQLiteシェルを使用してCSVをテーブルにインポートします。

sqlite> .mode csv 
sqlite> .import file1 stuff 
sqlite> .import file2 stuff 
sqlite> .import file3 stuff 

キーのインデックスを作成します。あなたの心のコンテンツへ

create index stuff_key on stuff (key1, key2); 

クエリ。

select value 
from stuff 
where key1 = "NM_001011874.1" and 
     key2 = "XR_104537.1" 

-0.929524370261122 
-0.11254525414 
-0.41254525414 

インポートとインデックス作成が完了すると、データの大きさは関係ありません。 CSVを更新してそのすべてを再インポートする代わりに、新しいフィールドだけで小さなCSVファイルをインポートすることができます。または、CSVをスキップして直接挿入することもできます。

insert into stuff (key1, key2, value) 
values ("NM_204958293.2", "XR_29238498.3", -239.2), 
     ("NM_2904892.3", "XR_3093.0", 9482.39); 

私はそれを倍の束を提唱してきましたが、それをテストしていないので、私は、これのパフォーマンスをテストしました。

まず、私はこれらのファイルが大きくなるため、ディスク領域を一掃しました。私は2011年の最先端のMacBook Pro i7でこれをやっています。幸いにも、それはアフターマーケットSSDを備えているので、I/Oパフォーマンスは優れています。それは恥ずかしがり屋ではありませんが、ラインサーバーのトップではありません。ポイントは、良いパフォーマンスを得るために派手なハードウェアは必要ないということです。

その後、私はPerl program to generate 400 million rows of dataと書いていましたが、実行中はa C program to do it fasterと書いてありました。まれに、のプログラム時間プログラマ時間より重要であり、1回限りのスクリプトでは、Cプログラムは最初に2つのほぼ同じ14Gファイルで終了しました。彼らは少し違っていますが、私たちの目的には関係ありません。

次に、テーブルを作成してインポートを開始しました。私がここに座ってそれを見つめたり、それを見つめたりする必要がないので、最初のインポート時間はあまり重要ではありません。私はそれがうまくいくことを知っている、私は一度だけそれをやらなければならないことを知っているので、(私はこの記事を編集するように)並行していくつでも作業することができます。残念ながら、SQLiteは並行して動作していないため、1つのコアだけを使用しているようです。 OTOHでは、約3メガ以上のメモリを使用していません。

4億行のファイルを1つインポートするのに20分かかりました。結果として得られるSQLiteデータベースは約17ギガであり、データの大幅な拡張はありません。私はすぐに冗長になるので、残りをするつもりはありません。

ここでインデックスを作成しています。もう一度、座って見る必要がないのは1回限りです...仮想メモリ1ギガを使用しているため、SQLiteファイルは現在30ギグです。だからもっと...ファイルを削除する。インデックスの作成には約30分かかりました。

30ギガバイトのディスクを使用してインポートおよびインデックスを作成するには50分、元のデータの約2倍。プログラミングは必要ありません。

+1

ありがとうこのオプションを認識している/思い出させることは有益です。私は、多くのデータだけではなく、リレーショナル機能にも優れており、利用可能になっていると考えています。 – zdim

関連する問題