Skip to content

Instantly share code, notes, and snippets.

@curran
Last active September 11, 2015 02:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save curran/4d3f0599577f5ab64aad to your computer and use it in GitHub Desktop.
Save curran/4d3f0599577f5ab64aad to your computer and use it in GitHub Desktop.
Model.js Dynamic Scatter Plot

An example model-driven scatter plot using model.js. Draws from the D3 scatter plot example.

Curran Kelleher 4/17/2014

Updated DOM creation pattern and posted to bl.ocks.org 9/10/2015.

{
"xField": "sepalWidth",
"yField": "sepalLength",
"xLabel": "Sepal Width (cm)",
"yLabel": "Sepal Width (cm)",
"margin": {
"top": 20,
"right": 20,
"bottom": 30,
"left": 40
}
}
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.2 setosa
5.0 3.2 1.2 0.2 setosa
5.5 3.5 1.3 0.2 setosa
4.9 3.6 1.4 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
<!--
A reactive scatter plot using model.js.
Curran Kelleher 4/17/2014
Updated with new CDN link and posted to bl.ocks.org 9/10/2015.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="http://curran.github.io/model/cdn/model-v0.2.4.js"></script>
<script src="scatterPlot.js"></script>
</head>
<body>
<div id="scatterPlotContainer"></div>
<script src="main.js"></script>
</body>
</html>
// The main program that runs the scatter plot.
// It periodically updates various aspects of the chart
// with random values, to illustrate that the chart
// is dynamic and reacts to changes in its model.
//
// Curran Kelleher 4/17/2014
// Revived 9/10/2015
var div = document.getElementById("scatterPlotContainer"),
scatterPlot = ScatterPlot();
div.appendChild(scatterPlot.el);
d3.json("configuration.json", function (config) {
scatterPlot.set(config);
});
d3.tsv("data.tsv", function (d) {
d.sepalLength = +d.sepalLength;
d.sepalWidth = +d.sepalWidth;
return d;
}, function(error, data) {
// Set size once to initialize
setSizeFromDiv();
// Set size on resize
window.addEventListener("resize", setSizeFromDiv);
// Set the data
scatterPlot.data = data;
// Reset data each second
setInterval(function () {
// Include each element with a 10% chance.
var randomSample = data.filter(function(d){
return Math.random() < 0.1;
});
scatterPlot.data = randomSample;
}, 1000);
// Randomly change the margin every 1.7 seconds.
function random(){ return Math.random() * 100; }
setInterval(function () {
scatterPlot.margin = {
top: random(),
right: random(),
bottom: random(),
left: random()
};
}, 1700);
// Change the Y axis label every 600 ms.
function randomString() {
var possibilities = ["Frequency", "Population", "Alpha", "Beta"],
i = Math.round(Math.random() * possibilities.length);
return possibilities[i];
}
setInterval(function () {
scatterPlot.yLabel = randomString();
}, 600);
});
function setSizeFromDiv(){
scatterPlot.size = {
width: div.clientWidth,
height: div.clientHeight
};
}
// An adaptation of the [D3 scatter plot example](http://bl.ocks.org/mbostock/3887118)
// that uses `model.js`. This version, unlike the original example,
// is model driven and reactive. When a part of the model updates,
// only the parts of the visualization that depend on those parts
// of the model are updated. There are no redundant calls to visualization
// update code when multiple properties are changed simultaneously.
function ScatterPlot(){
var x = d3.scale.linear(),
y = d3.scale.linear(),
xAxis = d3.svg.axis().scale(x).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left"),
div = document.createElement("div"),
svg = d3.select(div).append("svg"),
g = svg.append("g"),
xAxisG = g.append("g").attr("class", "x axis"),
yAxisG = g.append("g").attr("class", "y axis"),
xAxisLabel = yAxisG.append("text")
.attr("class", "label")
.attr("y", -6)
.style("text-anchor", "end"),
yAxisLabel = yAxisG.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end"),
model = new Model();
model.el = div;
model.when("xLabel", xAxisLabel.text, yAxisLabel);
model.when("yLabel", yAxisLabel.text, yAxisLabel);
model.when("margin", function (margin) {
g.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
});
model.when("size", function (size) {
svg.attr("width", size.width)
.attr("height", size.height);
});
model.when(["size", "margin"], function (size, margin) {
model.width = size.width - margin.left - margin.right;
model.height = size.height - margin.top - margin.bottom;
});
model.when("width", function (width) {
xAxisLabel.attr("x", width);
});
model.when("height", function (height) {
xAxisG.attr("transform", "translate(0," + height + ")");
});
model.when(["width", "height", "data", "xField", "yField"], function (width, height, data, xField, yField) {
var dots;
x.domain(d3.extent(data, function(d) { return d[xField]; })).nice();
y.domain(d3.extent(data, function(d) { return d[yField]; })).nice();
x.range([0, width]);
y.range([height, 0]);
xAxisG.call(xAxis);
yAxisG.call(yAxis);
dots = g.selectAll(".dot").data(data);
dots.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5);
dots
.attr("cx", function(d) { return x(d[xField]); })
.attr("cy", function(d) { return y(d[yField]); });
dots.exit().remove();
});
return model;
}
/* Size the visualization container. */
#scatterPlotContainer {
position: fixed;
top: 30px;
bottom: 30px;
left: 30px;
right: 30px;
}
/* Style the visualization.
* This CSS is copied verbatim from the
* D3 scatter plot example found at
* http://bl.ocks.org/mbostock/3887118 */
body {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment