Word Cloud + kuromoji + Processing で日本語文章をビジュアライズ

完成品:

Word Cloud

文章中で出現頻度に重みを付け、その重みに応じた大きさで単語群を図示する手法。

Processing で行うには WordCram が手っ取り早く、デザインも良い。

いかにして日本語文章中の単語を抜き出すか

WordCram に何の手も加えていない日本語文章を読み込ませたところで、日本語単語への分かち書き方法を知らないため、不自然な Word Cloud が出来上がる。

そこで、日本語解析器である kuromoji を使い、対象文章を解析し、名詞を読みこませることにした。

下準備

WordCram

さて、まずは外部ライブラリである WordCram を 手元の Processing にインストールする。

  • こちらから Processing 3.0 に対応した WordCram 1.0.0 の zip ファイルをダウンロードし、解凍する。
  • Processing の sketchbook フォルダを開く。(Processing > Preferences > Sketchbook location)

  • そのフォルダ内の libraries に、解凍した WordCram フォルダをコピーする。

基本的な使い方は次の通り:

import wordcram.*;

// Set up the Processing sketch
size(1000, 600);
colorMode(HSB);
background(230);

// Make a wordcram from a random wikipedia page.
new WordCram(this)
  .fromWebPage("https://en.wikipedia.org/wiki/Special:Random")
  .withColors(color(30), color(110),
              color(random(255), 240, 200))
  .sizedByWeight(5, 120)
  .withFont("Copse")
  .drawAll();

kuromoji

kuromoji は Java 用のライブラリなので少し手を加える必要がある。

  • こちらから zip ファイルをダウンロードし解凍する。
  • lib フォルダ内に kuromoji-0.7.7.jar ファイルがあるので、これを kuromoji.jar にリネーム。
  • sketchbook フォルダの librarieskuromoji/librariesを作成し、先ほどの kuromoji.jar ファイルを配置する。

基本的な使い方は次の通り:

import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;
import java.util.*;

Tokenizer tokenizer = Tokenizer.builder().build();
List<Token> tokens = tokenizer.tokenize("吾輩は猫である。");

for (Token token : tokens) {
    println("PartOfSpeech:" + token.getPartOfSpeech());
    println("surfaceForm:" + token.getSurfaceForm());
    println("baseForm:" + token.getBaseForm());
}

出力:

PartOfSpeech:名詞,代名詞,一般,*
surfaceForm:吾輩
baseForm:吾輩
PartOfSpeech:助詞,係助詞,*,*
surfaceForm:は
baseForm:は
PartOfSpeech:名詞,一般,*,*
surfaceForm:猫
baseForm:猫
PartOfSpeech:助動詞,*,*,*
surfaceForm:で
baseForm:だ
PartOfSpeech:助動詞,*,*,*
surfaceForm:ある
baseForm:ある
PartOfSpeech:記号,句点,*,*
surfaceForm:。
baseForm:。

日本語文章を Word Cloud でビジュアライズ

これら2つのライブラリを活用して、ホッテントリのフィードを解析、Word Cloud を作成する。

String URL = "http://feeds.feedburner.com/hatena/b/hotentry";
XML[] items;
String contents;
PrintWriter output;
String words;

import wordcram.*;
import org.atilika.kuromoji.Token;
import org.atilika.kuromoji.Tokenizer;
import java.util.*;

void setup() {
  size(1000, 600);
  colorMode(HSB);
  background(230);

  XML xml = loadXML(URL);
  items = xml.getChildren("item");
  for(int i = 0; i < items.length; i++){
    contents += items[i].getChild("title").getContent();
    contents += items[i].getChild("description").getContent();
  }

  Tokenizer tokenizer = Tokenizer.builder().build();
  List<Token> tokens = tokenizer.tokenize(contents);
  for (Token token : tokens) {
    String[] partOfSpeech = token.getPartOfSpeech().split(",");
    if (partOfSpeech[0].equals("名詞") && !token.getSurfaceForm().equals("こと")) {
      words += token.getSurfaceForm() + "\n";
    }
  }

  output = createWriter("words.txt");
  output.println(words);
  output.close();

  drawWordCloud();
}

void draw() {

}

void mousePressed() {
  drawWordCloud();
}

void drawWordCloud() {
  println("creating a new word clound");
  background(230);
  new WordCram(this)
    .fromTextFile("./words.txt")
    .withColors(color(30), color(110),
                color(random(255), 240, 200))
    .sizedByWeight(5, 120)
    .withFont("Copse")
    .drawAll();
}

結果はこんな感じ:

WordCram にかける前にある程度意味のない頻出単語をフィルタしたほうが良い。今回は Processing での正規表現がわからなかったので、!token.getSurfaceForm().equals("こと") のように kuromoji で弾くようにしている。