TypeScript Sampleをやってみるその３付録


## はじめに
[前回](https://qiita.com/pooshikin/items/68a5a3c7a3c98768a52a)の続きの付録。[d3のサイトにあるサンプル](https://github.com/d3/d3/wiki/Gallery)をローカル環境で試す時にたぶんみんなはまるので、備忘録を投稿します。
JavaScriptではブラウザーの制約でローカルのcsvを読込ができません。解決方法は簡易サーバを起動させて、そこでJavaScriptを実行させることです。TypeScriptは今回やりません。

## 前提
* [pythonがインストール](https://qiita.com/taiponrock/items/f574dd2cddf8851fb02c)済み
* [Bubble Chartのサンプル](https://bl.ocks.org/mbostock/4063269)のIndex.htmlとflare.csvをローカルに作成済み
* [Split bar chartのサンプル](http://bl.ocks.org/armollica/2330ff9849436e6f129be633fe911017)のIndex.htmlとsocial-network-usage.csvをローカルに作成済み

## サンプル Bubble Chart/Split bar chart
* Bubble Chartは入力したcsvデータをもとに美しいバブルチャートを出力します。
* Split bar chartは入力したデータをもとにバーチャートを出力します。

## ディレクトリ構成
```
root/
├───Bubble Chart/
└───Split bar chart/
```

## ファイル構成
|パス|ファイル名|説明|
|:--|:--|:--| 
|.|index.html|pythonサーバのデフォルトhtml、d3サンプルのリンク|
|.|server.py|pythonサーバの起動スクリプト|
|Bubble Chart|index.html|Bouble Chart出力html|
|Bubble Chart|flare.csv|Bouble Chart入力csv|
|Split bar chart|index.html|Split bar chart出力html|
|Split bar chart|social-network-usage.csv|Split bar chart入力csv|

### root/index.html
``` html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<title>D3 Sample Demo</title>
<style type="text/css">
<!--
body,td { font-size: 12px; line-height: 150% ; color: #999999;}
a:link{
  color: #1B0BB2;
  text-decoration: none;
  }
a:visited{
  color: #555555;
  text-decoration: none;
  }
a:hover,a:active{
  color: #0000FF;
  text-decoration: underline;
  }

.ss {
	font-size: 10px;}
.circle
{
    list-style-type: circle;
}

-->
</style>
</head>

<body>

<br>
<table width="550" align="center" cellpadding="1" cellspacing="0" style="BORDER-BOTTOM: #cccccc 1px solid">
<tr>
<td><strong>D3 Chart LINK</strong></td>
</tr></table>

<br>
<table width="500" border="0" align="center" cellpadding="3" cellspacing="0" style="BORDER-LEFT: #EFEFEF 10px solid">
<tr><td>

<UL class=circle>

<li><a href="..\Bubble Chart\index.html">Bubble Chart</a></li>
<li><a href="..\Split bar chart\index.html">Split bar chart</a></li>

</ul>

</td>
</tr>
</body>
</html>
```
 
### [root/server.py](https://qiita.com/bgg0u/items/4d3c74563154f191ba16)
``` python
import http.server
import socketserver

PORT = 8000
Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()
```

### Bubble Chart/index.html
``` html
<!DOCTYPE html>
<svg width="960" height="960" font-family="sans-serif" font-size="10" text-anchor="middle"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var format = d3.format(",d");

var color = d3.scaleOrdinal(d3.schemeCategory20c);

var pack = d3.pack()
    .size([width, height])
    .padding(1.5);

d3.csv("flare.csv", function(d) {
  d.value = +d.value;
  if (d.value) return d;
}, function(error, classes) {
  if (error) throw error;

  var root = d3.hierarchy({children: classes})
      .sum(function(d) { return d.value; })
      .each(function(d) {
        if (id = d.data.id) {
          var id, i = id.lastIndexOf(".");
          d.id = id;
          d.package = id.slice(0, i);
          d.class = id.slice(i + 1);
        }
      });

  var node = svg.selectAll(".node")
    .data(pack(root).leaves())
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  node.append("circle")
      .attr("id", function(d) { return d.id; })
      .attr("r", function(d) { return d.r; })
      .style("fill", function(d) { return color(d.package); });

  node.append("clipPath")
      .attr("id", function(d) { return "clip-" + d.id; })
    .append("use")
      .attr("xlink:href", function(d) { return "#" + d.id; });

  node.append("text")
      .attr("clip-path", function(d) { return "url(#clip-" + d.id + ")"; })
    .selectAll("tspan")
    .data(function(d) { return d.class.split(/(?=[A-Z][^A-Z])/g); })
    .enter().append("tspan")
      .attr("x", 0)
      .attr("y", function(d, i, nodes) { return 13 + (i - nodes.length / 2 - 0.5) * 10; })
      .text(function(d) { return d; });

  node.append("title")
      .text(function(d) { return d.id + "\n" + format(d.value); });
});

</script>
```

### Bubble Chart/flare.csv
``` csv
id,value
flare,
flare.analytics,
flare.analytics.cluster,
flare.analytics.cluster.AgglomerativeCluster,3938
flare.analytics.cluster.CommunityStructure,3812
flare.analytics.cluster.HierarchicalCluster,6714
flare.analytics.cluster.MergeEdge,743
flare.analytics.graph,
flare.analytics.graph.BetweennessCentrality,3534
flare.analytics.graph.LinkDistance,5731
flare.analytics.graph.MaxFlowMinCut,7840
flare.analytics.graph.ShortestPaths,5914
flare.analytics.graph.SpanningTree,3416
flare.analytics.optimization,
flare.analytics.optimization.AspectRatioBanker,7074
flare.animate,
flare.animate.Easing,17010
flare.animate.FunctionSequence,5842
flare.animate.interpolate,
flare.animate.interpolate.ArrayInterpolator,1983
flare.animate.interpolate.ColorInterpolator,2047
flare.animate.interpolate.DateInterpolator,1375
flare.animate.interpolate.Interpolator,8746
flare.animate.interpolate.MatrixInterpolator,2202
flare.animate.interpolate.NumberInterpolator,1382
flare.animate.interpolate.ObjectInterpolator,1629
flare.animate.interpolate.PointInterpolator,1675
flare.animate.interpolate.RectangleInterpolator,2042
flare.animate.ISchedulable,1041
flare.animate.Parallel,5176
flare.animate.Pause,449
flare.animate.Scheduler,5593
flare.animate.Sequence,5534
flare.animate.Transition,9201
flare.animate.Transitioner,19975
flare.animate.TransitionEvent,1116
flare.animate.Tween,6006
flare.data,
flare.data.converters,
flare.data.converters.Converters,721
flare.data.converters.DelimitedTextConverter,4294
flare.data.converters.GraphMLConverter,9800
flare.data.converters.IDataConverter,1314
flare.data.converters.JSONConverter,2220
flare.data.DataField,1759
flare.data.DataSchema,2165
flare.data.DataSet,586
flare.data.DataSource,3331
flare.data.DataTable,772
flare.data.DataUtil,3322
flare.display,
flare.display.DirtySprite,8833
flare.display.LineSprite,1732
flare.display.RectSprite,3623
flare.display.TextSprite,10066
flare.flex,
flare.flex.FlareVis,4116
flare.physics,
flare.physics.DragForce,1082
flare.physics.GravityForce,1336
flare.physics.IForce,319
flare.physics.NBodyForce,10498
flare.physics.Particle,2822
flare.physics.Simulation,9983
flare.physics.Spring,2213
flare.physics.SpringForce,1681
flare.query,
flare.query.AggregateExpression,1616
flare.query.And,1027
flare.query.Arithmetic,3891
flare.query.Average,891
flare.query.BinaryExpression,2893
flare.query.Comparison,5103
flare.query.CompositeExpression,3677
flare.query.Count,781
flare.query.DateUtil,4141
flare.query.Distinct,933
flare.query.Expression,5130
flare.query.ExpressionIterator,3617
flare.query.Fn,3240
flare.query.If,2732
flare.query.IsA,2039
flare.query.Literal,1214
flare.query.Match,3748
flare.query.Maximum,843
flare.query.methods,
flare.query.methods.add,593
flare.query.methods.and,330
flare.query.methods.average,287
flare.query.methods.count,277
flare.query.methods.distinct,292
flare.query.methods.div,595
flare.query.methods.eq,594
flare.query.methods.fn,460
flare.query.methods.gt,603
flare.query.methods.gte,625
flare.query.methods.iff,748
flare.query.methods.isa,461
flare.query.methods.lt,597
flare.query.methods.lte,619
flare.query.methods.max,283
flare.query.methods.min,283
flare.query.methods.mod,591
flare.query.methods.mul,603
flare.query.methods.neq,599
flare.query.methods.not,386
flare.query.methods.or,323
flare.query.methods.orderby,307
flare.query.methods.range,772
flare.query.methods.select,296
flare.query.methods.stddev,363
flare.query.methods.sub,600
flare.query.methods.sum,280
flare.query.methods.update,307
flare.query.methods.variance,335
flare.query.methods.where,299
flare.query.methods.xor,354
flare.query.methods._,264
flare.query.Minimum,843
flare.query.Not,1554
flare.query.Or,970
flare.query.Query,13896
flare.query.Range,1594
flare.query.StringUtil,4130
flare.query.Sum,791
flare.query.Variable,1124
flare.query.Variance,1876
flare.query.Xor,1101
flare.scale,
flare.scale.IScaleMap,2105
flare.scale.LinearScale,1316
flare.scale.LogScale,3151
flare.scale.OrdinalScale,3770
flare.scale.QuantileScale,2435
flare.scale.QuantitativeScale,4839
flare.scale.RootScale,1756
flare.scale.Scale,4268
flare.scale.ScaleType,1821
flare.scale.TimeScale,5833
flare.util,
flare.util.Arrays,8258
flare.util.Colors,10001
flare.util.Dates,8217
flare.util.Displays,12555
flare.util.Filter,2324
flare.util.Geometry,10993
flare.util.heap,
flare.util.heap.FibonacciHeap,9354
flare.util.heap.HeapNode,1233
flare.util.IEvaluable,335
flare.util.IPredicate,383
flare.util.IValueProxy,874
flare.util.math,
flare.util.math.DenseMatrix,3165
flare.util.math.IMatrix,2815
flare.util.math.SparseMatrix,3366
flare.util.Maths,17705
flare.util.Orientation,1486
flare.util.palette,
flare.util.palette.ColorPalette,6367
flare.util.palette.Palette,1229
flare.util.palette.ShapePalette,2059
flare.util.palette.SizePalette,2291
flare.util.Property,5559
flare.util.Shapes,19118
flare.util.Sort,6887
flare.util.Stats,6557
flare.util.Strings,22026
flare.vis,
flare.vis.axis,
flare.vis.axis.Axes,1302
flare.vis.axis.Axis,24593
flare.vis.axis.AxisGridLine,652
flare.vis.axis.AxisLabel,636
flare.vis.axis.CartesianAxes,6703
flare.vis.controls,
flare.vis.controls.AnchorControl,2138
flare.vis.controls.ClickControl,3824
flare.vis.controls.Control,1353
flare.vis.controls.ControlList,4665
flare.vis.controls.DragControl,2649
flare.vis.controls.ExpandControl,2832
flare.vis.controls.HoverControl,4896
flare.vis.controls.IControl,763
flare.vis.controls.PanZoomControl,5222
flare.vis.controls.SelectionControl,7862
flare.vis.controls.TooltipControl,8435
flare.vis.data,
flare.vis.data.Data,20544
flare.vis.data.DataList,19788
flare.vis.data.DataSprite,10349
flare.vis.data.EdgeSprite,3301
flare.vis.data.NodeSprite,19382
flare.vis.data.render,
flare.vis.data.render.ArrowType,698
flare.vis.data.render.EdgeRenderer,5569
flare.vis.data.render.IRenderer,353
flare.vis.data.render.ShapeRenderer,2247
flare.vis.data.ScaleBinding,11275
flare.vis.data.Tree,7147
flare.vis.data.TreeBuilder,9930
flare.vis.events,
flare.vis.events.DataEvent,2313
flare.vis.events.SelectionEvent,1880
flare.vis.events.TooltipEvent,1701
flare.vis.events.VisualizationEvent,1117
flare.vis.legend,
flare.vis.legend.Legend,20859
flare.vis.legend.LegendItem,4614
flare.vis.legend.LegendRange,10530
flare.vis.operator,
flare.vis.operator.distortion,
flare.vis.operator.distortion.BifocalDistortion,4461
flare.vis.operator.distortion.Distortion,6314
flare.vis.operator.distortion.FisheyeDistortion,3444
flare.vis.operator.encoder,
flare.vis.operator.encoder.ColorEncoder,3179
flare.vis.operator.encoder.Encoder,4060
flare.vis.operator.encoder.PropertyEncoder,4138
flare.vis.operator.encoder.ShapeEncoder,1690
flare.vis.operator.encoder.SizeEncoder,1830
flare.vis.operator.filter,
flare.vis.operator.filter.FisheyeTreeFilter,5219
flare.vis.operator.filter.GraphDistanceFilter,3165
flare.vis.operator.filter.VisibilityFilter,3509
flare.vis.operator.IOperator,1286
flare.vis.operator.label,
flare.vis.operator.label.Labeler,9956
flare.vis.operator.label.RadialLabeler,3899
flare.vis.operator.label.StackedAreaLabeler,3202
flare.vis.operator.layout,
flare.vis.operator.layout.AxisLayout,6725
flare.vis.operator.layout.BundledEdgeRouter,3727
flare.vis.operator.layout.CircleLayout,9317
flare.vis.operator.layout.CirclePackingLayout,12003
flare.vis.operator.layout.DendrogramLayout,4853
flare.vis.operator.layout.ForceDirectedLayout,8411
flare.vis.operator.layout.IcicleTreeLayout,4864
flare.vis.operator.layout.IndentedTreeLayout,3174
flare.vis.operator.layout.Layout,7881
flare.vis.operator.layout.NodeLinkTreeLayout,12870
flare.vis.operator.layout.PieLayout,2728
flare.vis.operator.layout.RadialTreeLayout,12348
flare.vis.operator.layout.RandomLayout,870
flare.vis.operator.layout.StackedAreaLayout,9121
flare.vis.operator.layout.TreeMapLayout,9191
flare.vis.operator.Operator,2490
flare.vis.operator.OperatorList,5248
flare.vis.operator.OperatorSequence,4190
flare.vis.operator.OperatorSwitch,2581
flare.vis.operator.SortOperator,2023
flare.vis.Visualization,16540
```
### Split bar chart/index.html
``` html
<html>
<head>
<style>

.graphic {
    font-family: sans-serif;
    color: #333;
    fill: #333;
    margin: 40px auto;
}

.hed {
    font-size: 24px;
    font-weight: bold;
    margin-bottom: 8px;
}

.dek {
    font-size: 16px;
    color: #555;
    margin-bottom: 15px;
}

.footnote {
    font-size: 12px;
    color: #999;
    margin-top: 10px;
}

.footnote a {
    color: #888;
    text-decoration: underline dashed;
}

.axis path,
.axis line {
    display: none;
}

.axis text,
.column .title {
    fill: #4c4c4c;
    font-size: 12px;
}

.bar {
    fill: steelblue;
}

.column .bar--underlying {
    fill: #f3f3f3;
}

.label {
    font-size: 12px;
    fill: #4c4c4c;
}

.label--white {
    fill: #fff;
}

</style>
</head>
<body>

<div class="graphic">
    <div class="hed">Social network usage by high schoolers</div>
    <div class="dek">Social network sites used by U.S. high school graduates, by frequency, 2014</div>
    <svg class="chart"></svg>
    <div class="footnote">Source: <a href="http://www.emarketer.com/Article/How-Elusive-Generation-Z-After-All/1011466">eMarketer</a>. Chart cloned from <a href="https://www.datawrapper.de/">Datawrapper</a> example.</div>
</div>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>

var formatLabel = function(d) { return d3.format('.0f')(d * 100); };

var margin = { top: 30, right: 10, bottom: 10, left: 65 },
    width = 600 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

var graphic = d3.select('.graphic')
    .style('width', width + 'px');

var svg = graphic.select('svg.chart')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
    .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

var x = function(d) { return d.share; },
    xScale = d3.scaleLinear(),
    xValue = function(d) { return xScale(x(d)); };

var y = function(d) { return d.social_network; },
    yScale = d3.scaleBand().range([height, 0]).padding(0.1),
    yValue = function(d) { return yScale(y(d)); },
    yAxis = d3.axisLeft(yScale);

var column = function(d) { return d.usage; },
    columnScale = d3.scaleBand().range([0, width]).paddingInner(0.075),
    columnValue = function(d) { return columnScale(column(d)); };

var color = column,
    colorScale = d3.scaleOrdinal(),
    colorValue = function(d) { return colorScale(color(d)); };

function row(d) {
    return {
        usage: d.usage,
        social_network: d.social_network,
        share: +d.share
    };
}

d3.csv('social-network-usage.csv', row, function(error, dataFlat) {
    if (error) throw error;

    var data = d3.nest()
        .key(function(d) { return d.usage; })
        .entries(dataFlat)
        .map(function(d) { return { usage: d.key, values: d.values }; });
    
    yScale.domain(dataFlat.map(y).reverse());
    columnScale.domain(dataFlat.map(column));
    xScale.range([0, columnScale.bandwidth()]);

    // Excluding the light colors from the color scheme
    var colorRange = d3.schemeBuPu[columnScale.domain().length + 2].reverse();
    colorScale
        .domain(dataFlat.map(color))
        .range(colorRange);

    svg.append('g').attr('class', 'axis axis--y')
        .call(yAxis);

    var gColumn = svg.append('g').attr('class', 'columns')
            .selectAll('.column').data(data)
        .enter().append('g')
            .attr('class', 'column')
            .attr('transform', function(d) { return 'translate(' + columnValue(d) + ',0)'; });

    gColumn.append('text').attr('class', 'title')
        .attr('dy', '-0.34em')
        .text(column);
    
    var bars = gColumn.append('g').attr('class', 'bars');

    bars.selectAll('.bar--underlying').data(function(d) { return d.values; })
        .enter().append('rect')
            .attr('class', 'bar bar--underlying')
            .attr('x', 0)
            .attr('y', function(d) { return yScale(y(d)); })
            .attr('width', xScale.range()[1])
            .attr('height', yScale.bandwidth());

    bars.selectAll('.bar--overlying').data(function(d) { return d.values; })
        .enter().append('rect')
            .attr('class', 'bar bar--overlying')
            .attr('x', 0)
            .attr('y', function(d) { return yScale(y(d)); })
            .attr('width', function(d) { return xScale(x(d)); })
            .attr('height', yScale.bandwidth())
            .style('fill', colorValue);

    function positionLabel(d) {
        var xValue = xScale(x(d));
        var xMax = xScale.range()[1];
        if (xValue < (0.25 * xMax)) {
            d3.select(this)
                .classed('label--white', false)
                .attr('x', xValue)
                .attr('dx', 2);
        } else {
            d3.select(this)
                .classed('label--white', true)
                .attr('x', 0)
                .attr('dx', 4);
        }
        d3.select(this)
            .attr('y', yScale(y(d)) + (yScale.bandwidth() / 2))
            .attr('dy', '0.33em');
    }

    gColumn.append('g').attr('class', 'labels')
            .selectAll('.label').data(function(d) { return d.values; })
        .enter().append('text') 
            .attr('class', 'label')
            .text(function(d) { return formatLabel(x(d)); })
            .each(positionLabel);

});

</script>
</body>
</html>
```
### Split bar chart/social-network-usage.csv
``` csv
usage,social_network,share
A few times a day,Facebook,0.47
Once a day,Facebook,0.14
A few times a week,Facebook,0.12
Occasionally,Facebook,0.14
I don't use it,Facebook,0.13
A few times a day,Instagram,0.43
Once a day,Instagram,0.08
A few times a week,Instagram,0.07
Occasionally,Instagram,0.08
I don't use it,Instagram,0.34
A few times a day,Twitter,0.28
Once a day,Twitter,0.07
A few times a week,Twitter,0.07
Occasionally,Twitter,0.13
I don't use it,Twitter,0.45
A few times a day,Google+,0.16
Once a day,Google+,0.07
A few times a week,Google+,0.08
Occasionally,Google+,0.21
I don't use it,Google+,0.48
A few times a day,Tumblr,0.11
Once a day,Tumblr,0.04
A few times a week,Tumblr,0.06
Occasionally,Tumblr,0.12
I don't use it,Tumblr,0.66
A few times a day,Pinterest,0.1
Once a day,Pinterest,0.06
A few times a week,Pinterest,0.1
Occasionally,Pinterest,0.18
I don't use it,Pinterest,0.56
```

## 実行
```shell
python server.py
```
簡易サーバ停止は<kbd>Ctrl+c</kbd>
簡易サーバ起動後
ブラウザーで<kbd>http://localhost:8000/</kbd>を入力

## 結果
`Index.html` で定義したリンクが表示されます。
![TS_015.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/0b3f087d-d100-92ab-a159-0994e18a2374.jpeg)
`Bubble Chart` をクリック
![TS_016.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/ce6d80c5-14ee-d3e2-7204-317fdab3f46f.jpeg)
`Split bar chart` をクリック
![TS_017.jpg](https://qiita-image-store.s3.amazonaws.com/0/239597/c03ea52e-0f25-f70c-ebd0-0a50ae787e44.jpeg)

## まとめ
d3のチャートサンプルはたくさんあります。ローカルで開発と試験を行う上で簡易サーバが必要です。
d3サイトからソースを取得してローカルでいじくりまわしましょう。
root/index.htmlにどしどし追加のリンクを張って利用してください。
サンプルはJavaScriptで記述されています。少しづつTypeScript化したいですね。

以上、おそまつ