Bash
在 bash 中進行計算的有效方法
我正在嘗試計算一個充滿數字的文件(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"
本質上:
- 檢查輸入文件中的每個條目以確保它大於 0 每次呼叫 bc
- 如果條目 > 0,我取該值的自然對數 (l) 並將其添加到每次呼叫 bc 的執行總數中
- 如果條目 <=0,我什麼也不做
- 計算幾何平均值
這對於小型數據集非常有效。不幸的是,我試圖在大型數據集上使用它(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 腳本的任何建議?
請不要在 shell 中執行此操作。沒有任何調整可以使其遠端高效。Shell 循環很慢,使用 shell 來解析文本只是不好的做法。您的整個腳本可以用這個簡單
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
在速度方面,我在包含 1 到 10000 的數字的文件上測試了你的 shell 解決方案、python 解決方案和我上面給出的 awk:
## 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 更快,而且編寫起來也簡單得多。如果你願意,你也可以把它做成一個“shell”腳本。要麼像這樣:#!/bin/awk -f BEGIN{ E = exp(1); } $1>0{ tot+=log($1); c++; } END{ m=tot/c; printf "%.2f\n", E^m }
或者通過將命令保存在 shell 腳本中:
#!/bin/sh awk 'BEGIN{E = exp(1);} $1>0{tot+=log($1); c++;} END{m=tot/c; printf "%.2f\n", E^m}' "$1"