かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

d3.jsのコレクション操作系関数

いろいろあるんですね…。知らないと損しちゃう。

個人的に特に便利と感じたのはnest関数からのkeyやrollupです。keyがgroup byみたいなものでrollupがグルーピングしたものを集計する処理です。

画面用にこんなHTMLがあるとして

<!DOCTYPE html>

<html lang="ja">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css" />
    <script src="Scripts/d3.v3.min.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <div id="content"></div>
</body>
</html>

サンプルデータでidとageを持ったオブジェクトを適当に20個作って10代, 20代ごとのグループにしてます。

window.onload = () => {
    var data = d3.range(0, 20).map(i => { return { id: i, age: 0 | Math.random() * 40 + 20 }; });
    d3.select("#content").append("div").text(JSON.stringify(data));
    d3.select("#content").append("hr");

    var nestedData = d3.nest()
        .key(d => (0 | d.age / 10).toString())
        .entries(data);
    d3.select("#content").append("div").text(JSON.stringify(nestedData));
};

実行すると以下のようなHTMLが得られます。

<html lang="ja"><head>
    <meta charset="utf-8">
    <title>TypeScript HTML App</title>
    <link href="app.css" rel="stylesheet" type="text/css">
    <script src="Scripts/d3.v3.min.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <div id="content"><div>[{"id":0,"age":41},{"id":1,"age":59},{"id":2,"age":41},{"id":3,"age":29},{"id":4,"age":59},{"id":5,"age":34},{"id":6,"age":56},{"id":7,"age":41},{"id":8,"age":20},{"id":9,"age":23},{"id":10,"age":43},{"id":11,"age":57},{"id":12,"age":21},{"id":13,"age":46},{"id":14,"age":50},{"id":15,"age":31},{"id":16,"age":22},{"id":17,"age":21},{"id":18,"age":59},{"id":19,"age":45}]</div>
<hr>
<div>[{"key":"4","values":[{"id":0,"age":41},{"id":2,"age":41},{"id":7,"age":41},{"id":10,"age":43},{"id":13,"age":46},{"id":19,"age":45}]},
{"key":"5","values":[{"id":1,"age":59},{"id":4,"age":59},{"id":6,"age":56},{"id":11,"age":57},{"id":14,"age":50},{"id":18,"age":59}]},
{"key":"2","values":[{"id":3,"age":29},{"id":8,"age":20},{"id":9,"age":23},{"id":12,"age":21},{"id":16,"age":22},{"id":17,"age":21}]},{"key":"3","values":[{"id":5,"age":34},{"id":15,"age":31}]}]</div>
</div>

</body></html>

keyにグループピングのキーにつかった値が、valuesに値の配列が入ってます。rollupを使うと年齢の平均とかを出すこともできます。(あまり意味ない例ですが…)

/// <reference path="scripts/typings/d3/d3.d.ts" />
window.onload = () => {
    var data = d3.range(0, 20).map(i => { return { id: i, age: 0 | Math.random() * 40 + 20 }; });
    d3.select("#content").append("div").text(JSON.stringify(data));
    d3.select("#content").append("hr");

    var nestedData = d3.nest()
        .key(d => (0 | d.age / 10).toString())
        .rollup((d: { id: number; age: number }[]) => d3.mean(d, d => d.age))
        .entries(data);
    d3.select("#content").append("div").text(JSON.stringify(nestedData));
};

実行するとこんな感じ

<html lang="ja"><head>
    <meta charset="utf-8">
    <title>TypeScript HTML App</title>
    <link href="app.css" rel="stylesheet" type="text/css">
    <script src="Scripts/d3.v3.min.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <div id="content"><div>
[{"id":0,"age":53},{"id":1,"age":21},{"id":2,"age":47},{"id":3,"age":59},{"id":4,"age":37},{"id":5,"age":51},{"id":6,"age":30},{"id":7,"age":29},{"id":8,"age":40},{"id":9,"age":29},{"id":10,"age":43},{"id":11,"age":43},{"id":12,"age":40},{"id":13,"age":49},{"id":14,"age":55},{"id":15,"age":42},{"id":16,"age":46},{"id":17,"age":25},{"id":18,"age":31},{"id":19,"age":24}]
</div>
<hr>
<div>
[{"key":"5","values":54.5},
{"key":"2","values":25.6},
{"key":"4","values":43.75},
{"key":"3","values":32.666666666666664}]
</div>
</div>

</body></html>

便利っぽい。