The following scatterplot utilizing d3 is based heavily on code generated by Mike Bostock, Michelle Weigle, and William Q Liu.
When dealing with my current scatterplot, I had plotted rushing yards vs passing yards. Each quarterback was a dot on the scatterplot and the conference to which they belong is color coded. The idea of animated transitions to me is great for change in time statistics. Unfortunately, our data is static. The number of rushing or passing yards is not changing. While animated transitions can be used to switch between different types of graphs or information displayed, I personally do not feel that this is nothing more than art. It does not seem to help the user evaluate the data. Granted the new chart or data may help, but the animated transition was just an artistic path between two views.
The selection and highlight has a much greater ability to help the user further evaluate the scatterplot data. Trying to put myself as the user, I asked what kind of analysis would I like to perform. Trends always come to mind. So I thought that it would be nice to be able to select a quarterback and have his conference highlighted so a trend can be analyzed. You can also click on the legend to accomlish the same highlighting. The highlighting was done for a set delay and then it returns. This was a trde-off between functionality and d3 coding ability.
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
}
.tooltip {
position: absolute;
width: 200px;
height: 28px;
pointer-events: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.domain(["SEC", "ACC", "Big 12", "Pac-12", "Big Ten"])
.range(["#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
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 + ")");
d3.tsv("data.tsv", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.Passing = +d.Passing;
d.Rushing = +d.Rushing;
});
x.domain(d3.extent(data, function(d) { return d.Rushing; })).nice();
y.domain(d3.extent(data, function(d) { return d.Passing; })).nice();
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Rushing Yards (yds)");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Passing Yards (yds)")
svg.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.Rushing); })
.attr("cy", function(d) { return y(d.Passing); })
.style("fill", function(d) { return color(d.Conf); })
.on("mouseover", function(d) {
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d["Player"])
.style("left", (x(d.Rushing) + 5) + "px")
.style("top", (y(d.Passing) - 15) + "px");
})
.on("mouseout", function(d) {
tooltip.transition()
.duration(500)
.style("opacity", 0);
})
.on("click", function(d) {
// Update circles
var current = d.Conf;
console.log(current);
svg.selectAll("circle")
.data(data) // Update with new data
.transition() // Transition from old to new
.duration(1000) // Length of animation
.each("start", function() { // Start animation
d3.select(this) // 'this' means the current element
.attr("r", function(d) {
if (d.Conf == current) {return "15"}
else { return "3.5" } // Change size
})
})
.each("end", function() { // End animation
d3.select(this) // 'this' means the current element
.transition()
.duration(500)
.attr("r", 3.5); // Change radius
});
});
var legend = svg.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; })
.on("click", function(d) {
// Update circles
var current = d;
console.log(current);
svg.selectAll("circle")
.data(data) // Update with new data
.transition() // Transition from old to new
.duration(1000) // Length of animation
.each("start", function() { // Start animation
d3.select(this) // 'this' means the current element
.attr("r", function(d) {
if (d.Conf == current) {return "15"}
else { return "3.5" } // Change size
})
})
.each("end", function() { // End animation
d3.select(this) // 'this' means the current element
.transition()
.duration(500)
.attr("r", 3.5); // Change radius
});
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
</script>
https://d3js.org/d3.v3.min.js