|
<!DOCTYPE html>
|
|
<meta charset="utf-8">
|
|
|
|
<style></style>
|
|
|
|
<svg width="960" height="500">
|
|
<g id="chart">
|
|
<g id="legend" font-family="sans-serif" text-anchor="end"></g>
|
|
<text id="cluster" font-size="64" text-anchor="middle"></text>
|
|
</g>
|
|
</svg>
|
|
|
|
<script src="https://d3js.org/d3.v4.js"></script>
|
|
|
|
<script>
|
|
// 変化しない描画要素の初期化
|
|
var svg = d3.select("svg"),
|
|
margin = { top: 20, right: 20, bottom: 30, left: 40 },
|
|
width = +svg.attr("width") - margin.left - margin.right,
|
|
height = +svg.attr("height") - margin.top - margin.bottom;
|
|
var chart = svg.select("g")
|
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
|
var legend = chart.select("g");
|
|
var cluster = chart.select("text")
|
|
.attr("x", width / 2)
|
|
.attr("y", height / 2)
|
|
.attr("fill", "black");
|
|
|
|
// 軸の変化しない部分を設定
|
|
var x0 = d3.scaleBand().rangeRound([0, width]);
|
|
var x1 = d3.scaleBand() // .rangeRoundはx0.domainをやってみた後のグループ毎の幅を後でセットする
|
|
.padding(0.1); // グループ内の各bar間の空白調整(単位は割合?)
|
|
var y = d3.scaleLinear().rangeRound([height, 0]).domain([0, 100]); // 比較のためY軸のdomainは固定
|
|
|
|
// 描画単位が国かサービスかで、使うデータセットやスケール類を束ねておきます
|
|
var bycountry = {};
|
|
var byservice = {};
|
|
var target = [bycountry, byservice];
|
|
|
|
// 色チャート
|
|
bycountry.colors = d3.scaleOrdinal().range([
|
|
// Facebook, Google+, Twitter, LinkedIn, YouTube, USTREAM, Instagram, Tumblr, Pinterest,
|
|
"#305097", "#db4a39", "#00aced", "#0077B5", "#cd201f", "#3388ff", "#bc2a8d", "#35465c", "#bd081c",
|
|
// LINE, WhatsApp, 微博(Weibo), 微信(WeChat), 人人網(Renren), KakaoTalk, Other
|
|
"#5ae628", "#075e54", "#df2029", "#7bb32e", "#2266b0", "#fcd411", "#6a737b"
|
|
]);
|
|
byservice.colors = d3.scaleOrdinal().range([
|
|
// Japan, USA, China, UK, Germany, India, Korea, Australia
|
|
"Red", "blue", "yellow", "blue", "gold", "Saffron", "orange", "Green"
|
|
]);
|
|
|
|
// 年代のリスト
|
|
var ages = {}; // total, 20-29,,,,
|
|
// グループ内の項目名のリスト
|
|
bycountry.group = []; // Facebook, Google+,,,
|
|
byservice.group = {}; // Japan, USA,,,
|
|
|
|
d3.csv("social.csv")
|
|
// 各行の加工
|
|
.row(function (raw, i, columns) {
|
|
ages[raw['Age']] = raw['Age'];
|
|
byservice.group[raw['Country']] = raw['Country'];
|
|
for (var j = 2, n = columns.length; j < n; ++j)
|
|
raw[columns[j]] = +raw[columns[j]]; // 文字列値を可能なら数値に変換している
|
|
raw['Group'] = raw['Country'];
|
|
return raw;
|
|
})
|
|
// 加工後のデータが揃ったら描画
|
|
.get(function (error, rows) {
|
|
if (error) throw error;
|
|
ages = Object.keys(ages); // 扱い易く素直なリストにしておく
|
|
bycountry.group = rows.columns.slice(2); // Country,Age, までを捨てる
|
|
byservice.group = Object.keys(byservice.group); // ages同様
|
|
// CSVの列構造ママ
|
|
bycountry.data = rows;
|
|
// Group, Age, Japan, USA,,,,,
|
|
// Facebook, total, 35.3, 77.7,,,,, となる筈
|
|
byservice.data = Array.prototype.concat.apply([], bycountry.group.map((s) => {
|
|
return ages.map((a) => {
|
|
var temp = { "Group": s, "Age": a };
|
|
rows.filter((row, i) => row.Age == a).map((row) => temp[row.Country] = row[s]);
|
|
return temp;
|
|
});
|
|
}));
|
|
// 今描画している単位(国orサービス)を保持するためのリスト
|
|
bycountry.list = byservice.group.concat(); // Japan, USA,,,
|
|
byservice.list = bycountry.group.concat(); // Facebook, Google+,,,
|
|
// とりあえず描画
|
|
drawFrame();
|
|
drawLegend();
|
|
draw();
|
|
});
|
|
|
|
function draw() {
|
|
var dataset = target[0];
|
|
var dset = dataset.data.filter((row, i) => row['Group'] == dataset.list[0]);
|
|
|
|
// mapでAgeの凡例リストを作って、横軸の項目として設定して描画
|
|
x0.domain(dset.map((d) => d.Age));
|
|
chart.selectAll("#axisx").call(d3.axisBottom(x0));
|
|
// x0.bandwidth()で棒グラフのグループ毎の幅を取れる
|
|
// その幅をさらにグループ内の項目数で均等割しているのではないか説
|
|
x1.domain(dataset.group).rangeRound([0, x0.bandwidth()]);
|
|
|
|
// 棒グラフのグループにデータセットを束縛
|
|
var grps = chart.selectAll("#group")
|
|
.data(dset, function (d) { return d.Age });
|
|
grps.enter()
|
|
.append("g").attr("id", "group");
|
|
grps.exit().remove();
|
|
|
|
// グループの横位置を移動
|
|
chart.selectAll("#group")
|
|
.attr("transform", function (d) { return "translate(" + x0(d.Age) + ",0)"; });
|
|
|
|
// グループ内の各barにデータを束縛
|
|
var bars = chart.selectAll("#group").selectAll("#bar")
|
|
// 各グループ毎に棒を書くため[{key:"Facebook", value:2704659},{},,]というデータを作る
|
|
.data(function (d) { return dataset.group.map(function (key) { return { key: key, value: d[key] }; }); }, function (d) { return d.key });
|
|
bars.enter()
|
|
.append("rect").attr("id", "bar")
|
|
// 以下は要素が追加された場合の初期値となる
|
|
.attr("width", x1.bandwidth())
|
|
.attr("height", 0)
|
|
.attr("x", function (d) { return x1(d.key); })
|
|
.attr("y", +svg.attr("height") - margin.top - margin.bottom);
|
|
bars.exit().remove();
|
|
|
|
// 束縛したデータでbarを再描画
|
|
chart.selectAll("#bar")
|
|
.transition().duration(1000)
|
|
.attr("fill", function (d) { return dataset.colors(d.key); })
|
|
.attr("x", function (d) { return x1(d.key); })
|
|
.attr("y", function (d) { return y(d.value); })
|
|
.attr("width", x1.bandwidth())
|
|
.attr("height", function (d) { return height - y(d.value); });
|
|
|
|
// 説明テキストも再描画
|
|
cluster
|
|
.transition().duration(500)
|
|
.attr("opacity", 0.0)
|
|
.transition().duration(500)
|
|
.text(dataset.list[0])
|
|
.attr("opacity", 0.3);
|
|
}
|
|
|
|
function drawLegend() {
|
|
var dataset = target[0];
|
|
legend.selectAll("g").remove();
|
|
var ls = legend.selectAll("g")
|
|
.data(dataset.group.slice())
|
|
.enter()
|
|
.append("g")
|
|
.attr("transform", function (d, i) { return "translate(0," + i * 20 + ")"; });
|
|
ls.append("rect")
|
|
.attr("x", width - 19)
|
|
.attr("width", 19)
|
|
.attr("height", 19)
|
|
.attr("fill", dataset.colors);
|
|
ls.append("text")
|
|
.attr("x", width - 24)
|
|
.attr("y", 9.5)
|
|
.attr("dy", "0.32em")
|
|
.text(function (d) { return d; });
|
|
}
|
|
|
|
function drawFrame() {
|
|
chart.append("g").attr("id", "axisx")
|
|
.attr("class", "axis")
|
|
.attr("transform", "translate(0," + height + ")");
|
|
chart.append("g")
|
|
.attr("class", "axis")
|
|
.call(d3.axisLeft(y).ticks(null, "s"))
|
|
.append("text")
|
|
.attr("x", 2)
|
|
.attr("y", y(y.ticks().pop()) + 0.5)
|
|
.attr("dy", "0.32em")
|
|
.attr("fill", "#000")
|
|
.attr("font-weight", "bold")
|
|
.attr("text-anchor", "start")
|
|
.text("Utilization(%)");
|
|
}
|
|
|
|
// カーソルキーを拾って描画対象を切り替え
|
|
document.onkeydown = function (event) {
|
|
if (event.keyCode == 39) { // right key
|
|
target[0].list.push(target[0].list.shift());
|
|
draw();
|
|
}
|
|
else if (event.keyCode == 37) { // left key
|
|
target[0].list.unshift(target[0].list.pop());
|
|
draw();
|
|
}
|
|
else if (event.keyCode == 38 || event.keyCode == 40) { // up or down key
|
|
target.unshift(target.pop());
|
|
drawLegend();
|
|
draw();
|
|
}
|
|
}
|
|
|
|
</script> |