テキストデータを使ってお手軽にNgram統計を取る方法

テキストデータの言語的な特徴を知りたい場合、そのデータを使ってNgram統計を取ることがよくある。Ngram統計というのはテキスト中の連続したN文字それぞれが何回出現したかの統計をとること。
といわれてもピンとこない人もいるかも知れない。実例を見るのが早いので当ブログの昨年12月の記事タイトルを使ってNgram統計を取ってみる。


まず記事タイトルを一行一列でテキストファイルに書き出す。

$$ cat blog-title.txt
「PIANO OPERA FINAL FANTASY I/II/III」がとても気になる
そっくりヒロインなラノベ「おおコウスケよ、えらべないとはなさけない!」を読みました
PSP「探偵オペラ ミルキィホームズ1.5」第5話(最終話)だよ?
簡潔ビットベクトル性能評価実験のソースコード(rx-trie編)
簡潔ビットベクトル性能評価実験のソースコード(ux-trie編)
簡潔ビットベクトル性能評価実験のソースコード(marisa-trie編)
30分でわかるレコメンデーションエンジンの作り方
ねんがんの TAoMP本を てにいれたぞ! 
魔装機神IIのPV第二弾が公開されたので興奮して眠れません!
DSIRNLP#2で発表しました「作ろう!簡潔ビットベクトル」
話題の新技術、簡潔データ構造の入門用資料をまとめてみた
「ミルキィホームズSS」のBDをげっとした
CRFがよくわからなくてお腹が痛くなってしまう人のための30分でわかるCRFのはなし
自然言語処理なサービスをつくりたい人は「入門ソーシャルデータ」も読むといいのでは
ALSIP2011に参加して簡潔データ構造の話を聴いて来ました

次にテキストデータからNgramを取り出すスクリプトを書く。

$$ cat text2ngram.pl
#!/usr/local/bin/perl
use strict;
use warnings;
binmode STDIN,  ":utf8";
binmode STDOUT, ":utf8";
  
my $n = shift @ARGV;
while (<>) {
  chomp;
  my @a = split(//);
  my $length = @a - $n + 1;
  for (my $i = 0; $i < $length; $i++) {
    for (my $j = 0; $j < $n; $j++) {
      print $a[$i + $j];
    }
    print "\n";
  }
}

これを実行するとテキストデータからすべての連続したN文字が得られる。ここではN=3の場合、つまり3gramを取り出してみる。

$$ ./text2ngram.pl 3 < blog-title.txt > blog-title.3gram
$$ tail blog-title.3gram
構造の
造の話
の話を
話を聴
を聴い
聴いて
いて来
て来ま
来まし
ました

この「構造の」とか「造の話」というのが3gram(=連続した3文字)。この3gramがそれぞれ何回出現したかの統計を取りたい。これは以下のようにsortコマンドとuniqコマンドを組み合わせると簡単に実行できる。

$$ LC_ALL=C sort blog-title.3gram | uniq -c | sort -nr > blog-title.3gram.ct
$$ head blog-title.3gram.ct
      4 簡潔ビ
      4 潔ビッ
      4 ベクト
      4 ビット
      4 トベク
      4 ットベ
      4 クトル
      3 験のソ
      3 評価実
      3 能評価

これが3gramの統計情報。当ブログの12月記事には簡潔ビットベクトルの記事が多かったんだなあ、ということが読み取れる。


おまけ(という名のerika-trieの宣伝)
拙作のerika-trie(http://code.google.com/p/erika-trie/)を使うとNgram統計に対する前方一致検索などができる。
まずはerika-trieをインスト。

$$ svn checkout http://erika-trie.googlecode.com/svn/trunk/ erika-trie
$$ cd erika-trie/src
$$ make
$$ sudo make install

インストしたら先ほどの3gram統計情報を「3gram\t回数」という形式にする。

$$ perl -pe 's/^\s+(\d+)\s(.+)$/$2\t$1/' blog-title.3gram.ct > blog-title.3gram.key

そしてerika-trieに付属のerika_make_vt.shを使ってトライを構築する。

$$ erika_make_vt.sh blog-title.3gram.key blog-title.3gram.trie

あとはerika_predictive_searchを使う。

$$ erika_predictive_search blog-title.3gram.trie
簡潔
# 簡潔
簡潔デ	2
簡潔ビ	4

このように「簡潔」で始まる3gramは「簡潔デ」と「簡潔ビ」の2つだよ、ということがわかる。と同時にこの結果から「簡潔」という2gramの出現回数が(2+4=)6回だということもわかる。