Skip to content

Instantly share code, notes, and snippets.

@starcalibre
Last active February 12, 2018 08:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save starcalibre/f4b8bb0da3b2090c56d79646a338fd81 to your computer and use it in GitHub Desktop.
Save starcalibre/f4b8bb0da3b2090c56d79646a338fd81 to your computer and use it in GitHub Desktop.
Interactive scatterplot with symbols
.idea/
*.iml

The Iris dataset is perhaps the most famous data-set of them all! Iris describes three different categories of flowers using four parameters.

In this visualisation, the four dimensional space has been reduced to two dimensions using Principal Components Analysis.

Each category of point is represented using a different type of symbol using the d3-shape library. The plot points are drawn using the Canvas API, and the plot is fully interactive allowing for panning and zooming.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Scatterplot With Shapes</title>
<style>
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.tick line{
opacity: 0.2;
}
text {
font: 10px sans-serif;
}
#container {
float: left;
}
#legend {
float: left;
}
</style>
</head>
<body>
<div>
<div id="container"></div>
<div id="legend">
</div>
</div>
</body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/d3-path.v0.1.min.js"></script>
<script src="//d3js.org/d3-shape.v0.6.min.js"></script>
<script>
var margin = {top: 10, left: 30, bottom: 20, right: 10};
var width = 600 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;
var numberOfPoints = 100;
var pointRadius = 9;
d3.json('./iris_pca.json', function(data) {
var labels = d3.set(data.map(function(d) {
return d.label;
})).values();
var xExtent = d3.extent(data, function(d) { return d.x });
var yExtent = d3.extent(data, function(d) { return d.y });
var xRange = xExtent[1] - xExtent[0];
var yRange = yExtent[1] - yExtent[0];
var xScale = d3.scale.linear()
.domain([xExtent[0] - xRange*0.1, xExtent[1] + xRange*0.1])
.range([0, width]);
var yScale = d3.scale.linear()
.domain([yExtent[0] - yRange*0.1, yExtent[1] + yRange*0.1])
.range([height, 0]);
var colourScale = d3.scale.ordinal()
.domain(labels)
.range(['#e41a1c', '#377eb8', '#4daf4a']);
var shapeScale = d3.scale.ordinal()
.domain(labels)
.range([d3_shape.symbolCircle, d3_shape.symbolCross,
d3_shape.symbolSquare]);
var svg = d3.select('#container').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.style('position', 'absolute')
.style('z-index', 1)
.append('g')
.attr("transform", "translate(" + margin.left + "," +
margin.top + ")");
var canvas = d3.select('#container').append('canvas')
.attr('width', width - 1)
.attr('height', height - 1)
.style('position', 'absolute')
.style('z-index', 2)
.style("transform", "translate(" + (margin.left + 1) +
"px" + "," + (margin.top + 1) + "px" + ")");
var context = canvas.node().getContext('2d');
d3.select("#container")
.style("width", width + margin.left + margin.right + 'px')
.style("height", height + margin.top + margin.bottom + "px");
var xAxis = d3.svg.axis()
.scale(xScale)
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(yScale)
.innerTickSize(-width)
.outerTickSize(0)
.orient('left');
var xAxisSvg = svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
var yAxisSvg = svg.append('g')
.attr('class', 'axis')
.call(yAxis);
// create zooming/panning behaviour
var zoomBehaviour = d3.behavior.zoom()
.x(xScale)
.y(yScale)
.scaleExtent([1, 5])
.on('zoom', onZoom);
canvas.call(zoomBehaviour);
// add legend
var legendWidth = 100;
var legendHeight = 90;
var legend = d3.select('#legend').append('svg')
.attr('width', legendWidth)
.attr('height', legendHeight);
legend.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', legendWidth)
.attr('height', legendHeight)
.attr('stroke', 'black')
.attr('fill', 'white');
labels.forEach(function(d, i) {
var x = pointRadius + 10;
var y = 23 + i * 20;
var symbol = d3_shape.symbol()
.type(shapeScale(d))
.size(pointRadius * pointRadius);
legend.append('path')
.attr('d', symbol)
.attr('fill', colourScale(d))
.attr('stroke', 'black')
.attr('stroke-width', 1)
.attr('transform', 'translate(' + x + ',' + y + ')');
legend.append('text')
.attr('class', 'legend')
.attr('x', pointRadius + 20)
.attr('y', y)
.attr('dominant-baseline', 'central')
.text(d);
});
draw();
// draw points
function draw() {
console.log('draw');
context.clearRect(0, 0, width, height);
data.forEach(function(d) {
var x = Math.round(xScale(d.x));
var y = Math.round(yScale(d.y));
var symbol = d3_shape.symbol()
.type(shapeScale(d.label))
.size(pointRadius * pointRadius)
.context(context);
context.translate(x, y);
context.fillStyle = colourScale(d.label);
context.beginPath();
symbol();
context.closePath();
context.fill();
context.stroke();
context.translate(-x, -y);
});
}
function onClick(e) {
console.log('click!!');
}
function onZoom() {
console.log('onZoom');
draw();
xAxisSvg.call(xAxis);
yAxisSvg.call(yAxis);
}
});
</script>
</html>
[{"x":-2.6842071251,"y":-0.3266073148,"label":"setosa"},{"x":-2.7153906156,"y":0.1695568476,"label":"setosa"},{"x":-2.8898195396,"y":0.1373456096,"label":"setosa"},{"x":-2.7464371973,"y":0.3111243158,"label":"setosa"},{"x":-2.7285929818,"y":-0.3339245636,"label":"setosa"},{"x":-2.279897361,"y":-0.7477827132,"label":"setosa"},{"x":-2.8208906822,"y":0.082104511,"label":"setosa"},{"x":-2.6264819933,"y":-0.170405349,"label":"setosa"},{"x":-2.8879585653,"y":0.5707980263,"label":"setosa"},{"x":-2.6738446867,"y":0.1066917038,"label":"setosa"},{"x":-2.5065267893,"y":-0.6519350137,"label":"setosa"},{"x":-2.6131427183,"y":-0.021520632,"label":"setosa"},{"x":-2.787433976,"y":0.2277401889,"label":"setosa"},{"x":-3.2252004463,"y":0.5032799095,"label":"setosa"},{"x":-2.6435432169,"y":-1.1861948994,"label":"setosa"},{"x":-2.3838693238,"y":-1.3447543446,"label":"setosa"},{"x":-2.6225262031,"y":-0.8180896746,"label":"setosa"},{"x":-2.6483227325,"y":-0.3191366678,"label":"setosa"},{"x":-2.1990779614,"y":-0.8792440881,"label":"setosa"},{"x":-2.5873461889,"y":-0.5204736388,"label":"setosa"},{"x":-2.3105317013,"y":-0.3978678216,"label":"setosa"},{"x":-2.5432349073,"y":-0.4400317547,"label":"setosa"},{"x":-3.2158576949,"y":-0.1416155716,"label":"setosa"},{"x":-2.3031285377,"y":-0.1055226784,"label":"setosa"},{"x":-2.3561710867,"y":0.0312095891,"label":"setosa"},{"x":-2.5079172268,"y":0.1390563399,"label":"setosa"},{"x":-2.4690559975,"y":-0.1378873146,"label":"setosa"},{"x":-2.5623909468,"y":-0.3746845628,"label":"setosa"},{"x":-2.6398212684,"y":-0.319290066,"label":"setosa"},{"x":-2.632847908,"y":0.1900758306,"label":"setosa"},{"x":-2.5884620513,"y":0.1973930794,"label":"setosa"},{"x":-2.4100773371,"y":-0.4180800082,"label":"setosa"},{"x":-2.6476366734,"y":-0.8199826326,"label":"setosa"},{"x":-2.5971594771,"y":-1.100021928,"label":"setosa"},{"x":-2.6738446867,"y":0.1066917038,"label":"setosa"},{"x":-2.8669998469,"y":-0.0771930957,"label":"setosa"},{"x":-2.6252284647,"y":-0.6068000084,"label":"setosa"},{"x":-2.6738446867,"y":0.1066917038,"label":"setosa"},{"x":-2.9818426649,"y":0.4802500489,"label":"setosa"},{"x":-2.5903230256,"y":-0.2360593373,"label":"setosa"},{"x":-2.7701389107,"y":-0.2710594198,"label":"setosa"},{"x":-2.8522110816,"y":0.9328653675,"label":"setosa"},{"x":-2.9982964428,"y":0.3343075746,"label":"setosa"},{"x":-2.4055141013,"y":-0.1959172577,"label":"setosa"},{"x":-2.2088329542,"y":-0.4426960304,"label":"setosa"},{"x":-2.7156651907,"y":0.2426814829,"label":"setosa"},{"x":-2.537573371,"y":-0.5103675455,"label":"setosa"},{"x":-2.8403212968,"y":0.2205763383,"label":"setosa"},{"x":-2.5426857571,"y":-0.5862810253,"label":"setosa"},{"x":-2.7039123149,"y":-0.1150108522,"label":"setosa"},{"x":1.2847945878,"y":-0.6854391861,"label":"versicolor"},{"x":0.932410753,"y":-0.3191980898,"label":"versicolor"},{"x":1.4640613228,"y":-0.504189833,"label":"versicolor"},{"x":0.1809672063,"y":0.8256039436,"label":"versicolor"},{"x":1.0871344872,"y":-0.0753903893,"label":"versicolor"},{"x":0.6404367495,"y":0.417323483,"label":"versicolor"},{"x":1.0952237099,"y":-0.2838912109,"label":"versicolor"},{"x":-0.7514671406,"y":1.001107513,"label":"versicolor"},{"x":1.0432977807,"y":-0.2289569088,"label":"versicolor"},{"x":-0.0101900707,"y":0.7205748667,"label":"versicolor"},{"x":-0.5110861959,"y":1.2624919539,"label":"versicolor"},{"x":0.5110980607,"y":0.102284105,"label":"versicolor"},{"x":0.2623357562,"y":0.547893298,"label":"versicolor"},{"x":0.9840445452,"y":0.1243604202,"label":"versicolor"},{"x":-0.174864002,"y":0.2518155711,"label":"versicolor"},{"x":0.927572942,"y":-0.468236205,"label":"versicolor"},{"x":0.6595927891,"y":0.3519762911,"label":"versicolor"},{"x":0.2345405863,"y":0.3319218294,"label":"versicolor"},{"x":0.9423617074,"y":0.5418222582,"label":"versicolor"},{"x":0.0432464003,"y":0.5814894466,"label":"versicolor"},{"x":1.1162407238,"y":0.0842140139,"label":"versicolor"},{"x":0.3567865678,"y":0.0668238279,"label":"versicolor"},{"x":1.2964688503,"y":0.3275615198,"label":"versicolor"},{"x":0.9205026489,"y":0.1823903633,"label":"versicolor"},{"x":0.7140082136,"y":-0.1503791531,"label":"versicolor"},{"x":0.8996408633,"y":-0.3296109796,"label":"versicolor"},{"x":1.3310414189,"y":-0.2446695206,"label":"versicolor"},{"x":1.5573962721,"y":-0.2673925848,"label":"versicolor"},{"x":0.812455549,"y":0.1623315749,"label":"versicolor"},{"x":-0.3073347557,"y":0.3650866128,"label":"versicolor"},{"x":-0.0703428889,"y":0.7025379317,"label":"versicolor"},{"x":-0.1918844921,"y":0.6774905444,"label":"versicolor"},{"x":0.1349949505,"y":0.3117096427,"label":"versicolor"},{"x":1.3787369828,"y":0.4212051382,"label":"versicolor"},{"x":0.5872748536,"y":0.4832842677,"label":"versicolor"},{"x":0.8072054967,"y":-0.1950539638,"label":"versicolor"},{"x":1.2204289662,"y":-0.408035337,"label":"versicolor"},{"x":0.8128677904,"y":0.3706789983,"label":"versicolor"},{"x":0.2451951617,"y":0.2667280357,"label":"versicolor"},{"x":0.1645134284,"y":0.6796614693,"label":"versicolor"},{"x":0.4630309889,"y":0.6695265465,"label":"versicolor"},{"x":0.8901604457,"y":0.0338124427,"label":"versicolor"},{"x":0.22887905,"y":0.4022576202,"label":"versicolor"},{"x":-0.7070812839,"y":1.0084247618,"label":"versicolor"},{"x":0.3555330392,"y":0.5032184874,"label":"versicolor"},{"x":0.3311269473,"y":0.2111801407,"label":"versicolor"},{"x":0.3752382289,"y":0.2916220248,"label":"versicolor"},{"x":0.6416902782,"y":-0.0190711765,"label":"versicolor"},{"x":-0.9084633331,"y":0.7515687252,"label":"versicolor"},{"x":0.2978079074,"y":0.3470165216,"label":"versicolor"},{"x":2.5317269804,"y":0.0118422366,"label":"virginica"},{"x":1.4140722252,"y":0.5749250559,"label":"virginica"},{"x":2.6164846083,"y":-0.341935287,"label":"virginica"},{"x":1.9708149459,"y":0.1811256947,"label":"virginica"},{"x":2.349757984,"y":0.0418825497,"label":"virginica"},{"x":3.3968799207,"y":-0.5471680462,"label":"virginica"},{"x":0.5193832451,"y":1.1913516891,"label":"virginica"},{"x":2.932005097,"y":-0.3523770062,"label":"virginica"},{"x":2.3196727939,"y":0.2455481706,"label":"virginica"},{"x":2.9181342336,"y":-0.7803806294,"label":"virginica"},{"x":1.661934947,"y":-0.242038401,"label":"virginica"},{"x":1.8023404527,"y":0.2161546066,"label":"virginica"},{"x":2.165378863,"y":-0.2152802834,"label":"virginica"},{"x":1.3445942175,"y":0.7764154252,"label":"virginica"},{"x":1.5852672993,"y":0.5393070538,"label":"virginica"},{"x":1.9047435782,"y":-0.118818991,"label":"virginica"},{"x":1.9492487819,"y":-0.0407302594,"label":"virginica"},{"x":3.4887653797,"y":-1.1715445443,"label":"virginica"},{"x":3.7946868612,"y":-0.253265571,"label":"virginica"},{"x":1.2983298246,"y":0.7610139365,"label":"virginica"},{"x":2.428167259,"y":-0.3767819713,"label":"virginica"},{"x":1.1980973723,"y":0.6055789618,"label":"virginica"},{"x":3.4992654843,"y":-0.456773467,"label":"virginica"},{"x":1.3876682502,"y":0.2040309866,"label":"virginica"},{"x":2.2758536493,"y":-0.3333865258,"label":"virginica"},{"x":2.6141938307,"y":-0.5583669503,"label":"virginica"},{"x":1.2576251829,"y":0.1791369974,"label":"virginica"},{"x":1.2906696477,"y":0.1164252518,"label":"virginica"},{"x":2.1228539805,"y":0.2108548845,"label":"virginica"},{"x":2.3875644024,"y":-0.4625192507,"label":"virginica"},{"x":2.8409609253,"y":-0.372742591,"label":"virginica"},{"x":3.2323428983,"y":-1.370524036,"label":"virginica"},{"x":2.1587383731,"y":0.2183255316,"label":"virginica"},{"x":1.4431026044,"y":0.1438012891,"label":"virginica"},{"x":1.7796401069,"y":0.5014647947,"label":"virginica"},{"x":3.076521621,"y":-0.6857644422,"label":"virginica"},{"x":2.1449868567,"y":-0.1389066089,"label":"virginica"},{"x":1.9048629251,"y":-0.0480475082,"label":"virginica"},{"x":1.1688534695,"y":0.1645024998,"label":"virginica"},{"x":2.1076537312,"y":-0.3714822492,"label":"virginica"},{"x":2.3143033946,"y":-0.1826088508,"label":"virginica"},{"x":1.9224508848,"y":-0.4092711762,"label":"virginica"},{"x":1.4140722252,"y":0.5749250559,"label":"virginica"},{"x":2.5633227123,"y":-0.2759745022,"label":"virginica"},{"x":2.4193912198,"y":-0.3035039377,"label":"virginica"},{"x":1.9440170489,"y":-0.187415222,"label":"virginica"},{"x":1.5256636313,"y":0.3750208482,"label":"virginica"},{"x":1.7640459355,"y":-0.0785191864,"label":"virginica"},{"x":1.9016290753,"y":-0.1158767482,"label":"virginica"},{"x":1.3896661333,"y":0.2828867092,"label":"virginica"}]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment