目次

成績集計(AWK)

 教育機関で講義、実習を担当していると
 試験をしてから点数を集計し、統計処理
 が必要になります。

 統計として、次の項目が要求されたりします。

 これらの内容は、パーソナルコンピュータが
 普及した現在では、表計算ソフトを利用する
 のが定番なのでしょう。

 表計算ソフトをインストールするのが面倒で
 専らスクリプトを書いて対応しています。

 次のようなフォーマットで、テキストファイルに
 学生番号と各問の点数が入った状態で集計と統計
 を出してみます。

201301   6  9  8  3  6  6  6  6
201302   1  2  2  1  1  3  7  5
201303   7  3  4  2  9  3  3  6
201304   1  1  9  3  2  4  2  1
201305   9  4  1 10  3  4  2  1
201306   1  9  3 10  3  7  3 10
201307   7  2  1  4  9  4  5  2
201308   4  4  4  8  9  9  3  5
201309   8  4  4 10  9  3  8  5
201310   1  6  8 10  3  4 10  9
201311   8  1  7  9  3  3  9  7
201312   6  9 10  8  1  2  5  4
201313   8  2  8  4  8  1  1  3
201314   4 10  5  2  8  7  1  3
201315   3  1  6  9  2  5  8  8
201316   6  3  2  1 10  7  3  5
201317   1  9  2  2  1  8  9  3
201318   9  2  3  8  9  9  9  5
201319   4 10  7  2  1  1  5  9
201320   9  1  3  8  1  9  5 10

 フィールドでみると2から9に8問に
 点数が入っているので、まず合計を
 求めます。

{
  result = 0
  for ( i = 2 ; i < NF+1 ; i++ ) {
    result += $i
  }
  printf("%s total(%3d)\n",$0,result)
  # print $0 " total(" result ")"
}

 フィールド数は、組込み変数NFに入っている
 ことから、第2から最終フィールドまで加算
 してしまえば、合計になります。

 フィールド数を使えば、問の数は任意で処理
 できるので、合計を求める処理は、問の数が
 変わっても使いまわせます。

 実際に処理すると、次のようになります。



 最高点、最低点、平均を求めるには
 合計を求めた後で処理します。

 ENDクローズで計算すればよいでしょう。
 初期化はBEGINクローズで指定します。

 BEGINクローズは、最高点、最低点、平均の
 値を初期すればよいので、次のようにします。

BEGIN {
  xmax = 0 
  xmin = 10000
  xavr = 0
}

 ENDクローズでは、平均を求めて最高点、最低点
 平均を表示します。レコード数が組込み変数に
 入っているので、これで計算します。

END {
  xavr /= NR
  printf("max(%d) min(%d) avr(%d)\n",xmax,xmin,xavr)
  # print "max(" xmax ") min(" xmin ") avr(" xavr ")"
}

 最高点、最低点は、1ライン毎に合計が
 求められているので、その合計と仮設定
 の最高点、最低点を比較して求めます。

{
  result = 0
  for ( i = 2 ; i < NF+1 ; i++ ) {
    result += $i
  }
  printf("%s total(%3d)\n",$0,result)
  # print $0 " total(" result ")"
  # calculate xmax
  if ( xmax < result ) {
    xmax = result
  }
  # calculate xmin
  if ( xmin > result ) {
    xmin = result
  }
  # calculate total
  xavr = xavr + result
}

 ひとつのスクリプトにまとめると以下。

BEGIN {
  xmax = 0 
  xmin = 10000
  xavr = 0
}
{
  result = 0
  for ( i = 2 ; i < NF+1 ; i++ ) {
    result += $i
  }
  printf("%s total(%3d)\n",$0,result)
  # print $0 " total(" result ")"
  # calculate xmax
  if ( xmax < result ) {
    xmax = result
  }
  # calculate xmin
  if ( xmin > result ) {
    xmin = result
  }
  # calculate total
  xavr = xavr + result
}
END {
  xavr /= NR
  printf("max(%d) min(%d) avr(%d)\n",xmax,xmin,xavr)
  # print "max(" xmax ") min(" xmin ") avr(" xavr ")"
}

 スクリプトを実行すると、次のようになります。



 得点の状況を見たいときには、合計を'*'の個数で
 表示すればよいでしょう。

{
  result = 0
  for ( i = 2 ; i < NF+1 ; i++ ) {
    result += $i
  }
  printf("%d :",$1);
  for ( i = 0 ; i < result ; i++ ) {
    printf("*")
  }
  printf("\n")
  # print ""
}

 得点状況は、次のようになります。



 CUIでは、1行が80桁程度なので、100点満点
 のような試験で得点は、2で割る等で80桁の
 中に入るようにすればよいでしょう。

 ヒストグラムは、点数の範囲を区切って処理
 すればよいので、カテゴリーをBEGINクローズ
 の中に入れて対応します。ENDクロースには
 ヒストグラム表示を担当させます。

BEGIN {
  h0 = 0
  h1 = 0
  h2 = 0
  h3 = 0
  h4 = 0
  h5 = 0
  h6 = 0
  h7 = 0
  h8 = 0
}
{
  result = 0
  for ( i = 2 ; i < NF+1 ; i++ ) {
    result += $i
  }
  printf("%s total(%3d)\n",$0,result)
  # class
  if ( result < 10                ) { h0 = h0 + 1 }
  if ( result < 20 && result >  9 ) { h1 = h1 + 1 }
  if ( result < 30 && result > 19 ) { h2 = h2 + 1 }
  if ( result < 40 && result > 29 ) { h3 = h3 + 1 }
  if ( result < 50 && result > 39 ) { h4 = h4 + 1 }
  if ( result < 60 && result > 49 ) { h5 = h5 + 1 }
  if ( result < 70 && result > 59 ) { h6 = h6 + 1 }
  if ( result < 80 && result > 69 ) { h7 = h7 + 1 }
  if ( result > 89                ) { h8 = h8 + 1 }
}
END {
  printf("\histgram \n");
  printf("0: "); for ( i = 0 ; i < h0 ; i++ ) { printf("*") ; } print "";
  printf("1: "); for ( i = 0 ; i < h1 ; i++ ) { printf("*") ; } print "";
  printf("2: "); for ( i = 0 ; i < h2 ; i++ ) { printf("*") ; } print "";
  printf("3: "); for ( i = 0 ; i < h3 ; i++ ) { printf("*") ; } print "";
  printf("4: "); for ( i = 0 ; i < h4 ; i++ ) { printf("*") ; } print "";
  printf("5: "); for ( i = 0 ; i < h5 ; i++ ) { printf("*") ; } print "";
  printf("6: "); for ( i = 0 ; i < h6 ; i++ ) { printf("*") ; } print "";
  printf("7: "); for ( i = 0 ; i < h7 ; i++ ) { printf("*") ; } print "";
  printf("8: "); for ( i = 0 ; i < h8 ; i++ ) { printf("*") ; } print "";
}

 このスクリプトで、ヒストグラムを表示させると
 次のようになります。



 ヒストグラム表示で、ある程度の得点の分布が
 わかりますが、最後に標準偏差を求めてみます。

 標準偏差は、分散の平方根なので、分散を
 求めてから、SQRTで平方根を計算します。

 分散は、得点と平均の差の2乗の合計をサンプル数
 で割った商という公式で求めます。
 文章で書くと面倒ですが、スクリプトにすると
 簡単です。

BEGIN {
  xsqr = 0
  xavr = 0
  x[0] = 0
}
{
  result = 0
  for ( i = 2 ; i < NF+1 ; i++ ) {
    result += $i
  }
  printf("%s total(%3d)\n",$0,result)
  # print $0 " total(" result ")"
  # store
  x[NR] = result
}
END {
  # average
  for (e in x) {
    xavr += x[e]
  }
  xavr /= NR
  # variable
  for(e in x){
    xsqr += (x[e] - xavr) ^ 2
  }
  ss = sqrt(xsqr/NR)
  printf("average(%d) ss(%d)\n",xavr,ss)
  # print "average(" xavr ") ss(" ss ")"
}

 平均、標準偏差を求めると、次のようになります。



 一度スクリプトを作成すると、使い回しが
 できるので、表計算ソフトと違い、採点と
 テキストファイルへの入力に集中できます。

 WindowsのDOS窓、Unixの端末、Macの端末、MS-DOS等で
 AWKが動作すれば、どんな環境でも利用できます。

 MS-DOSのようにネットワークに接続できないコンピュータ
 環境を使うと、セキュリティ上の問題を回避できます。

 SpreadSheet(表計算ソフト)を動かせない、非力な
 コンピュータであっても、成績集計ができます。


目次

inserted by FC2 system