A reusable D3 scatter plot, constructed using Towards Reusable Charts and Standalone Scatter Plot as inspiration.
In the original reusable charts example, the comments state
// Convert data to standard representation greedily;
// this is needed for nondeterministic accessors.
In this adaptation, a simplifying assumption was made that no nondeterministic accessors will be given. Going one step further, an assumption was made that accessors will only want to access specific “columns” of data, meaning properties on each row object.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Reusable Scatterplot Example</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<link href="//fonts.googleapis.com/css?family=Poiret+One" rel="stylesheet" type="text/css">
<style>
/* Make the container flush against the upper left corner. */
body {
margin: 0px;
}
/* Put a border around SVG elements so we can see how big they are. */
svg {
border-style: solid;
border-width: 1px;
}
/* Style the X and Y axes. */
.axis text {
font-family: "Poiret One", cursive;
font-size: 16pt;
}
.axis .label {
font-size: 32pt;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="chart"></div>
<script src="scatterPlot.js"></script>
<script src="main.js"></script>
<script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-66108038-1', 'auto'); ga('send', 'pageview'); </script>
</body>
</html>
sepal_length,sepal_width,petal_length,petal_width,species
5.1,3.5,1.4,0.2,setosa
4.9,3.0,1.4,0.2,setosa
4.7,3.2,1.3,0.2,setosa
4.6,3.1,1.5,0.2,setosa
5.0,3.6,1.4,0.2,setosa
5.4,3.9,1.7,0.4,setosa
4.6,3.4,1.4,0.3,setosa
5.0,3.4,1.5,0.2,setosa
4.4,2.9,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.4,3.7,1.5,0.2,setosa
4.8,3.4,1.6,0.2,setosa
4.8,3.0,1.4,0.1,setosa
4.3,3.0,1.1,0.1,setosa
5.8,4.0,1.2,0.2,setosa
5.7,4.4,1.5,0.4,setosa
5.4,3.9,1.3,0.4,setosa
5.1,3.5,1.4,0.3,setosa
5.7,3.8,1.7,0.3,setosa
5.1,3.8,1.5,0.3,setosa
5.4,3.4,1.7,0.2,setosa
5.1,3.7,1.5,0.4,setosa
4.6,3.6,1.0,0.2,setosa
5.1,3.3,1.7,0.5,setosa
4.8,3.4,1.9,0.2,setosa
5.0,3.0,1.6,0.2,setosa
5.0,3.4,1.6,0.4,setosa
5.2,3.5,1.5,0.2,setosa
5.2,3.4,1.4,0.2,setosa
4.7,3.2,1.6,0.2,setosa
4.8,3.1,1.6,0.2,setosa
5.4,3.4,1.5,0.4,setosa
5.2,4.1,1.5,0.1,setosa
5.5,4.2,1.4,0.2,setosa
4.9,3.1,1.5,0.1,setosa
5.0,3.2,1.2,0.2,setosa
5.5,3.5,1.3,0.2,setosa
4.9,3.1,1.5,0.1,setosa
4.4,3.0,1.3,0.2,setosa
5.1,3.4,1.5,0.2,setosa
5.0,3.5,1.3,0.3,setosa
4.5,2.3,1.3,0.3,setosa
4.4,3.2,1.3,0.2,setosa
5.0,3.5,1.6,0.6,setosa
5.1,3.8,1.9,0.4,setosa
4.8,3.0,1.4,0.3,setosa
5.1,3.8,1.6,0.2,setosa
4.6,3.2,1.4,0.2,setosa
5.3,3.7,1.5,0.2,setosa
5.0,3.3,1.4,0.2,setosa
7.0,3.2,4.7,1.4,versicolor
6.4,3.2,4.5,1.5,versicolor
6.9,3.1,4.9,1.5,versicolor
5.5,2.3,4.0,1.3,versicolor
6.5,2.8,4.6,1.5,versicolor
5.7,2.8,4.5,1.3,versicolor
6.3,3.3,4.7,1.6,versicolor
4.9,2.4,3.3,1.0,versicolor
6.6,2.9,4.6,1.3,versicolor
5.2,2.7,3.9,1.4,versicolor
5.0,2.0,3.5,1.0,versicolor
5.9,3.0,4.2,1.5,versicolor
6.0,2.2,4.0,1.0,versicolor
6.1,2.9,4.7,1.4,versicolor
5.6,2.9,3.6,1.3,versicolor
6.7,3.1,4.4,1.4,versicolor
5.6,3.0,4.5,1.5,versicolor
5.8,2.7,4.1,1.0,versicolor
6.2,2.2,4.5,1.5,versicolor
5.6,2.5,3.9,1.1,versicolor
5.9,3.2,4.8,1.8,versicolor
6.1,2.8,4.0,1.3,versicolor
6.3,2.5,4.9,1.5,versicolor
6.1,2.8,4.7,1.2,versicolor
6.4,2.9,4.3,1.3,versicolor
6.6,3.0,4.4,1.4,versicolor
6.8,2.8,4.8,1.4,versicolor
6.7,3.0,5.0,1.7,versicolor
6.0,2.9,4.5,1.5,versicolor
5.7,2.6,3.5,1.0,versicolor
5.5,2.4,3.8,1.1,versicolor
5.5,2.4,3.7,1.0,versicolor
5.8,2.7,3.9,1.2,versicolor
6.0,2.7,5.1,1.6,versicolor
5.4,3.0,4.5,1.5,versicolor
6.0,3.4,4.5,1.6,versicolor
6.7,3.1,4.7,1.5,versicolor
6.3,2.3,4.4,1.3,versicolor
5.6,3.0,4.1,1.3,versicolor
5.5,2.5,4.0,1.3,versicolor
5.5,2.6,4.4,1.2,versicolor
6.1,3.0,4.6,1.4,versicolor
5.8,2.6,4.0,1.2,versicolor
5.0,2.3,3.3,1.0,versicolor
5.6,2.7,4.2,1.3,versicolor
5.7,3.0,4.2,1.2,versicolor
5.7,2.9,4.2,1.3,versicolor
6.2,2.9,4.3,1.3,versicolor
5.1,2.5,3.0,1.1,versicolor
5.7,2.8,4.1,1.3,versicolor
6.3,3.3,6.0,2.5,virginica
5.8,2.7,5.1,1.9,virginica
7.1,3.0,5.9,2.1,virginica
6.3,2.9,5.6,1.8,virginica
6.5,3.0,5.8,2.2,virginica
7.6,3.0,6.6,2.1,virginica
4.9,2.5,4.5,1.7,virginica
7.3,2.9,6.3,1.8,virginica
6.7,2.5,5.8,1.8,virginica
7.2,3.6,6.1,2.5,virginica
6.5,3.2,5.1,2.0,virginica
6.4,2.7,5.3,1.9,virginica
6.8,3.0,5.5,2.1,virginica
5.7,2.5,5.0,2.0,virginica
5.8,2.8,5.1,2.4,virginica
6.4,3.2,5.3,2.3,virginica
6.5,3.0,5.5,1.8,virginica
7.7,3.8,6.7,2.2,virginica
7.7,2.6,6.9,2.3,virginica
6.0,2.2,5.0,1.5,virginica
6.9,3.2,5.7,2.3,virginica
5.6,2.8,4.9,2.0,virginica
7.7,2.8,6.7,2.0,virginica
6.3,2.7,4.9,1.8,virginica
6.7,3.3,5.7,2.1,virginica
7.2,3.2,6.0,1.8,virginica
6.2,2.8,4.8,1.8,virginica
6.1,3.0,4.9,1.8,virginica
6.4,2.8,5.6,2.1,virginica
7.2,3.0,5.8,1.6,virginica
7.4,2.8,6.1,1.9,virginica
7.9,3.8,6.4,2.0,virginica
6.4,2.8,5.6,2.2,virginica
6.3,2.8,5.1,1.5,virginica
6.1,2.6,5.6,1.4,virginica
7.7,3.0,6.1,2.3,virginica
6.3,3.4,5.6,2.4,virginica
6.4,3.1,5.5,1.8,virginica
6.0,3.0,4.8,1.8,virginica
6.9,3.1,5.4,2.1,virginica
6.7,3.1,5.6,2.4,virginica
6.9,3.1,5.1,2.3,virginica
5.8,2.7,5.1,1.9,virginica
6.8,3.2,5.9,2.3,virginica
6.7,3.3,5.7,2.5,virginica
6.7,3.0,5.2,2.3,virginica
6.3,2.5,5.0,1.9,virginica
6.5,3.0,5.2,2.0,virginica
6.2,3.4,5.4,2.3,virginica
5.9,3.0,5.1,1.8,virginica
function ScatterPlot(){
var outerWidth = 100;
var outerHeight = 100;
var margin = { left: 60, top: 5, right: 10, bottom: 60 };
var rMin = 2; // "r" stands for radius
var rMax = 300;
var xColumn = "No X column configured";
var yColumn = "No Y column configured";
var rColumn = "No radius column configured";
var colorColumn = "No color column configured";
var colorRange = d3.scale.category10().range();
var xAxisLabel = "No X axis label configured";
var xAxisLabelOffset = 40;
var yAxisLabel = "No Y axis label configured";
var yAxisLabelOffset = 30;
var xScale = d3.scale.linear();
var yScale = d3.scale.linear();
var rScale = d3.scale.linear();
var colorScale = d3.scale.ordinal();
var xAxis = d3.svg.axis().scale(xScale).orient("bottom")
.tickFormat(d3.format("s"))
.outerTickSize(0);
var xTicks = 5;
var yAxis = d3.svg.axis().scale(yScale).orient("left")
.tickFormat(d3.format("s"))
.outerTickSize(0);
var yTicks = 5;
function chart(selection){
var innerWidth = outerWidth - margin.left - margin.right;
var innerHeight = outerHeight - margin.top - margin.bottom;
colorScale.range(colorRange);
xScale.range([0, innerWidth]);
yScale.range([innerHeight, 0]);
rScale.range([rMin, rMax]);
xAxis.ticks(xTicks);
yAxis.ticks(yTicks);
selection.each(function (data) {
var svg = d3.select(this).selectAll("svg").data([data]);
var gEnter = svg.enter().append("svg").append("g");
var g = svg.select("g");
var circles = g.selectAll("circle").data(data);
gEnter
.append("g")
.attr("class", "x axis")
.append("text")
.attr("class", "label")
.style("text-anchor", "middle");
gEnter
.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "label")
.style("text-anchor", "middle");
xScale.domain(d3.extent(data, function (d){ return d[xColumn]; }));
yScale.domain(d3.extent(data, function (d){ return d[yColumn]; }));
rScale.domain(d3.extent(data, function (d){ return d[rColumn]; }));
svg
.attr("width", outerWidth)
.attr("height", outerHeight);
g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g
.select(".x.axis")
.attr("transform", "translate(0," + innerHeight + ")")
.call(xAxis)
.select("text")
.attr("x", innerWidth / 2)
.attr("y", xAxisLabelOffset)
.text(xAxisLabel);
g
.select(".y.axis")
.call(yAxis)
.select("text")
.attr("transform", "translate(-" + yAxisLabelOffset + "," + (innerHeight / 2) + ") rotate(-90)")
.text(yAxisLabel);
circles.enter().append("circle");
circles
.attr("cx", function (d){ return xScale(d[xColumn]); })
.attr("cy", function (d){ return yScale(d[yColumn]); })
.attr("r", function (d){ return rScale(d[rColumn]); })
.attr("fill", function (d){ return colorScale(d[colorColumn]); });
circles.exit().remove();
});
}
chart.outerWidth = function(_) {
if (!arguments.length) return outerWidth;
outerWidth = _;
return chart;
};
chart.outerHeight = function(_) {
if (!arguments.length) return outerHeight;
outerHeight = _;
return chart;
};
chart.xColumn = function(_) {
if (!arguments.length) return xColumn;
xColumn = _;
return chart;
};
chart.yColumn = function(_) {
if (!arguments.length) return yColumn;
yColumn = _;
return chart;
};
chart.rColumn = function(_) {
if (!arguments.length) return rColumn;
rColumn = _;
return chart;
};
chart.rMin = function(_) {
if (!arguments.length) return rMin;
rMin = _;
return chart;
};
chart.rMax = function(_) {
if (!arguments.length) return rMax;
rMax = _;
return chart;
};
chart.colorRange = function(_) {
if (!arguments.length) return colorRange;
colorRange = _;
return chart;
};
chart.colorColumn = function(_) {
if (!arguments.length) return colorColumn;
colorColumn = _;
return chart;
};
chart.xAxisLabel = function(_) {
if (!arguments.length) return xAxisLabel;
xAxisLabel = _;
return chart;
};
chart.yAxisLabel = function(_) {
if (!arguments.length) return yAxisLabel;
yAxisLabel = _;
return chart;
};
chart.xAxisLabelOffset = function(_) {
if (!arguments.length) return xAxisLabelOffset;
xAxisLabelOffset = _;
return chart;
};
chart.yAxisLabelOffset = function(_) {
if (!arguments.length) return yAxisLabelOffset;
yAxisLabelOffset = _;
return chart;
};
chart.xTicks = function(_) {
if (!arguments.length) return xTicks;
xTicks = _;
return chart;
};
chart.yTicks = function(_) {
if (!arguments.length) return yTicks;
yTicks = _;
return chart;
};
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
return chart;
}