目的
前回、Twitter実況の可視化を行いたいと展望で述べました。
実況内容を集計するところまではできましたが、集計結果の内容は膨大であり、内容も変動するため、実況から「今何がホットなのか」を提示するという目的は未達だったというのが経緯です。
これについて進展がありましたので報告します。
結論
**Twitter野球実況の形態素解析結果を可視化(ワードクラウド化)**しました。
これにより以下の恩恵が得られました。
- 人間が一目で「今何がホットなのか」を判別できる
- 各球団の色でワードクラウドを着色していることにより、「どの球団で何がホットなのか」も知ることができる
「D3.js + d3-cloud.js + 形態素解析」による、野球HOTワード分析。 pic.twitter.com/JNZwXY5qeh
— すいばり@'16年度9試合2勝2敗 (@Suibari_cha) 2017年2月23日
内容
コード
d3-cloudによる描画処理部(フロントエンド)のみ記載します。
function wordCloud(selector) {
const W = 800; // 描画範囲サイズwidth
const H = 500; // 描画範囲サイズheight
const P = 4; // ワードクラウド文字間のpadding
// wordCloud関数のコンストラクタ(描画初期設定)
var svg = d3.select(selector).append("svg")
.attr("width", W)
.attr("height", H)
.append("g")
.attr("transform", "translate("+W/2+","+H/2+")");
// 描画(draw)関数
function draw(words) {
var cloud = svg.selectAll("g text")
.data(words, function(d) { return d.word; })
// ワード出現時&ワード存在時の処理
cloud.enter()
.append("text")
.style("fill", function(d) { return d.color; })
.attr("text-anchor", "middle")
.attr('font-size', 1)
.merge(cloud)
.transition()
.duration(600)
.style("font-size", function(d) { return d.size + "px"; })
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.style("fill-opacity", 1)
.text(function(d) { return d.word; });
// ワード消去時処理
cloud.exit()
.transition()
.duration(200)
.style('fill-opacity', 1e-6)
.attr('font-size', 1)
.remove();
}
// 再描画時に呼び出す関数
return {
update: function(words) {
var random = d3.randomIrwinHall(2);
d3.layout.cloud().size([W, H])
.words(words)
.padding(P)
.rotate(function() {
//return Math.round(1-random())*90; //文字をランダム回転させたい場合
return 0; //回転させない場合
})
.text(function(d) { return d.word; })
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();
}
}
}
// 与えられたデータ([{},{},{},...])を描画可能なオブジェクトに成形する
function getWords(data) {
var countMax = d3.max(data, function(d){return d.count});
var sizeScale = d3.scaleLog().domain([1, countMax]).range([10, 50]); //ログスケール
//var sizeScale = d3.scaleLinear().domain([0, countMax]).range([10, 100]); //リニアスケール
// 各チームの色を指定する関数
var colorScale = function(t){
switch(t) {
case "baystars": return d3.color("dodgerblue");
case "giants": return d3.color("orange");
case "tigers": return d3.color("yellow");
case "swallows": return d3.color("limegreen");
case "dragons": return d3.color("darkblue");
case "carp": return d3.color("red");
case "hawks": return d3.color("gold");
case "lions": return d3.color("aqua");
case "buffaloes": return d3.color("darkgoldenrod");
case "eagles": return d3.color("firebrick");
case "fighters": return d3.color("steelblue");
case "marines": return d3.color("black");
default: return d3.color("gray");
}
};
return data.map( function(d) {
return {
word: d.word,
size: sizeScale(d.count),
color: colorScale(d.team)
}
})
}
function showNewWords(vis, data) {
vis.update(getWords(data))
}
// -------------------------
// ワードクラウドのインスタンス生成
var myWordCloud = wordCloud('#main');
// Socket.io経由でサーバのレスポンスの度に以下実行
var ioSocket = io.connect("http://192.168.33.10:8080");
ioSocket.on('result_analyze', function(data) {
showNewWords(myWordCloud, data);
});
MeCabの新語辞書導入
前回、野球選手名が適切に分解されない問題があり、今回はMeCabの新語辞書"mecab-ipadic-NEologd"を導入しています。
著名な選手ですと、明らかに適切に分解されるようになりましたが、新戦力(ルーキーや新外国人など)ですと流石に分解されません。
メモリ不足によるインストールエラー
ちなみに、辞書インストール時に以下ログが出てメモリ不足となり、インストールが中止される不具合がありました。
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted (core dumped)
調べると全く同じ現象について書かれた以下の記事があり、一発でした。
以下のようにvb.customizeを追記した(config.vm.providerも)。
Vagrant.configure(2) do |config| config.vm.box = "ubuntu1404" config.vm.provider :virtualbox do |vb| vb.customize ["modifyvm", :id, "--memory", 1024] end end
展望
- D3.js周りの設計はこの記事までとします。今はD3.jsの利用方法の習得よりも、JavaScriptの基礎知識の習得を優先したいためです。「ドキュメントやFAQがほぼ英語(Qiita記事は230件しかない)」、「多機能すぎてネイティブJavaScriptとは別物」である点が習得を難しくさせていると感じます。
- 「野球選手用名解析用ユーザ辞書」の生成と導入を検討。→導入しました
- 「12球団ごとの毎秒ツイート数」をサーバサイドで計算させているので、それをDOMに反映する。
- 阪神タイガースの文字色(yellow)が背景と同化してしまっている。。
- 球団名などはワードクラウド表示させない。例えば、ワードクラウド上の赤は広島カープのHOTワードを表しているが、赤で「広島」や「カープ」が大きく表示されるのは当たり前。既知の情報は除外する方が、情報として有用。
所見
- D3.jsが異常に難しい。コピーしたコードを実行するだけでかなりの時間を浪費してしまいました(主にD3のバージョンがv3→v4間で大幅にAPIが変わった点に苦しめられました。既存のドキュメントで対応できない)。
- 今回の目的は「情報を分かりやすく伝える」ことであり、「そのためのツールを使う」ことに時間がかかりすぎる(手段が目的と化す)のは避けたい。
参考
1. GitHub: "D3 API Reference"
2. GitHub: "d3-cloud API Reference"
3. GUNMA GIS GEEK: "【D3.js】「全ツイート履歴」からWord cloudを作ってみた。"
4. d3.jsな日々: "スケーリング:domainとrange"
5. About Blocks: "Animated d3 word cloud"