ScalaとMecabで形態素解析を行いコンソールに自動Tagリストを作る
MecabをScalaから呼び出し、あるデーターを形態素解析し、コンソール上にTag Cloudっぽい表示をしたいと思います。自動的に抽出した単語をTagっぽくみせますので、形態素解析した単語の中から「名詞」にターゲットを絞りこんでScalaで抽出します。コンソールへの出力は以前blogで書いた、「ScalaからCライブラリを読み込んで実行する」で利用した、libfont_style.soを使用し、ScalaからMecab APIを呼び出すのにJavaのAPIが提供されている、cmecab-javaを使用したいと思います。
実装環境
OS :Ubuntu(kernel:2.6.32-24-generic) JVM :OpenJDK 1.6.0_20 Scala :Scala 2.8.1.final charset:UTF-8
解析対象データ
2011/02/07の共同通信社の報道ニュースを使用
米ナスダックのシステムにハッカー侵入―取引システムには被害なし
米株式市場ナスダックを運営するナスダックOMXは5日、 同社のコンピューターシステムにハッカーが侵入していたことを認めた。 ただ、取引システムには被害は生じていないとしている。 ハッカー攻撃を受けたシステムの顧客には通知済みだという。 ハッカー攻撃にあったのは、企業役員向け情報サービス 「ディレクターズ・デスク(Directors Desk)」の システム内で、不正ファイルが見つかったという。
ナスダックへのハッカー攻撃については、 5日付のウォール・ストリート・ジャーナルが報じており、 過去1年間に複数回、侵入されていたことを明らかにしている。 ナスダックOMXは本紙報道の後、リリースを発表し、 「当該ファイルはすぐに除去されており、現時点では、 ハッカーがディレクターズ・デスクの顧客の情報にアクセスしたり、 それらを取り出したりした形跡はない」と述べた。
取引システムについては、「ディレクターズ・デスクなど ウェブにつながっているサービスとは独立して運営されており、 ナスダックOMXが運営また提供する取引プラットフォームが 影響を受けたことはまったくない」としている。 取引所運営大手のNYSEユーロネクストとダイレクト・エッジは、 7日もナスダックとの接続を続けるとしている。 一方、一部トレーディング会社も、ナスダックへの発注を続ける方針を示した。
動作環境構築
sconsのインストール(cmecab-javaのインストールで必要)
antのインストール(cmecab-javaのインストールで必要)
protocol bufferのインストール(cmecab-javaのインストールで必要)
mecabのインストール
cmecab-javaのインストール
ipa辞書のインストール
cmecab-javaではmake形式ではなく、sconsをつかってC/C++プログラムをコンパイルします。aptパッケージにsconsがあるのでaptからインストールします
apt-get install scons
日々Javaで開発を行っている人であれば、antは既にインストールされていると思いますが、一応書いておきます。aptパッケージにantがあるのでaptからインストールします
apt-get install ant
wget http://protobuf.googlecode.com/files/protobuf-2.3.0.tar.bz2 tar -xjf protobuf-2.3.0.tar.bz2 cd protobuf-2.3.0 ./configure make make install
usr/local/lib に作られるので、ldconfigを実行しておきます (もしかすると)/etc/ld.so.confに"/usr/local/lib"というパスの追加が必要かもしれないです
wget http://sourceforge.net/projects/mecab/files/mecab/0.98/mecab-0.98.tar.gz/download -O mecab.tar.gz tar -xvf mecab.tar.gz cd mecab-0.98 ./configure --with-charset=utf8 make make install
デフォルトだと/usr/local/lib配下にライブラリはインストールされます
wget http://cmecab-java.googlecode.com/files/cmecab-1.7.tar.gz tar -xzf cmecab-1.7.tar.gz cd cmecab-1.7/ ant
cmecab-1.7/bin/cmecab-1.7.jarが生成されます
cd cmecab-1.7/jni/ vi SConstruct #環境に応じてjavaの動作環境パスに適時変更します Line:37 javahome = '/usr/lib/jvm/java-6-openjdk' #環境に応じてjni.hファイルなどJavaヘッダーファイルのパスを適時追加します Line:39 join(javahome, 'include', 'linux', '/usr/lib/jvm/java-6-openjdk/include')]
scons libCMeCab.so
cmecab-1.7/jni/libCMeCab.soが生成されます
このままですと、単語の認識ができないお馬鹿な解析になってしまいますので、標準的な「ipa辞書」をインストールします
wget http://sourceforge.net/projects/mecab/files/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz/download -O mecab-ipadic-2.7.0-20070801.tar.gz tar -xzf mecab-ipadic-2.7.0-20070801.tar.gz cd mecab-ipadic-2.7.0-20070801 ./configure --with-charset=utf8 make make install
"--with-charset=utf8"をつけないと、デフォルトcharsetで"EUC-JP"になってしまいます 「ipa辞書」意外にも「wikipedia」、「はてな」から辞書を追加する方法を明記されている方がいらっしゃるので、そちらを参考にしてより語彙を高めるとよいかと思います。
実行プログラム
Scalaのソース内容(Analyzer.scala)
Scalaプログラムのコンパイル
コマンド実行
実行結果
ソースの可読性を優先するためエラー処理などは省いています。
(def main()のwhile (node.hasNext) {xxxxx}の中の実装がいけてない感じがします・・・)
/**
* Analyzer.scala
* This class Morphological analysis, occurrence frequency
* of words does display
* 形態素解析、単語出現リスト表示クラス
*
* @author fukaoi
* @version 0.1
*/
package blojsomscala
import net.moraleboost.mecab.{ Node }
import net.moraleboost.mecab.impl.StandardTagger
import scala.util.matching.Regex
import scala.io.Source
import com.sun.jna._
object Analyzer {
/**
* Main method
* メインメソッド
*/
def main(args: Array[String]) {
val words = Source.fromFile(args(0)).getLines.mkString
val tagger = new StandardTagger("UTF-8", "")
val node = tagger.parse(words)
val regex = """名詞""".r
var mapWordLists = Map[Int, String]()
var i = 0
while (node.hasNext) {
val feature = node.next
regex findPrefixOf node.feature match {
case Some(v) = mapWordLists += (i - feature)
case None =
}
i = i + 1
}
val sortWordLists = getMostShowWord(mapWordLists)
display(sortWordLists)
}
/**
* Get Occurrence frequency of word in Scala List
* 単語の出現頻度をリストにして返す
*/
def getMostShowWord(wordList: Map[Int, String]): Seq[(String, Int)] = {
wordList.groupBy(x = x._2).
map(y = (y._1, y._2.size)).
toSeq.sortWith(_._2 _._2)
}
/**
* Setting in JNA for C Library
* CライブラリをJava JNAにセットする
*/
def setNativeLibrary(colorName: String) {
val lib = NativeLibrary.getInstance("libfont_style.so")
val func = lib.getFunction("font_color")
func.invokeVoid(Array(colorName))
}
/**
* Analytics data to display on console
* コンソール結果に集計した結果を出力する
*/
def display(list: Seq[(String, Int)]) {
val charCode = "Shift-JIS"
var maxLength = 0
list.map(x = if (x._1.getBytes(charCode).length maxLength) maxLength = x._1.getBytes(charCode).length)
list.map(x = (setNativeLibrary(getColorNumber(x._2)),
print(x._1),
print(" " * (maxLength - x._1.getBytes(charCode).length)),
print("+" * x._2),
print("\n")))
}
/**
* Get color code for C Library(setNativeLibrary)
* Cライブラリ(setNativeLibrary)に渡す
* カラーコードを取得する
*/
def getColorNumber(count: Int): String = {
val colors = Map(
(1 - "blue"),
(2 - "cyan"),
(3 - "green"),
(4 - "magenta"),
(5 - "red"))
if (count = 6)
"yellow"
else
colors(count)
}
}
自動Tagを作成する上で対象となる名詞だけを抽出するので、mecabの解析データから正規表現でマッチさせて抜き出します
val regex = """名詞""".r
Tagの文字長がばらばらなため、コンソールに出力する際の見た目の体裁を整えるために、一番長いTagに合わせて出力位置を決定します。そのために冗長ながら、Mapの中から最大文字長を調べています。charCode"Shift-JIS"と定義したのは、UTF-8ですと1文字が1~6バイトの可変長になり文字長を求めるのが難解だったため、固定長のChar Codeにしたかったからです
val charCode = "Shift-JIS" if (x._1.getBytes(charCode).length maxLength) maxLength = x._1.getBytes(charCode).length
Analyzer.scalaのソースコードをダウンロードする(GitHub)
cd blog/2011/ scalac -classpath .:jna.jar:cmecab-1.7.jar blojsomscala/Analyzer.scala
scala -Djava.library.path=
"+"の数が解析対象データの中でのTag(名詞単語)の出現回数になり、回数が多いTagから上位に並んでいます

結果考察
抽出したTagを見てみると、上位3位の「ナスダック」、「システム」、「ハッカー」はTagとしては十分意味が通じる単語 ですが、それより下位の「日」、「こと」、「5」、「の」はTagとしては不十分な結果です。やはり辞書の追加であったり、N-gram による単語の解析が必要だと思います。今後の課題結果となりました。




