Four different time series visualizations: scatterplot, line chart, stackedgraph, and streamgraph. Time series show the price history of the cryptocurrency since 2017.
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<style>
.visualization-type{
margin-top:5px;
margin-left:10px;
}
.line{
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: 1.5px;
fill: none;
}
.text{
font: 14px sans-serif;
}
</style>
</head>
<body>
<div>
<h4 style="display: inline;" class ="visualization-type">Visualization type:</h4>
<select id="visualizationType" class ="visualization-type">
<option value = "scatterplot">Scatterplot</option>
<option value = "line-chart" selected>Line chart</option>
<option value = "stackedgraph">Stackedgraph</option>
<option value = "streamgraph">Streamgraph</option>
</select>
</div>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 30, right: 100, bottom: 70, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform","translate(" + margin.left + "," + margin.top + ")"),
l = svg.append("g").attr("transform","translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%d/%m/%Y");
// x Axis (Time dimension)
var x = d3.scaleTime().rangeRound([0,width]);
// y Axis (Quantitative)
var y = d3.scaleLinear().rangeRound([height,0]);
// Color
var color = d3.scaleOrdinal(d3.schemeCategory10);
// stack
var stack = d3.stack()
.order(d3.stackOrderNone);
//area
var area = d3.area()
.x(function(d,i){return x(d.data.date);})
.y0(function(d){return y(d[0]);})
.y1(function(d){return y(d[1]);});
//line
var line = d3.line()
.x(function(d) {return x(d.date); })
.y(function(d) {return y(d.price); });
var cryptoPrice = [];
//load the data
d3.csv("data.csv", type, function(error, data){
if(error) throw error;
cryptoPrice = data.columns.slice(1).map(function(key){
return {
key:key,
values: data.map(function(d) {
return {date: d.date, price: d[key]};
})
};
});
//get keys to stack
var keys = data.columns.slice(1);
//domain time dimension
x.domain(d3.extent(data,function(d){return d.date;})).nice();
//domain quantitative dimension
y.domain([
d3.min(cryptoPrice, function(c){return d3.min(c.values,function(d){return d.price;});}),
d3.max(cryptoPrice, function(c){return d3.max(c.values,function(d){return d.price;});})
]).nice();
//domain color dimension
color.domain(keys);
//Join new data with old elements
g.selectAll("path")
.data(cryptoPrice, function(d){return d.key;})
.enter().append("path")
.attr("class","line")
.attr("d",function(d){ return line(d.values);})
.attr("stroke",function(d){return color(d.key);});
//Enter legend
g.selectAll("text")
.data(cryptoPrice, function(d){return d.key;})
.enter().append("text")
.datum(function(d) {return {key: d.key, value: d.values[0]}; })
.attr("class","text")
.attr("y",function(d){return y(d.value.price)})
.attr("x", width + 2)
.attr("dy", "0.35em")
.text(function(d) {return d.key; });
l.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
l.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("fill", "#000")
.text("Price, $");
//Update Visualization layout
d3.select("#visualizationType").on("change",function(){
cryptoPrice.forEach(function(d,i) {
g.selectAll("#scatter-"+i).data([]).exit().remove();
});
d3.select(".axis--y").style("display","inline");
switch(this.value){
case "line-chart":
//update the Y domain
y.domain([
d3.min(cryptoPrice, function(c){return d3.min(c.values,function(d){return d.price;});}),
d3.max(cryptoPrice, function(c){return d3.max(c.values,function(d){return d.price;});})
]).nice();
d3.select(".axis--y").transition().call(d3.axisLeft(y));
//Join data
var cryptoLine = g.selectAll("path")
.data(cryptoPrice, function(d){return d.key;});
//Update
cryptoLine.attr("class","line")
.style("fill-opacity", 1e-6)
.transition()
.duration(850)
.style("fill-opacity", 1)
.attr("d",function(d){return line(d.values);})
//Enter
cryptoLine.enter().append("path")
.transition()
.duration(850)
.attr("class","line")
.attr("d",function(d){return line(d.values);})
.attr("stroke",function(d){return color(d.key);});
//Exit
cryptoLine.exit().remove();
//update legend position
g.selectAll("text")
.data(cryptoPrice)
.datum(function(d) {return {key: d.key, value: d.values[0]}; })
.transition()
.duration(850)
.attr("y", function(d) {return y(d.value.price); })
break;
case "scatterplot":
//update the Y domain
y.domain([
d3.min(cryptoPrice, function(c){return d3.min(c.values,function(d){return d.price;});}),
d3.max(cryptoPrice, function(c){return d3.max(c.values,function(d){return d.price;});})
]).nice();
d3.select(".axis--y").transition().call(d3.axisLeft(y));
//remove the line or layer path
g.selectAll("path").data([]).exit().remove();
// add the scatterplot value
cryptoPrice.forEach(function(d,i) {
g.append("g")
.attr("id",function(d){return "scatter-"+i})
.selectAll("dot")
.data(d.values)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 2)
.attr("cx", function (d) {return x(d.date);})
.attr("cy", function (d) {return y(d.price);})
.style("fill", function(d) {return color(cryptoPrice[i].key);});
});
//update legend position
g.selectAll("text")
.data(cryptoPrice)
.datum(function(d) {return {key: d.key, value: d.values[0]}; })
.transition()
.duration(850)
.attr("y", function(d) {return y(d.value.price); })
break;
case "stackedgraph":
stack.offset(d3.stackOffsetNone);
var dataStacked = stack.keys(keys)(data);
//update the Y domain
y.domain([0,d3.max(data,function(d){return d.total;})]).nice();
d3.select(".axis--y").transition().call(d3.axisLeft(y));
//Join data
var cryptoLayer = g.selectAll("path")
.data(dataStacked, function(d){return d.key;});
//Update
cryptoLayer.attr("class","layer")
.style("fill-opacity", 1e-6)
.transition()
.duration(850)
.attr("d",area)
.style("fill-opacity", 1)
.attr("fill",function(d){return color(d.key);})
.attr("stroke",function(d){return color(d.key);});
//Enter
cryptoLayer.enter().append("path")
.transition()
.duration(850)
.attr("class","layer")
.attr("d",area)
.attr("fill",function(d){return color(d.key);})
.attr("stroke",function(d){return color(d.key);});
//Exit
cryptoLayer.exit().remove();
//update the legends position
g.selectAll("text")
.data(dataStacked)
.datum(function(d) {return {key: d.key, value: (d[0][0] + d[0][1])/2}; })
.transition()
.duration(850)
.attr("y", function(d) {return y(d.value); })
.attr("x", width + 2)
.attr("dy", "0.35em");
break;
case "streamgraph":
stack.offset(d3.stackOffsetWiggle);
var dataStacked = stack.keys(keys)(data);
//update the Y domain
y.domain([
d3.min(dataStacked, function(c){return d3.min(c, function(d) { return d[0]; });}),
d3.max(dataStacked, function(c){return d3.max(c, function(d) { return d[1]; });})
]).nice();
d3.select(".axis--y").style("display","none");
//Join data
var cryptoLayer = g.selectAll("path")
.data(dataStacked, function(d){return d.key;});
//Update
cryptoLayer.attr("class","layer")
.style("fill-opacity", 1e-6)
.transition()
.duration(850)
.attr("d",area)
.style("fill-opacity", 1)
.attr("fill",function(d){return color(d.key);})
.attr("stroke",function(d){return color(d.key);});
//Enter
cryptoLayer.enter().append("path")
.transition()
.duration(850)
.attr("class","layer")
.attr("d",area)
.attr("fill",function(d){return color(d.key);})
.attr("stroke",function(d){return color(d.key);});
//Exit
cryptoLayer.exit().remove();
//update the legends position
g.selectAll("text")
.data(dataStacked)
.datum(function(d) {return {key: d.key, value: (d[0][0] + d[0][1])/2}; })
.transition()
.duration(850)
.attr("y", function(d) {return y(d.value); })
.attr("x", width + 2)
.attr("dy", "0.35em");
break;
}
});
})
function type(d,i,columns){
d.date = parseTime(d.date);
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js