d3.jsとHyperestraierで新聞のワードクラウド作った

とりあえあえずサンプルです。hyperestraierはこちらで動いています。

何故ワードクラウドを作りたかったかというと自分が欲しかったからなのですが、Webでニュースを見ていると、今現在あちこちで扱われている注目の出来事がなんなのか分かりにくく見逃すことがあると痛感したからです。

で、参考にしたサイトはGUNMA GIS GEEKさん。
【D3.js】「全ツイート履歴」からWord cloudを作ってみた。
です。僕より説明がうまいと思うのでそちらに目を通しておいていただけると先が読みやすいかと。

まずワードとその出現頻度のデータを入手して、出現頻度の大きいワードほど文字も大きくして、目立つようにしたいわけですが、僕はHyperestraierを使っていたので、find -ctime -1で一日分だけのインデックス(casket)をestcmdで作って

estcmd words casket > dfdb1.tsv 

みたいな感じで得たデータを元にしています。まぁこれはMeCabと違って、文字の種類が変わるところで文章をぶった切って、その個数をカウントして、検索の時に使おうというデータなので、上のサンプルで漢字に送り仮名がなかったりするのはそのためです。

今回はまだとりあえずなので、すべてのひらがなと、一文字だけのカタカナ、あと手作業で1000個以上の残ったワードをピックアップして削除するようにしましてその他にもExcelを駆使しcountをMAXが100くらいの控えめな値にしました。文字コードExcelShift_JISですが、最終的にはUTF-8にする必要があるようです。

count,word
100.630,オバマ
57.716,セウォル
55.877,米大統領
55.049,リニア
54.889,自民
・・・

のようなranking.csvファイルを作ります。ちょうどオバマ大統領が来日し、韓国でフェリーが沈没した時だったのでこうなりました。

ちなみにJavascriptもあまりよくわかっていないのですが、index.htmlの部分に書いたのはこんな感じです。

<script>
d3.select('body').append('svg') // 元のサイトから変更
.attr({
        x:50,
        y:50,
        fill:"black",
        "font-size":40
})
.text('Loading...')     
        
d3.csv('ranking.csv', function(data){
        var h = 800;
        var w = 1000;       // 大きくした
data = data.splice(0, 500); // 処理wordを500件に減らした

        var random = d3.random.irwinHall(2)
        
        var countMin = d3.min(data, function(d){ return d.count} ); //こうした方がいい時もある
        var countMax = d3.max(data, function(d){ return d.count} );
        var sizeScale = d3.scale.linear().domain([countMin, countMax]).range([10, 100])
        var colorScale = d3.scale.category20();
 
        var words = data.map(function(d) {
                return {
                        text: d.word,
                        size: sizeScale(d.count) //頻出カウントを文字サイズに反映
                        };
                });
         
        d3.layout.cloud().size([w, h])
                .words(words)
                .rotate(function() { return Math.round(1-random()) *90; }) //ランダムに文字を90度回転
                .font("Impact")
                .fontSize(function(d) { return d.size; })
                .on("end", draw) //描画関数の読み込み
                .start();

        //wordcloud 描画
        function draw(words) {
                d3.selectAll('text').remove();
                d3.select("svg")
                .attr({
                        "width": w,
                        "height": h
                })
                .append("g")
                .attr("transform", "translate(150,150)")
                .selectAll("text")
                .data(words)
                .enter().append("text")
                .style({
                        "font-family": "Impact",
                        "font-size":function(d) { return d.size + "px"; },
                        "fill": function(d, i) { return colorScale(i); }
                })
                .attr({
                        "text-anchor":"middle",
                        "transform": function(d) {
                                return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
                        }
                })
                .text(function(d) { return d.text; })
                //ワードクラウドがクリックできるリンクになるように追加。
                .on("click", function (d, i){
                    window.open("../../cgi-bin/estseek.cgi?phrase="+d.text+"&perpage=100&attr=&order=&clip=9&navi=0", "_blank");});
        }
        
});
</script>

見よう見まねで何とか出来ました。でもまぁ見落すことがあるのを避けるのは難しいかな^^;