An example of a stylized scatter plot with re-usable code organization using D3 v4.
<!DOCTYPE html>
<html>
<head>
<title>Solution</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
margin: 0px;
}
.axis .tick line {
stroke-width: 2px;
stroke: #dddddd;
}
.axis .tick text {
font-size: 30px;
fill: #8E8883;
}
.axis .domain {
display: none;
}
.axis__label {
text-anchor: middle;
font-size: 50px;
fill: #635F5D;
}
.legend .tick text {
font-size: 30px;
fill: #8E8883;
font-family: sans-serif;
alignment-baseline: middle;
}
.legend__label {
font-size: 45px;
fill: #635F5D;
font-family: sans-serif;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
const xValue = d => d.sepalLength;
const yValue = d => d.petalLength;
const colorValue = d => d.species;
const xLabel = "Sepal Length";
const yLabel = "Petal Length";
const margin = {top: 30, right: 300, bottom: 100, left: 100};
const svg = d3.select("svg");
const width = +svg.attr("width");
const height = +svg.attr("height");
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const xScale = d3.scaleLinear().range([0, innerWidth]);
const yScale = d3.scaleLinear().range([innerHeight, 0]);
const colorScale = d3.scaleOrdinal()
.range(["#eb8e37", "#1ac6cf", "#e35dd4"]);
const xAxis = d3.axisBottom()
.scale(xScale)
.tickSizeInner(-innerHeight)
.tickPadding(15);
const yAxis = d3.axisLeft()
.scale(yScale)
.tickSizeInner(-innerWidth)
.ticks(5)
.tickPadding(10);
const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
const xAxisG = g.append("g")
.attr("class", "axis")
.attr("transform", `translate(0, ${innerHeight})`);
const yAxisG = g.append("g")
.attr("class", "axis");
xAxisG
.append("text")
.attr("class", "axis__label")
.attr("x", innerWidth / 2)
.attr("y", 85)
.text(xLabel);
yAxisG
.append("text")
.attr("class", "axis__label")
.attr("transform", "rotate(-90)")
.attr("x", -innerHeight / 2)
.attr("y", -45)
.text(yLabel);
function colorLegend(selection, props){
const colorScale = props.colorScale;
const positionX = props.positionX;
const positionY = props.positionY;
const tickRadius = props.tickRadius;
const tickSpacing = props.tickSpacing;
const tickPadding = props.tickPadding;
const label = props.label;
const labelX = props.labelX;
const labelY = props.labelY;
let legendG = selection
.selectAll(".legend--color")
.data([null]);
legendG = legendG
.enter().append("g")
.attr("class", "legend legend--color")
.merge(legendG)
.attr("transform", `translate(${positionX}, ${positionY})`);
const legendLabel = legendG
.selectAll(".legend__label")
.data([null]);
legendLabel
.enter().append("text")
.attr("class", "legend__label")
.merge(legendLabel)
.attr("x", labelX)
.attr("y", labelY)
.text(label);
const ticks = legendG
.selectAll(".tick")
.data(colorScale.domain());
const ticksEnter = ticks
.enter().append("g")
.attr("class", "tick");
ticksEnter
.merge(ticks)
.attr("transform", (d, i) => `translate(0, ${i * tickSpacing})`);
ticks.exit().remove();
ticksEnter
.append("circle")
.merge(ticks.select("circle"))
.attr("r", tickRadius)
.attr("fill", colorScale);
ticksEnter
.append("text")
.merge(ticks.select("text"))
.attr("x", tickRadius + tickPadding)
.text(d => d);
}
function render(data){
xScale
.domain(d3.extent(data, xValue))
.nice();
yScale
.domain(d3.extent(data, yValue))
.nice();
colorScale
.domain(d3.set(data.map(colorValue)).values().sort());
xAxisG.call(xAxis);
yAxisG.call(yAxis);
const circles = g.selectAll("circle").data(data);
circles.exit().remove();
circles
.enter().append("circle")
.attr("r", 5)
.merge(circles)
.attr("cx", d => xScale(xValue(d)))
.attr("cy", d => yScale(yValue(d)))
.attr("fill", d => colorScale(colorValue(d)));
svg.call(colorLegend, {
colorScale: colorScale,
positionX: 750,
positionY: 200,
tickRadius: 12,
tickSpacing: 35,
tickPadding: 6,
label: "Species",
labelX: -20,
labelY: -30
});
}
function type(d){
d.sepalLength = +d.sepalLength;
d.sepalWidth = +d.sepalWidth;
d.petalLength = +d.petalLength;
d.petalWidth = +d.petalWidth;
return d;
}
d3.csv("iris.csv", type, render);
</script>
</body>
</html>
sepalLength,sepalWidth,petalLength,petalWidth,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