数値による全項目並べ換え

実践的データ処理として、複数の項目を持つレコードを ある数値を規準に並べ換えてみよう。

レコードとフィールド

コンピュータでデータ管理をするときには、どんなデータもかならず 同じ項目が含まれるようにする。たとえば、あるデータベースを作るときに

という項目のものを集めると決めたら、全てのデータがこれらの項目だけを 含むようにしなければならない。必要な項目を満たした1件のデータのことを レコードといい、1レコード内の各項目のことをフィールド という。

〜〜〜〜〜〜〜〜〜〜〜〜〜レコード〜〜〜〜〜〜〜〜〜〜〜〜〜
公益太郎 コウエキタロウ 酒田市飯森山 1111 最上川
フィールド フィールド フィールド フィールド フィールド

これらを実際にコンピュータに入力するためのデータ形式としては、 CSV形式 (Comma Separated Value) がもっとも標準的である。 CSV形式は1行が1レコードで、フィールドごとに項目をカンマ(,) で区切ったものである。

公益太郎,コウエキタロウ,酒田市飯森山,1111,最上川
飯森花子,イイモリハナコ,酒田市飯森山,2222,赤川
酒田三吉,サカタサンキチ,酒田市中町,3333,新井田川

データは全てテキストで記述されるので、たとえ専用のデータベース管理ソ フトウェアが無くても読むことができる。さらに、CSV形式で保存された データはほぼ全てのデータベースソフト、表計算ソフトで扱うことができる。 プログラムでデータ処理をしたあとで、それに基づいたグラフなどを 作成する場合にはCSV形式で処理結果を出力しておくと良い。

データの並べ換え

以下のようなデータを並べ換えてみよう。

巨人       37 35  1
ヤクルト   37 35  0
横浜       20 52  0
中日       38 34  0
阪神       50 21  1
広島       30 35  0

このデータは、6つのレコードからなり、それぞれ4つのフィールドで構成さ れる。各フィールドは順に、チーム名、勝数、負数、引き分け数となる。 このデータを1行、つまり1レコードずつ読み込んで、各フィールドに切り分けて 読み込む部分は次のようになるだろう。

  char buf[100];
  FILE *cl;
  if (NULL == (cl=fopen("celeague.dat", "r"))) {
    fprintf(stderr, "celeague.dat にデータを置いて下さい\n");
    exit(1);
  }
  /* ここから読み込み */
  while (NULL != fgets(buf, sizeof buf, cl)) {
    if (3 == sscanf(buf, "%s %d %d", team[n], &win[n], &lose[n])) {
      /* 3つのデータがちゃんと解析できたら n を1増やす */
      n++;
    }
  }
  fclose(cl);

これにより、2次元char型配列 team にチーム名、 int型配列 win, lose にそれぞれ勝数、負数が入る。

前々回のソートのアルゴリズムを利用して 並べ換えよう。並べ換える規準は勝率にする。データの読み込みによって win[i], lose[i] に勝敗数が入っている。ここから勝率を計算す るには

(float)win[i]/(win[i]+lose[i])

とすれば良い。win, lose ともにint型の配列なので、 結果が小数となる勝率を計算するときは (float) によって float型にキャストすることを忘れないように注意する。

以上のことをふまえて、勝敗表データを勝率の高い順に並べ換えて、 なおかつ結果をCSV形式で出力するプログラムは以下のように書ける。

celeague.c

#include <stdio.h>
#include <string.h>
#define TL	20

int main()
{
  char buf[100];
  int  n=0;
  FILE *cl;
  char team[6][TL];             /* チーム名 */
  char scanfmt[10];             /* sscanf用フォーマット */
  int  win[6], lose[6];         /* 勝ち 負け */
  char temp[TL];                /* 並べ換え用 */
  int  i, left, x;              /* 並べ換え用 */
  float r, s;
  if (NULL == (cl=fopen("celeague.dat", "r"))) {
    fprintf(stderr, "celeague.dat にデータを置いて下さい\n");
    exit(1);
  }
  sprintf(scanfmt, "%%%ds %%d %%d", TL-1);
  /* ここから読み込み */
  while (NULL != fgets(buf, sizeof buf, cl)) {
    if (3 == sscanf(buf, scanfmt, team[n], &win[n], &lose[n])) {
      /* 3つのデータがちゃんと解析できたら n を1増やす */
      n++;
    }
  }
  /* ここから勝率で並べ換え(単純ソート) 
   * 勝率は 勝数/(勝数+負数) */
  for (left=0; left<n-1; left++) {
    for (i=left+1; i<n; i++) {
      r = (float)win[left]/(win[left]+lose[left]); /* 着目点の勝率 */
      s = (float)win[i]/(win[i]+lose[i]); /* 比べる相手の勝率 */
      if (r < s) {
        strlcpy(temp, team[left], TL);		/* チーム名の入れ換え */
        strlcpy(team[left], team[i], TL);
        strlcpy(team[i], temp, TL);
        x = win[left];				/* 勝数の入れ換え */
        win[left] = win[i];
        win[i] = x;
        x = lose[left];				/* 負数の入れ換え */
        lose[left] = lose[i];
        lose[i] = x;
      }
    }
  }
  /* ここから表示 */
  for (i=0; i<n; i++) {
    r = (float)win[i]/(win[i]+lose[i]);
    printf("%s,%d,%d,%5.3f\n", team[i], win[i], lose[i], r);
  }
}

celeague.datを 同一ディレクトリに置いて実行してみよう。CSV形式で出力される。

% gcc -o celeague celeague.c
% ./celeague
阪神,50,21,0.704
中日,38,34,0.528
巨人,37,35,0.514
ヤクルト,37,35,0.514
広島,30,35,0.462
横浜,20,52,0.278

目次