Example of transitioning between a scatter chart and bar chart.
Last active
January 13, 2020 03:45
-
-
Save johnwalley/764d192d8b9971dc26ffd0556de887cd to your computer and use it in GitHub Desktop.
Bar Scatter Transition
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit | |
height: 668 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
id | risk | return | equity | interest | |
---|---|---|---|---|---|
Strategy 1 | 0.3 | 0.026 | 0.6 | 0.4 | |
Strategy 2 | 0.16 | 0.031 | 0.8 | 0.2 | |
Strategy 3 | 0.42 | 0.034 | 0.5 | 0.5 | |
Strategy 4 | 0.08 | 0.06 | 0.5 | 0.5 | |
Strategy 5 | 0.1 | 0.021 | 0.3 | 0.7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
<div class="container"> | |
<h1>Investment Strategies</h1> | |
<p>Different asset allocations offer a range of potential risk and return characteristics. | |
</p> | |
<div class="btn-group" role="group"> | |
<button type="button" id="scatter" class="btn btn-default" value="scatter">Efficient frontier</button> | |
<button type="button" id="bar" class="btn btn-default" value="bar">Sources of investment risk</button> | |
</div> | |
<div> | |
<svg width="1024" height="480"></svg> | |
</div> | |
<p id="summary"></p> | |
</div> | |
<style> | |
.axis text, | |
.yAxis text { | |
font-family: sans-serif; | |
font-size: 14px; | |
fill: darkslategray; | |
} | |
</style> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script> | |
var DURATION = 500; | |
var margin = { top: 20, right: 20, bottom: 40, left: 160 }; | |
var svg = d3.select("svg"); | |
var width = +svg.attr("width") - margin.left - margin.right; | |
var height = +svg.attr("height") - margin.top - margin.bottom; | |
var g = svg.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var xScaleScatter = d3.scaleLinear() | |
.range([0, width]); | |
var xScaleBar = d3.scaleLinear() | |
.range([0, width]); | |
var yScaleScatter = d3.scaleLinear() | |
.range([height, 0]); | |
var yScaleBar = d3.scaleBand() | |
.range([height, 0]) | |
.padding(0.2); | |
var z = d3.scaleOrdinal() | |
.range([ | |
'#ccf7f6', | |
'#70e4e0', | |
'#00d8d2', | |
'#00acaf', | |
'#007f8c', | |
'#005e66', | |
'#003c3f', | |
'#002d2f', | |
'#0d2223' | |
]); | |
var idScale = d3.scaleOrdinal() | |
.range([ | |
'#6aedc7', //green | |
'#39c2c9', //blue | |
'#ffce00', //yellow | |
'#ffa71a', //orange | |
'#f866b9', //pink | |
'#998ce3' //purple | |
]); | |
d3.csv('data.csv', function (d) { | |
d.risk = +d.risk; | |
d.return = +d.return; | |
d.equity = +d.equity; | |
d.interest = +d.interest; | |
return d; | |
} | |
, function (data) { | |
data = data.sort(function (a, b) { return a.return - b.return; }) | |
xScaleScatter.domain([0, d3.max(data, function (d) { return d.risk; })]).nice(); | |
xScaleBar.domain([0, 1]); | |
yScaleScatter.domain([0, d3.max(data, function (d) { return d.return; })]).nice(); | |
yScaleBar.domain(data.map(function (d) { return d.id; })); | |
z.domain(["equity", "interest"]); | |
var xAxis = g.append("g") | |
.attr("class", "axis axis--x") | |
.attr("transform", "translate(0," + height + ")") | |
.call(d3.axisBottom(xScaleScatter).ticks(10, "%")); | |
xAxis.select(".domain").remove(); | |
xAxis.selectAll(".tick") | |
.attr("y", 20) | |
.attr("x", 0); | |
var yAxisScatter = g.append("g") | |
.attr("class", "axis axis--y") | |
.call(d3.axisLeft(yScaleScatter).ticks(10, "%")); | |
yAxisScatter.select(".domain").remove(); | |
yAxisScatter.selectAll(".tick") | |
.attr("y", 0) | |
.attr("x", -20); | |
var yAxisBar = g.append("g") | |
.attr("class", "axis axis--y") | |
.call(d3.axisLeft(yScaleBar)); | |
yAxisBar.select(".domain").remove(); | |
var bars = g.selectAll('.bar') | |
.data(data) | |
.enter() | |
.append("rect") | |
.attr("class", "bar") | |
.attr("cursor", "pointer") | |
.attr("fill", function (d) { return idScale(d.id); }) | |
.attr("rx", 20) | |
.attr("ry", 20) | |
.attr("width", 20) | |
.attr("y", function (d) { return yScaleScatter(d.return) - 10; }) | |
.attr("height", 20) | |
.attr("x", function (d) { return xScaleScatter(d.risk) - 10; }); | |
var stack = d3.stack() | |
.keys(["equity", "interest"]) | |
.order(d3.stackOrderNone); | |
var stacks = g.selectAll(".stack") | |
.data(stack(data)) | |
.enter() | |
.append("g") | |
.attr("class", "stack") | |
.attr("fill", function (d) { return z(d.key); }) | |
.selectAll("rect") | |
.data(function (d) { return d; }) | |
.enter() | |
.append("rect") | |
.attr("y", function (d) { return yScaleBar(d.data.id); }) | |
.attr("x", function (d) { return xScaleBar(d[0]); }) | |
.attr("height", yScaleBar.bandwidth()) | |
.attr("width", function (d) { return (xScaleBar(d[1]) - xScaleBar(d[0])); }) | |
.attr("opacity", 0); | |
var previousChartType = "scatter"; | |
drawScatter(); | |
d3.select("#scatter").on("click", function () { | |
drawScatter(); | |
}); | |
d3.select("#bar").on("click", function () { | |
drawBar(); | |
}); | |
function drawScatter() { | |
d3.select("#summary").text("Risk and return position of the five investment strategies. The vertical axis shows the potential return. The horizontal axis shows the Value at Risk (VaR)."); | |
xAxis.transition() | |
.duration(DURATION) | |
.call(d3.axisBottom(xScaleScatter).ticks(10, "%")); | |
xAxis.select(".domain").remove(); | |
yAxisScatter.transition() | |
.duration(DURATION) | |
.delay(3 * DURATION) | |
.attr('opacity', 1); | |
yAxisBar.transition() | |
.duration(DURATION) | |
.delay(previousChartType === "bar" ? 3 * DURATION : 0) | |
.attr('opacity', 0); | |
bars.transition() | |
.duration(previousChartType === "bar" ? DURATION : 0) | |
.delay(DURATION) | |
.style("opacity", 1) | |
.transition() | |
.duration(previousChartType === "bar" ? DURATION : 0) | |
.attr("rx", 20) | |
.attr("ry", 20) | |
.attr("width", 20) | |
.attr("x", function (d) { return xScaleScatter(d.risk) - 10; }) | |
.attr("height", 20) | |
.attr("y", function (d) { return yScaleBar(d.id) + yScaleBar.bandwidth() / 2 - 10; }) | |
.transition() | |
.duration(previousChartType === "bar" ? DURATION : 0) | |
.attr("y", function (d) { | |
return yScaleScatter(d.return) - 10; | |
}); | |
stacks.transition() | |
.transition() | |
.duration(DURATION) | |
.attr("y", function (d) { return yScaleBar(d.data.id); }) | |
.attr("x", function (d) { return xScaleScatter(d[0]) * d.data.risk; }) | |
.attr("width", function (d) { return (xScaleScatter(d[1]) - xScaleScatter(d[0])) * d.data.risk; }) | |
.transition() | |
.duration(previousChartType === "bar" ? DURATION : 0) | |
.style("opacity", 0); | |
previousChartType = "scatter"; | |
} | |
function drawBar() { | |
d3.select("#summary").text("Sources of the investment risk underlying the five investment strategies"); | |
xAxis.transition() | |
.duration(DURATION) | |
.delay(3 * DURATION) | |
.call(d3.axisBottom(xScaleBar).ticks(10, "%")); | |
xAxis.select(".domain").remove(); | |
yAxisScatter.transition() | |
.duration(DURATION) | |
.attr('opacity', 0); | |
yAxisBar.transition() | |
.duration(DURATION) | |
.attr('opacity', 1); | |
bars.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.attr("y", function (d) { return yScaleBar(d.id) + yScaleBar.bandwidth() / 2 - 10; }) | |
.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.attr("y", function (d) { return yScaleBar(d.id); }) | |
.attr("height", yScaleBar.bandwidth()) | |
.attr("rx", 0) | |
.attr("ry", 0) | |
.attr("x", function (d) { return xScaleScatter(0); }) | |
.attr("width", function (d) { return xScaleScatter(d.risk); }) | |
.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.style("opacity", 1); | |
stacks.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.delay(2 * DURATION) | |
.style("opacity", 1) | |
.transition() | |
.duration(DURATION) | |
.attr("y", function (d) { return yScaleBar(d.data.id); }) | |
.attr("x", function (d) { return xScaleBar(d[0]); }) | |
.attr("width", function (d) { return xScaleBar(d[1]) - xScaleBar(d[0]); }); | |
previousChartType = "bar"; | |
} | |
function drawBar2() { | |
xAxisScatter.transition() | |
.duration(DURATION) | |
.attr('opacity', 0); | |
xAxisBar.transition() | |
.duration(DURATION) | |
.attr('opacity', 1); | |
yAxis.transition() | |
.duration(DURATION) | |
.delay(DURATION) | |
.call(d3.axisLeft(yScaleBar).ticks(10, "%")) | |
bars.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.attr("x", function (d) { return xScaleScatterBar(d.id) + xScaleScatterBar.bandwidth() / 2; }) | |
.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.attr("x", function (d) { return xScaleScatterBar(d.id); }) | |
.attr("width", xScaleScatterBar.bandwidth()) | |
.attr("rx", 0) | |
.attr("ry", 0) | |
.attr("y", function (d) { return yScaleBar(d.risk); }) | |
.attr("height", function (d) { return height - yScaleBar(d.risk); }) | |
.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.style("opacity", 0); | |
stacks.transition() | |
.duration(previousChartType === "scatter" ? DURATION : 0) | |
.delay(2 * DURATION) | |
.style("opacity", 1); | |
previousChartType = "bar"; | |
} | |
}); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment