2010/11/21

SQLiteのFTSを応用して長文の要点を抜き出してみる

SQLiteの全文検索機能であるFTS3には、snippet()という関数が用意されており、検索ワードが含まれていた箇所の前後の文章を取得できます。
これを応用すれば、長文から要点っぽい部分を抜き出す機能が実現できるのではないかと思い、Segmenterで分かち書きしたテキストをDBに格納し、出現頻度上位の単語を検索した時のsnippet()を取得するというスクリプトを作成してみました。
use('Clipboard','Segmenter','SQLite');
try{
    var seg=Segmenter.create("ja");
    var db=DB.open();
    db.execute("CREATE VIRTUAL TABLE docs USING fts3(body)");
    var a=seg.segment(Clipboard.text.replace(/[\s\r\n\t]+/g,' '));
    db.table('docs').insert([{body:a.join(' ')}]);
    var re=/[ぁ-ん]{4,}|[々〇\u303B\u3400-\u9FFF\uF900-\uFAFF0-90-9a-za-zァ-ヴーア-ン゙ー]{2,}/i;
    var k=$G(a).groupBy(void(0),function(w,g)({w:w,c:g.count()}))
               .filter(function(o)(re.test(o.w)))
               .orderByDesc('c').map('w').head(3).toArray();
    println(k);
    println(db.select(
        [{d:'snippet(docs,"<<",">>","...",0,64)'},'docs','body MATCH $q'],
        "",-1,0,{q:k.join(' ')}
    ).first().d.replace(/([^\w])\s|\s([^\w])/g,'$1$2'));
}finally{
    free(seg,db);
}
最初にSegmenterオブジェクトとオンメモリDBオブジェクトを生成し、クリップボードのテキストを分かち書きした配列をDBに格納します。
次に、ジェネレータの拡張メソッドで分かち書き配列を加工し、出現頻度の高い上位3件の単語を得ます。
この時、記号や助詞などの不要な単語を除外するため、正規表現で大雑把にフィルタリングしています。
そして、その頻出単語をキーワードとしてDBから検索を行い、sinppet()を取得し、不要な空白を除去して出力します。
なお、snippet()の引数ではテーブル名、マッチ箇所を囲む開始・終了文字列、列番号(0~)、抜き出したい単語数を指定できます。単語数を負の数にすると、抜き出す箇所が複数に分かれる場合に、合計ではなくそれぞれの部分の単語数が指定した数になるように抜き出されるようです。

適当にいくつかのニュースやブログ記事で試してみたところ、期待通りに頻出単語とそれを含む部分を抜き出せました。
文章によっては本題ではない部分が抜き出されてしまうこともありましたが、文の書き出しなどを決め撃ちで抜き出す単純な方法よりは、有用な部分を抜き出せる可能性が高いような気がします。

0 件のコメント:

コメントを投稿