こんにちは.北山です.
今日はなんの脈絡もなく,データベースでTF-IDF法をやってみよう!! という話をしましょう.データベースは以下のように設計されているとします.

このテーブルにはWebページを形態素解析して得られた単語が登録されています.urlには解析元のurl,termには形態素解析した単語が格納されているものとします.
さて,これで下準備は完了です.まずはTF値(単語頻度)を出しましょう.
select url, term, count(*) from terms group by url, term;
たったこれだけです.urlとtermに同じ値が入っている行の数を数えると… ある文書に出てくるある単語の数,すなわち単語頻度になりますね.
次は,DF値(文書頻度)です.
select term,count(*) from (select distinct url, term from terms) as D group by term;
こちらもこれだけです.副問い合わせを使っていますが,(select distinct url, term from terms)の部分で,1つの文書に重複して出てくる単語を集約して1つにしています.そのあとは,ただただ同じtermの数を数えるだけで文書頻度になりますね.
ここで,TF-IDFの式を tf * log(N / df) としましょう.Nは全文書数で,以下で求まります.
select count(distinct url) from terms;
さて,あとはこれらを使って計算するだけです.
select TFT.url, TFT.term, tf, N, df, tf * log(N/df) as tfidf
from (select url, term, count(*) as tf from terms group by url, term) as TFT,
(select term,count(*) as df from (select distinct url, term from terms) as D group by term) as DFT,
(select count(distinct url) as N from terms) as NT
where TFT.term = DFT.term;
とまあ,from句の中がややこしいことになってますが,これまでやったことを書いているだけです.where句で単語同士で繋げています.
何かの言語で書こうとすると,少々面倒くさいコードになりがちですが,SQLだとほぼほぼ集約関数だけでできてしまうという例でした.まあ,これこのまま実行すると,where句での結合がネックになって非常に時間がかかるのですが…インデクスをつけるとか,tfを計算したテーブルを流用してdfを計算するとか色々高速化する余地はありますし,tfとかdfとかの集計だけデータベースに任せて,残りの計算はプログラム側で行うなどのことも考えられます.まあ,ようは使い方ということで.
それでは今日はこの辺りで.ではでは.