Unixの長い列に1行に1つの値を掛け、0.01秒間隔で増加する大きなファイルがあります。 1日分のデータの場合、これは864万行に相当します。
135699840000
135699840001
135699840002
135699840003
135699840004
このファイルの各行から各行のシリアル日付番号(参照年01/01/0000のmatlabの日付カウンタ)を計算するコマンドを実行したいと思います。
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629
私はコーディングが初めてですが、whileループを使用して動作するようにしました。しかし、これは非常に非効率的で、実行には数時間かかります。
while read epochtimerange; do
echo "scale=10; (($epochtimerange/(100*86400))+719529)" |bc
done < epochtimerangetmp.txt > serialdaterangetmp.txt
私はawkを使って実行する方法があるはずですが、うまくいきません。重要なことは、出力で小数点以下10桁の精度を維持できることです。
誰でも私を助けることができますか?ありがとうございます。
答え1
私たち全員が知っているように、シェルは非常に遅いです。
あなたが要求することは次のようにシェルで達成することができます:
#!/bin/bash
while read line; do
bc <<<"scale=10;($line/(100*86400))+719529"
done <datafile
1000行を処理するのに約1.1秒かかります。
864万枚の写真はすべて約2時間41分かかります。
また、bc の数値結果は正しく丸められません。
例の5行は次の値を生成します。
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629
より多くの数字を表示するには、精度を20に変更してみましょう。
735235.00000000000000000000
735235.00000011574074074074
735235.00000023148148148148
735235.00000034722222222222
735235.00000046296296296296
たとえば、で終わる3番目の数字2314
は誤って丸められ、次の数字はに丸められなければならないこと4
が示されます。8
5
AWK
awkを使用すると、より高速なソリューションを得ることができます。 awkが要求したものを実装すると、次のようになります。
$ awk '{printf ("%.10f\n",($0/(100*86400))+719529)}' datafile
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003473
735235.0000004630
1000行を処理するには0.006(6ミリ秒)しかかかりません。 864万行すべてを約50秒で処理する必要があります。
しかし、awkは精度範囲を超えました。デフォルトでは、64ビット浮動小数点値を使用して表されます。これは精度は小数点以下15桁程度です。。データ結果の整数部分は6桁で、分数部分は8桁目まで正確に推定できます。
実際にビット数を拡張しようとすると、次のようになります。
awk '{printf ("%.20f\n",($0/(100*86400))+719529)}' datafile
私たちが得るのは騒音だけです。
735235.00000000000000000000
735235.00000011571682989597
735235.00000023143365979195
735235.00000034726690500975
735235.00000046298373490572
より正確なbc結果と比較:
735235.00000000000000000000
735235.00000000000000000000
735235.00000011571682989597
735235.00000011574074074074
735235.00000023143365979195
735235.00000023148148148148
735235.00000034726690500975
735235.00000034722222222222
735235.00000046298373490572
735235.00000046296296296296
この問題を実際に解決するには、より正確なawkが必要です。
多精度AWK
GNU awk(ここではgawkと呼びます)を使用してMPFR(Multiple Precision Floating Point Library)でコンパイルすると、はるかに高い精度が得られます。
あなたのawkにこのライブラリがあることを確認してください(バージョンにお問い合わせください):
$ awk --version
GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.5, GNU MP 6.1.1)
Copyright (C) 1989, 1991-2015 Free Software Foundation.
そして、使用可能な精度を使用するようにawkコマンドを変更します。
gawk -M -v PREC=100 '{printf ("%.20f\n",($0/(100*86400))+719529)}' datafile
735235.00000000000000000000
735235.00000011574074074074
735235.00000023148148148148
735235.00000034722222222222
735235.00000046296296296296
結果は高精度bcと同じです。
この場合、awkの速度とbcの精度が得られます。
10進数10桁で要求された最終コマンドは次のとおりです。
gawk -M -v PREC=100 '{printf ("%.10f\n",($0/(100*86400))+719529)}' datafile
735235.0000000000
735235.0000001157
735235.0000002315
735235.0000003472
735235.0000004630
すべての値は正しく丸められます。
答え2
簡単な方法:ex
修正行を使用してバッファ全体(修正ファイル)をに渡しますbc
。その後、変更されたバージョンを印刷します。
printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!' | ex file.txt
サンプルファイルの出力:
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003472
735235.0000004629
または印刷するのではなく、変更を保存してください。
printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' x | ex file.txt
説明する:
ex
渡されたコマンドを表示するには、printf
コマンドを単独で実行します。
$ printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!'
%s:.*:&/8640000+719529:
0a
scale=10
.
%!bc
%p
q!
ex
さて、それらを命令に分けてみましょう。最初はもっと複雑なので、特別に説明形式を指定しました。
%s:.*:&/8640000+719529:
% - For every line of the buffer (file)
s - Run a substitute command
: - Using ':' as the regex delimiter
.* - Match each entire line
: - and replace with
& - The entire line, followed by
/8640000+719529 - this text
: - End command
0a
「行 0 の後にテキストを追加」、つまりバッファ (ファイル) の先頭にテキストを追加するという意味です。
textはscale=10
追加するリテラルテキストです。
1行.
自体が「追加」コマンドを終了します。
このコマンドは、%!bc
バッファ全体の内容を標準入力として外部コマンドに渡し、bc
バッファ全体を結果出力に置き換えます。
%p
バッファ全体を標準出力として印刷する方法です。
q!
変更を保存せずに終了することを示します。
お持ちの場合非常に非常に大きいファイルには数千万行があり、これは明らかに問題を引き起こすでしょう。私はこの用途のための可能な解決策を研究しましたが、ex
いくつかの方法があります。できる完了しました。しかし、私はまだこれを使用する非常に簡単なアプローチを好み、このアプローチをあきらめました。POSIX専用ツール。
使用split
ファイルをチャンクに分割し、cat
結果出力とともに各チャンクに対して以前に指定されたコマンドを実行します。
split -l 1000000 -a 3 file.txt myprefix.
for f in myprefix.???; do
printf '%s\n' '%s:.*:&/8640000+719529:' 0a scale=10 . '%!bc' %p 'q!' |
ex "$f"
done > myoutputfile.txt
rm myprefix.???
split
ここで、コマンドは、各行が百万減のチャンクに分割するために使用されますfile.txt
(もちろん、残りもファイルに保存されます)。指定した通り、-a 3
ブロックのサフィックス長は3文字です。 myprefix.aaa
、、myprefix.aab
など。
その後、各ファイルを個別に処理でき、ex
ループ全体の出力を次にリダイレクトするため、変更を保存する必要はありませんmyoutputfile.txt
(その後、チャンクファイルを削除してきれいに保ちます)。
答え3
シェルでこれを行うと、非常に遅くなります。
$ awk '{printf "%.10f\n", (($1/(100*86400))+719529)}' filename
735235.0000000000
735235.0000001157
735235.0000002314
735235.0000003473
735235.0000004630
最後の項目に示すように、わずかに異なる丸め結果が得られます。