Skip to content

Instantly share code, notes, and snippets.

@armollica
Last active February 22, 2016 02:58
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 armollica/a6b26f0f0abad5505e08 to your computer and use it in GitHub Desktop.
Save armollica/a6b26f0f0abad5505e08 to your computer and use it in GitHub Desktop.
2D/3D Scatterplot
height: 600

Scrolling moves the z-axis. Points at the current z-position will be more visible than those far away from this z-position.

I was curious whether this kind of thing would be useful for exploring the correlation of three continuous variables at once. I don't think it works all that well. Then again I haven't tried it out on real data yet so who knows.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style>
.axis {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
stroke-width: 1px;
}
</style>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var margin = { top: 10, left: 170, bottom: 200, right: 300 },
width = 960 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var scale = {
x: d3.scale.linear().range([0, width]).nice(),
y: d3.scale.linear().range([height, 0]).nice(),
z: d3.scale.linear().range([0, width/2]).nice(),
fill: d3.scale.sqrt().range(["#fff", "#000", "#fff"])
.interpolate(d3.interpolateLab),
opacity: d3.scale.sqrt().range([0.1, 1, 0.1]),
r: d3.scale.sqrt().range([1, 5, 1])
};
var axis = {
x: d3.svg.axis().scale(scale.x).orient("bottom").ticks(5),
y: d3.svg.axis().scale(scale.y).orient("right").ticks(5),
z: d3.svg.axis().scale(scale.z).orient("bottom").ticks(5)
};
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var eventRect = d3.select("svg").append("rect")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("opacity", 0);
var data = createData();
var zExtent = d3.extent(data, function(d) { return d.z; }),
zDifferenceExtent = [
zExtent[0] - zExtent[1],
0,
zExtent[1] - zExtent[0]
],
currentZ = 0;
scale.x.domain(d3.extent(data, function(d) { return d.x; }));
scale.y.domain(d3.extent(data, function(d) { return d.y; }));
scale.z.domain(zExtent);
scale.fill.domain(zDifferenceExtent);
scale.opacity.domain(zDifferenceExtent);
scale.r.domain(zDifferenceExtent);
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(axis.x);
svg.append("g").attr("class", "y axis")
.attr("transform", "translate(" + width + ",0)")
.call(axis.y);
svg.append("g").attr("class", "z axis")
.attr("transform",
"translate(" + (width - scale.z(currentZ)) + "," + (height + scale.z(currentZ)) + ") " +
"skewY(-45)"
)
.call(axis.z)
.selectAll("text")
.attr("transform", "skewY(45)"); // un-skew text to make legible
svg.call(renderPoints, data, currentZ);
eventRect.on("wheel", function() {
event.preventDefault();
var scrolled = d3.event.deltaY > 0 ? "down" : "up";
currentZ += 0.1*(scrolled === "up" ? 1 : -1);
currentZ = currentZ > zExtent[1] ? zExtent[1] :
currentZ < zExtent[0] ? zExtent[0] :
currentZ;
svg.call(renderPoints, data, currentZ);
svg.select(".z.axis")
.attr("transform",
"translate(" + (width - scale.z(currentZ)) + "," + (height + scale.z(currentZ)) + ") " +
"skewY(-45)"
)
});
function renderPoints(selection, data, focusedZ) {
var points = selection.selectAll(".point").data(data);
points.enter().append("circle")
.attr("class", "point");
points
.attr("cx", function(d) { return scale.x(d.x); })
.attr("cy", function(d) { return scale.y(d.y); })
.attr("r", function(d) { return scale.r(focusedZ - d.z); })
.style("fill", function(d) { return scale.fill(focusedZ - d.z); })
.style("opacity", function(d) { return scale.opacity(focusedZ - d.z); });
points.exit()
.remove();
}
function createData() {
return d3.range(200)
.map(function() {
var x = d3.random.normal(10)(),
y = d3.random.normal(10)(),
z = Math.cos(x) + Math.sin(y);
return {x:x, y:y, z:z};
});
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment