A scatter plot or scatter graph is another common type of diagram used to display data points on Cartesian coordinates with two different variables. Scatter plot is especially useful when exploring the problem of clustering and classification. In this recipe, we will learn how to implement a multi-series scatter plot chart in D3.
Open your local copy of the following file in your web browser:
https://github.com/NickQiZhu/d3-cookbook/blob/master/src/chapter8/scatterplot-chart.html
A scatter plot is another chart which uses Cartesian coordinates. Thus, a large part of its implementation is very similar to the charts we have introduced so far, therefore the code concerning peripheral graphical elements are again omitted to save space in this book. Please review the companion code for the complete implementation.
... _symbolTypes = d3.scale.ordinal() // <-A .range(["circle", "cross", "diamond", "square", "triangle-down", "triangle-up"]); ... function renderBody(svg) { if (!_bodyG) _bodyG = svg.append("g") .attr("class", "body") .attr("transform", "translate(" + xStart() + "," + yEnd() + ")") .attr("clip-path", "url(#body-clip)"); renderSymbols(); } function renderSymbols() { // <-B _data.forEach(function (list, i) { _bodyG.selectAll("path._" + i) .data(list) .enter() .append("path") .attr("class", "symbol _" + i); _bodyG.selectAll("path._" + i) .data(list) .classed(_symbolTypes(i), true) .transition() .attr("transform", function(d){ return "translate(" + _x(d.x) + "," + _y(d.y) + ")"; }) .attr("d", d3.svg.symbol().type(_symbolTypes(i))); }); } ...
This recipe generates a scatter plot chart:
The content of the scatter plot chart is mainly rendered by the renderSymbols
function on line B. You probably have already noticed that the renderSymbols
function implementation is very similar to the renderDots
function we discussed in the Creating a line chart recipe. This is not by accident since both are trying to plot data points on Cartesian coordinates with two variables (x and y). In the case of plotting dots, we were creating svg:circle
elements, while in scatter plot we need to create d3.svg.symbol
elements. D3 provides a list of predefined symbols that can be generated easily and rendered using an svg:path
element. On line A we defined an ordinal scale to allow mapping of data series index to different symbol types:
_symbolTypes = d3.scale.ordinal() // <-A .range(["circle", "cross", "diamond", "square", "triangle-down", "triangle-up"]);
Plotting the data points with symbols is quite straight-forward. First we loop through the data series array and for each data series we create a set of svg:path
elements representing each data point in the series.
_data.forEach(function (list, i) { _bodyG.selectAll("path._" + i) .data(list) .enter() .append("path") .attr("class", "symbol _" + i); ... });
Whenever data series are updated, as well as for newly created symbols, we apply the update with transition (line C) placing them on the right coordinates with an SVG translation transformation (line D).
_bodyG.selectAll("path._" + i) .data(list) .classed(_symbolTypes(i), true) .transition() // <-C .attr("transform", function(d){ return "translate(" // <-D + _x(d.x) + "," + _y(d.y) + ")"; }) .attr("d", d3.svg.symbol() // <-E .type(_symbolTypes(i)) );
Finally, the d
attribute of each svg:path
element is generated using the d3.svg.symbol
generator function as shown on line E.