別のファイルに保存されている行番号に基づいて大容量ファイルから行を抽出する簡単な方法

別のファイルに保存されている行番号に基づいて大容量ファイルから行を抽出する簡単な方法

800億行を含む大容量ファイルがあります。今、いくつかの行(約10000行)を抽出したいと思います。行番号を知っていて、これを処理する最速の方法は何ですか?

この行を抽出するために、行番号を含む他のファイルを使用できますか?行番号ファイルの行番号が常に連続するわけではありません。

たとえば、ソースファイルは次のようになります。

0.1
0.2
0.3
0.4
...

行番号ファイル:

1
3
4

出力:

0.1
0.3
0.4

ベストアンサー1

以下はそれ以外の選択肢といくつかのベンチマークです。Zhou Weijunの回答から

join

data行を抽出するファイルと抽出する行数をリストするファイルがあるとし、line_numbers出力のソート順序が重要でない場合は、次のようにします。

join <(sort padded_line_numbers) <(nl -w 12 -n rz data) | cut -d ' ' -f 2-

これにより、ファイルの行に番号が付けられ、最初のフィールドdatapadded_line_numbersデフォルト)のファイルと結合され、共通の行が印刷されます(切り捨てられた結合フィールド自体を除く)。

join入力ファイルはアルファベット順にソートする必要があります。上記のファイルは、ファイルの各行をpadded_line_numbers左に記入して準備する必要があります。line_numbersたとえば、

while read rownum; do
    printf '%.12d\n' "$rownum"
done <line_numbers >padded_line_numbers

オプション-w 12 -n rzと引数は、nl前にゼロが付いた12桁の長い数字の出力を指示します。

出力のソート順序がline_numbersファイルのソート順序と一致する必要がある場合は、以下を使用できます。

join -1 2 -2 1 <(nl padded_line_numbers | sort -k 2,2) \
    <(nl -w 12 -n rz data) |
    sort -k 2,2n |
    cut -d ' ' -f 3-

padded_line_numbersファイルに番号を付け、結果を2番目のフィールドに基づいてアルファベット順に並べ替え、番号付きファイルに関連付け、結果を元の並べ替え順dataに番号順に並べ替えますpadded_line_numbers

ここでは、便宜上、プロセス置換を使用します。これに依存できない、または依存したくなく、中間結果を保持するために通常のファイルを作成するために必要なストレージスペースを無駄にする意図がない場合は、名前付きパイプを利用できます。

mkfifo padded_line_numbers
mkfifo numbered_data

while read rownum; do
    printf '%.12d\n' "$rownum"
done <line_numbers | nl | sort -k 2,2 >padded_line_numbers &

nl -w 12 -n rz data >numbered_data &

join -1 2 -2 1 padded_line_numbers numbered_data | sort -k 2,2n | cut -d ' ' -f 3-

ベンチマーク

問題の特異性はファイルの行数なので、data適切な量のデータで代替をテストするのが役に立つと思いました。

テストでは、32億行のデータファイルを使用しました。各行は、およびopenssl encを使用して16進数でエンコードされた2バイトのゴミであり、od -An -tx1 -w2以下を使用してスペースを削除しましたtr -d ' '

$ head -n 3 data
c15d
061d
5787

$ wc -l data
3221254963 data

このファイルは、GNU Coreutilsを使用して1から3,221,254,963までの10,000の数字を繰り返し無作為に選択することによってline_numbers作成されました。shuf

shuf -i 1-"$(wc -l <data)" -n 10000 >line_numbers

bashテスト環境は、i7-2670QM Intelクアッドコアプロセッサ、16GiBメモリ、SSDストレージ、GNU / Linux、5.0、およびGNUツールを備えたノートブックです。
私が測定した唯一の次元は、timeシェル組み込み関数による実行時間でした。

私がここで考えているのは次のとおりです。

perl一番早いようです。

$ time perl_script line_numbers data | wc -l
10000

real    14m51.597s
user    14m41.878s
sys     0m9.299s

awkパフォーマンスはかなりよさそうだ。

$ time awk 'FNR==NR { seen[$0]++ }; FNR!=NR && FNR in seen' line_numbers data | wc -l
10000

real    29m3.808s
user    28m52.616s
sys     0m10.709s

join、また同様のようです。

$ time join <(sort padded_line_numbers) <(nl -w 12 -n rz data) | wc -l
10000

real    28m24.053s
user    27m52.857s
sys     0m28.958s

上記の順序付けられたバージョンは、このバージョンと比較してパフォーマンスの低下がほとんどないことに注意してください。

最後に目立つようにsed遅くなったようでした。約9時間後に終了しました。

$ time sed -nf <(sed 's/$/p/' line_numbers) data | wc -l
^C

real    551m12.747s
user    550m53.390s
sys     0m15.624s

おすすめ記事