本節では,関数 csb の結果を調べるもう一つの方法を学びます.csb の呼び出し部をもう一度みておきます.
#define NKW 5 syminfo *q; int qlen; cs_elem *cslst; int csno; WAM *wam; int wam_dir; int nc; cslst = csb(q, qlen, 0, wam, wam_dir, CS_HBC, &nc, 2, NKW);関数 csb は *q を並べかえると同時に, cs_elem 型のリストを自動的に alloc して, ここにクラスタの情報を格納します.csb の返り値である cslst は,この cs_elem 型リストへのポインタとなります. リストの長さはクラスタ数と等しく nc です.
それでは cs_elem 型を見ましょう.
struct cs_elem { syminfo *csw; /* pointer to word list */ syminfo *csa; /* pointer to article list */ int cswlen; /* length of csw */ int csalen; /* length of csa */ }; typedef struct cs_elem cs_elem;各クラスタがこれだけの情報を持ちます.
まず,*csa はクラスタに含まれている文書のリストです. 例のごとく syminfo 型リストです.長さ(文書数)は csalen です. 文書リスト *csa での文書の並びは,*q での並びと同じになります. 例えば前節の例で,{ 1, 5 | 2 | 3, 4 } の場合, 一番目のクラスタに相当する *csa の並びは 1, 5 となります.
csw は,このクラスタの代表単語リストで, これも syminfo 型リストです.長さ(単語数)は cswlen です.cswlen は csb で指定した数 NKW, または NKW に達しない場合はそれ以下です. 代表単語リスト *csw での単語の並びは代表度の強い順です. ちなみに代表度の値は, 各単語要素(syminfo 型)のメンバ weight に収められています.
まとめると,例の場合以下のようになります.
cs_elem 0 csa: {1, 4} <- クラスタ0の文書リスト csalen: 2 csw: {202, 10, 152, 829, 1002} <- クラスタ0の代表単語リスト cswlen: 5 1 csa: {2} <- クラスタ1の文書リスト csalen: 1 csw: {112, 136, 9, 2129, 891} <- クラスタ1の代表単語リスト cswlen: 5 2 csa: {3, 5} <- クラスタ2の文書リスト csalen: 2 csw: {734, 1183, 90, 3912} <- クラスタ2の代表単語リスト cswlen: 4
関数 csb は領域 *csa, *csw を自動的に確保しますので, 必要なくなったら各自で解放してください. 関数 free_cs_elems を呼べば,これらの領域をまとめて解放できます.
free_cs_elems(cslst);
あとは cs_elem 型リストを表示するだけです.例を示しておきます.
for (i = 0; i < nc; i++) { for (j = 0; j < cslst[i].csalen; j++) { fprintf(stdout, "%d\t%d\t%s\n", i, cslst[i].csa[j].id, wam_id2name(wam, wam_dir, cslst[i].csa[j].id)); } for (j = 0; j < cslst[i].cswlen; j++) { fprintf(stdout, "%d\t%d\t%s\n", i, cslst[i].csw[j].id, wam_id2name(wam, WAM_REVERT_DIRECTION(wam_dir), cslst[i].csw[j].id)); } }
コンパイルして実行してみましょう. 文書リストに加え,代表単語リストが表示されます.
「4: 検索結果を自動分類する」に進む
「2: クラスタリングプログラムを作る」に戻る
「1: はじめに」に戻る