かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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>

便利っぽい。