给定文件file.txt:
AAA 1 2 3 4 5 6 3 4 5 2 3
BBB 3 2 3 34 56 1
CCC 4 7 4 6 222 45
对于如何使用Bash脚本分别计算每个项目(即AAA,BBB,CCC)的均值,方差和范围,是否有任何想法?感谢。
答案 0 :(得分:7)
这是一个awk
的解决方案,它计算:
NF
- 1(以awk,NF
=行上的字段数量)
awk '{
min = max = sum = $2; # Initialize to the first value (2nd field)
sum2 = $2 * $2 # Running sum of squares
for (n=3; n <= NF; n++) { # Process each value on the line
if ($n < min) min = $n # Current minimum
if ($n > max) max = $n # Current maximum
sum += $n; # Running sum of values
sum2 += $n * $n # Running sum of squares
}
print $1 ": min=" min ", avg=" sum/(NF-1) ", max=" max ", var=" ((sum*sum) - sum2)/(NF-1);
}' filename
输出:
AAA: min=1, avg=3.45455, max=6, var=117.273
BBB: min=1, avg=16.5, max=56, var=914.333
CCC: min=4, avg=48, max=222, var=5253
请注意,您可以将awk脚本(单引号之间的所有内容,但不包括单引号)保存在文件中,例如名为script
,并使用awk -f script filename
执行
答案 1 :(得分:1)
您可以使用python
:
$ AAA() { echo "$@" | python -c 'from sys import stdin; nums = [float(i) for i in stdin.read().split()]; print(sum(nums)/len(nums))'; }
$ AAA 1 2 3 4 5 6 3 4 5 2 3
3.45454545455
答案 2 :(得分:1)
第1部分(平均):
mean () {
len=$#
echo $* | tr " " "\n" | sort -n | head -n $(((len+1)/2)) | tail -n 1
}
nMean () {
echo -n "$1 "
shift
mean $*
}
平均用法:
nMean AAA 3 4 5 6 3 4 3 6 2 4
4
第2部分(差异):
variance () {
count=$1
avg=$2
shift
shift
sum=0
for n in $*
do
diff=$((avg-n))
quad=$((diff*diff))
sum=$((sum+quad))
done
echo $((sum/count))
}
sum () {
form="$(echo $*)"
formula=${form// /+}
echo $((formula))
}
nVariance () {
echo -n "$1 "
shift
count=$#
s=$(sum $*)
avg=$((s/$count))
var=$(variance $count $avg $*)
echo $var
}
用法:
nVariance AAA 3 4 5 6 3 4 3 6 2 4
1
第3部分(范围):
range () {
min=$1
max=$1
for p in $* ; do
(( $p < $min )) && min=$p
(( $p > $max )) && max=$p
done
echo $min ":" $max
}
nRange () {
echo -n "$1 "
shift
range $*
}
用法:
nRange AAA 1 2 3 4 5 6 3 4 5 2 3
AAA 1 : 6
nX是命名X的缩写,名为mean,命名方差,.... 注意,我使用整数运算,也就是说,shell的可能性。例如,要使用浮点运算,可以使用bc。在这里你松散精度,这可能是大自然数可以接受的。
处理输入行的所有3个命令:
processLine () {
nVariance $*
nMean $*
nRange $*
}
逐行读取文件中的数据:
# data:
# AAA 1 2 3 4 5 6 3 4 5 2 3
# BBB 3 2 3 34 56 1
# CCC 4 7 4 6 222 45
while read line
do
processLine $line
done < data
与我的期望相反,用bc
中的函数处理未知数量的参数似乎并不容易,例如min (3, 4, 5, 2, 6)
。
但是如果输入是整数,则需要调用bc可以减少到2个位置。我使用的精度为2(“scale = 2”) - 您可以根据需要进行更改。
variance () {
count=$1
avg=$2
shift
shift
sum=0
for n in $*
do
diff="($avg-$n)"
quad="($diff*$diff)"
sum="($sum+$quad)"
done
# echo "$sum/$count"
echo "scale=2;$sum/$count" | bc
}
nVariance () {
echo -n "$1 "
shift
count=$#
s=$(sum $*)
avg=$(echo "scale=2;$s/$count" | bc)
var=$(variance $count $avg $*)
echo $var
}
其余代码可以保持不变。请验证方差的公式是否正确 - 我使用了我的想法:
对于值(1,5,9),我总结(15)除以计数(3)=&gt; 5。 然后我为每个值(-4,0,4)创建平均差异,构建正方形(16,0,16),将它们相加(32)并除以计数(3)=&gt; 10.66
这是正确的,还是我需要一个平方根;)?
注意,我必须更正平均值计算。对于1,5,9,平均值是5,而不是1 - 我对吗?它现在使用sort -n
(数字)和(len+1)/2
。
答案 3 :(得分:0)
接受的答案中有一个拼写错误导致差异被误算。在print
声明中:
", var=" ((sum*sum) - sum2)/(NF-1)
应该是:
", var=" (sum2 - ((sum*sum)/NF))/(NF-1)
此外,最好使用类似Welford's algorithm的内容来计算方差;当方差相对于平均值较小时,接受答案中的算法是不稳定的:
foo="1 2 3 4 5 6 3 4 5 2 3";
awk '{
M = 0;
S = 0;
for (k=1; k <= NF; k++) {
x = $k;
oldM = M;
M = M + ((x - M)/k);
S = S + (x - M)*(x - oldM);
}
var = S/(NF - 1);
print " var=" var;
}' <<< $foo