数値(1列)でいっぱいのファイルの幾何平均を計算しようとしています。
幾何平均の基本的な式は、すべての値の自然対数(または対数)の平均で、e(または底10)をその値に引き上げます。
私の現在のbashのみのスクリプトは次のようになります。
# Geometric Mean
count=0;
total=0;
for i in $( awk '{ print $1; }' input.txt )
do
if (( $(echo " "$i" > "0" " | bc -l) )); then
total="$(echo " "$total" + l("$i") " | bc -l )"
((count++))
else
total="$total"
fi
done
Geometric_Mean="$( printf "%.2f" "$(echo "scale=3; e( "$total" / "$count" )" | bc -l )" )"
echo "$Geometric_Mean"
基本的に:
これは、小さなデータセットでは完全にうまく機能します。残念ながら、私はこれを大きなデータセットで使用しようとしています(input.txtには250,000個の値があります)。これは最終的にはうまくいくと思いますが、非常に遅いです。私はそれが完了するのに十分なほど辛抱強くなかった(45分以上)。
このファイルをより効率的に処理する方法が必要です。
Pythonを使用するなどの代替方法があります
# Import the library you need for math
import numpy as np
# Open the file
# Load the lines into a list of float objects
# Close the file
infile = open('time_trial.txt', 'r')
x = [float(line) for line in infile.readlines()]
infile.close()
# Define a function called geo_mean
# Use numpy create a variable "a" with the ln of all the values
# Use numpy to EXP() the sum of all of a and divide it by the count of a
# Note ... this will break if you have values <=0
def geo_mean(x):
a = np.log(x)
return np.exp(a.sum()/len(a))
print("The Geometric Mean is: ", geo_mean(x))
Python、Ruby、Perlなどの使用は避けたいです。
Bashスクリプトをより効率的に作成する方法について何か提案はありますか?
[このコメントを編集して、私の名前とこのがらくたの関連付けを削除してください。どうもありがとうございました]
シェルではこれを行わないでください。リモートで効率的にするための微調整はありません。シェルループはslowであり、シェルを使用してテキストを解析するのは単に悪い習慣です。スクリプト全体を次の単純なawk
ワンライナーで置き換えることができます。
awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' file
たとえば、1から100までの数字を含むファイルでこれを実行すると、次のようになります。
$ seq 100 > file
$ awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' file
37.99
速度に関しては、シェルソリューション、pythonソリューション、および上記で与えたawkを、1〜10000の数値を含むファイルでテストしました。
## Shell
$ time foo.sh
3677.54
real 1m0.720s
user 0m48.720s
sys 0m24.733s
### Python
$ time foo.py
The Geometric Mean is: 3680.827182220091
real 0m0.149s
user 0m0.121s
sys 0m0.027s
### Awk
$ time awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++} END{m=tot/c; printf "%.2f\n", E^m}' input.txt
3680.83
real 0m0.011s
user 0m0.010s
sys 0m0.001s
ご覧のように、awk
はpythonよりも高速で、はるかに簡単に記述できます。必要に応じて、「シェル」スクリプトにすることもできます。このような:
#!/bin/awk -f
BEGIN{
E = exp(1);
}
$1>0{
tot+=log($1);
c++;
}
END{
m=tot/c; printf "%.2f\n", E^m
}
または、コマンドをシェルスクリプトに保存します。
#!/bin/sh
awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++;} END{m=tot/c; printf "%.2f\n", E^m}' "$1"
ここにいくつかの提案があります。私はあなたのファイルの内容を正確に知らずにそれらをテストすることはできませんが、これが役に立てば幸いです。物事を行うには、常に異なる、より良い方法があるので、これはまったく網羅的ではありません。
if (( $(echo " "$i" > "0" " | bc -l) )); then
それを次のように変更します。
if [[ "$i" -gt 0 ]]; then
最初の行は、単純な計算を行っているだけの場合でも、複数のプロセスを作成します。解決策は[[
シェルキーワード。
else
total="$total"
これは基本的に何もしない時間を明示的に無駄にする方法です:)。これらの2行は完全に削除できます。