使用Bash脚本计算均值,方差和范围

时间:2012-02-22 01:02:05

标签: bash numeric

给定文件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)的均值,方差和范围,是否有任何想法?感谢。

4 个答案:

答案 0 :(得分:7)

这是一个awk的解决方案,它计算:

  • 最小=每行最小值
  • 最大值=每行最大值
  • average =μ=每行所有值的总和除以数字的数量。
  • 方差= 1 / n×[(Σx)² - Σ(x²)]其中
    n =行上的值的数量= NF - 1(以awk,NF =行上的字段数量)
    (Σx)²=线上值之和的平方
    Σ(x²)=线上值的平方和

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