Dynamic Hexbin (動的六角分配)

HexBin とは

D3 の作者マイク・ボストック氏が一昨日(12/06)、bl.okcs.orgDynamic Hexbin というデモを投稿されました。

同じデモを、少しサイズと色をアレンジしたものが次のデモです。じっくりご覧ください。さて何に見えるでしょうか?

デモI

お風呂をスリガラスごしに覗いてるように見えた人は、わりと正常ですが間違っています。実はこれは、動的( Dynamic )な六角分配( Hexbin )のデモです。と、書いただけでは何のことやらわかりませんね。

Hexbin は、マイク氏自身による D3 のプラグインで、名称は Hexagonal Binning(六角形の分配)の略です。

上記 Github の README の言葉をそのまま借りると「 d3 の hexbinは、六角分配を実装するためのプラグインであり、データをひとまとめにして分かりやすく表示したい場合に役立つ。何万もの点を散布図にするかわりに、それを六角形の各格子に分配( bin )し、その分布を六角形の色や大きさで表現する。」ためのものです。

マイク氏自身によるサンプルが下記2例で、上が六角形の「色」で表現した例、下が「大きさ」で表現した例です。

また、次のページは群馬の清水さんがわかりやすく解説したHexBin の例です。

以上の三例はいずれも静的な六角分配図ですが、今回のマイク氏のデモは、これを動的なデータに対応させたものです。

具体的には、まず常に変動し続ける( constantly-fluctuating )2000のランダムな点を生成し(ただしこの点は実際には画面に表示していません)、それを動的に各六角格子に分配しています。

ただしランダムといっても、2000個の点を完全にランダムな場所に生成させているわけではなく、一定の角速度δθ(デルタシータ)で時計回りに回転する、画面の中央から一定の間隔(ここでは 80ピクセル )だけ離れた点を中心に生成させています。そのため、六角形の濃度の高い部分も、ゆっくりと画面を右回りに回転しているわけです。

上のデモⅠも、よく見れば画面中央を軸に、分布の中心が時計回りに回転しているのがわかると思います(決してスリガラスの向こうで裸の女性が湯あみをしているのではありません)。

今回はデモですのでデータをランダムに生成していますが、マイク氏の語る通り、「ストリーミングデータに接続してリアルタイムに更新する」ことも可能でしょう。

コード解説

今回のサンプルのソースコードは、分量もそれほど多くなく、動作もストレートでわかりやすいため、D3 初心者が勉強するのにもちょうど良いサンプルになっていると思います。

といっても D3 のスタイルに慣れていないと面食らうところもあると思いますので、以下、少し詳しく解説します。

読者として、D3 の基本は頭では一通り身につけたものの、自分で D3 のコードを書くのは少し心もとない、というレベルの人を想定しています。もう少し具体的には、スコット・マレイ氏によるチュートリアルを読み終え、一通り理解したレベルの人です。未読の方はぜひ一度目を通してから先にお進みください。

・スコット・マレイ D3 日本語チュートリアル(翻訳)

なお、コードを少しずつ引用しながら解説して行きますが、マイク氏のデモページで一覧することができますので、できれば横にデモ画面を開いておくと分かりやすいでしょう。

Dynamic Hexbin(別画面で開く)

(続きは 12月31日公開!)

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.hexagon {
  fill: none;
  stroke: #000;
  stroke-width: .5px;
}

</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.hexbin.v0.min.js"></script>
<script>
var width = 960,
    height = 500,
    i = -1,
    θ = 0,
    δθ = .03,
    n = 2000,
    k = 20; // samples to replace per frame
var randomX = d3.random.normal(width / 2, 80),
    randomY = d3.random.normal(height / 2, 80),
    points = d3.range(n).map(function() { return [randomX(), randomY()]; });
var color = d3.scale.linear()
    .domain([0, 20])
    .range(["white", "steelblue"])
    .interpolate(d3.interpolateLab);
var hexbin = d3.hexbin()
    .size([width, height])
    .radius(20);
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);
var hexagon = svg.append("g")
    .attr("class", "hexagons")
  .selectAll("path")
    .data(hexbin(points))
  .enter().append("path")
    .attr("d", hexbin.hexagon(19.5))
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    .style("fill", function(d) { return color(d.length); });
d3.timer(function() {
  θ += δθ;
  randomX = d3.random.normal(width / 2 + 80 * Math.cos(θ), 80),
  randomY = d3.random.normal(height / 2 + 80 * Math.sin(θ), 80);

  for (var j = 0; j < k; ++j) {
    i = (i + 1) % n;
    points[i][0] = randomX();
    points[i][1] = randomY();
  }
  hexagon = hexagon
      .data(hexbin(points), function(d) { return d.i + "," + d.j; }); 
  hexagon.exit().remove();

  hexagon.enter().append("path")
      .attr("d", hexbin.hexagon(19.5))
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  hexagon
      .style("fill", function(d) { return color(d.length); });
});






コメントを残す


3 − = 二