Skip to content

Instantly share code, notes, and snippets.

@sebg
Forked from feyderm/.DS_Store
Last active January 11, 2018 12:33
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 sebg/e96bce41f2cd1e0131fc5b4bf8427296 to your computer and use it in GitHub Desktop.
Save sebg/e96bce41f2cd1e0131fc5b4bf8427296 to your computer and use it in GitHub Desktop.
Scatter plot + Brush

Data is from the state data set.

Brush over points to reveal their values in an adjacent table.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
text {
font-family: sans-serif;
fill: #000000;
}
table {
visibility: hidden;
position: absolute;
top: 30px;
left: 500px;
font-family: sans-serif;
font-size: 0.7em;
}
tr:nth-child(even) {
background-color: #d9d9d9;
}
.brushed {
fill: #ff3399;
stroke: #8e1b54;
opacity: 1.0;
}
.non_brushed {
fill: #404040;
opacity: 0.5;
}
</style>
<body>
<!--viz-->
<div id="chart"></div>
<!--table for data of brushed elements-->
<div id="table">
<table>
<tr>
<th>State</th>
<th>Income</th>
<th>High School Graduation</th>
</tr>
</table>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript">
// Commented version of
// http://bl.ocks.org/feyderm/6bdbc74236c27a843db633981ad22c1b
// Variables
var margin = {top: 20, right: 0, bottom: 50, left: 85},
svg_dx = 500,
svg_dy = 400,
plot_dx = svg_dx - margin.right - margin.left,
plot_dy = svg_dy - margin.top - margin.bottom;
// x axis scale
// y axis scale
var x = d3.scaleLinear().range([margin.left, plot_dx]),
y = d3.scaleLinear().range([plot_dy, margin.top]);
// Tick formatting code
var formatIncome = d3.format("$,"),
formatHsGrad = d3.format(".1%"),
formatHsGradAxis = d3.format(".0%");
// Create SVG Viewport
var svg = d3.select("#chart")
.append("svg")
.attr("width", svg_dx)
.attr("height", svg_dy);
// CSV AJAX Call
d3.csv("states.csv", function(d) {
// Figure out number of data elements in the data
var n = d.length;
// Define the extent for the x domain based on numerical income
// Define the extend for the y domain based on hs_grad percentages
var d_extent_x = d3.extent(d, d => +d.income),
d_extent_y = d3.extent(d, d => +d.hs_grad);
// Define x and y scale domains based on calculated data
x.domain(d_extent_x);
y.domain(d_extent_y);
// Define x and y axis as well as the respective tick formats
var axis_x = d3.axisBottom(x)
.tickFormat(formatIncome),
axis_y = d3.axisLeft(y)
.tickFormat(formatHsGradAxis);
// Draw the X Axis
svg.append("g")
.attr("id", "axis_x")
.attr("transform", "translate(0," + (plot_dy + margin.bottom / 2) + ")")
.call(axis_x);
// Draw the Y Axis
svg.append("g")
.attr("id", "axis_y")
.attr("transform", "translate(" + (margin.left / 2) + ", 0)")
.call(axis_y);
// Label the X Axis
d3.select("#axis_x")
.append("text")
.attr("transform", "translate(360, -10)")
.text("Per capita income (1974)");
// Label the Y Axis
d3.select("#axis_y")
.append("text")
.attr("transform", "rotate(-90) translate(-20, 15)")
.text("High-school graduates (1970)");
// Create the Scatterplot circles
var circles = svg.append("g")
.selectAll("circle")
.data(d)
.enter()
.append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(+d.income); })
.attr("cy", function(d) { return y(+d.hs_grad); })
.attr("class", "non_brushed");
// Function to run when things are being brushed
// - .on("brush", highlightBrushedCircles)
function highlightBrushedCircles() {
// If something was actually brushed, then do the following
if (d3.event.selection != null) {
// revert circles to initial style
circles.attr("class", "non_brushed");
// Figure out the brush coordinates
var brush_coords = d3.brushSelection(this);
// style brushed circles
// For each circle in the selection
// - figure out where it is
// - run it through the isBrushed function
// - if the coordinates fall within the brush_coords then keep
// - otherwise, don't do anything
// Finally, for those kept, set class to "brushed"
circles.filter(function () {
//
var cx = d3.select(this).attr("cx"),
cy = d3.select(this).attr("cy");
return isBrushed(brush_coords, cx, cy);
})
.attr("class", "brushed");
}
}
// Function to display the HTML table
// - Function run .on("end", displayTable);
function displayTable() {
// disregard brushes w/o selections
// ref: http://bl.ocks.org/mbostock/6232537
if (!d3.event.selection) return;
// programmed clearing of brush after mouse-up
// ref: https://github.com/d3/d3-brush/issues/10
d3.select(this).call(brush.move, null);
var d_brushed = d3.selectAll(".brushed").data();
// populate table if one or more elements is brushed
if (d_brushed.length > 0) {
clearTableRows();
d_brushed.forEach(function(d_row) { return populateTableRow(d_row); })
} else {
clearTableRows();
}
}
// Define Brush Behavior
var brush = d3.brush()
.on("brush", highlightBrushedCircles)
.on("end", displayTable);
svg.append("g")
.call(brush);
});
// Clear the table rows functions
function clearTableRows() {
// First hide the column names
hideTableColNames();
// Remove all the table rows
d3.selectAll(".row_data").remove();
}
// Given coordinates of circles and brush_coords
// - get the boundaries from the brush_coords
// - do a massively long boolean comparison
// to make sure circle coordinates are with brush_coords
function isBrushed(brush_coords, cx, cy) {
var x0 = brush_coords[0][0],
x1 = brush_coords[1][0],
y0 = brush_coords[0][1],
y1 = brush_coords[1][1];
return x0 <= cx && cx <= x1 && y0 <= cy && cy <= y1;
}
// Change HTML Table names to invisible
function hideTableColNames() {
d3.select("table").style("visibility", "hidden");
}
// Change HTML Table names to visitle
function showTableColNames() {
d3.select("table").style("visibility", "visible");
}
// Given data for a row, fill it out
function populateTableRow(d_row) {
// Change HTML Table names to visitle
showTableColNames();
// Create data structure and format as needed
var d_row_filter = [d_row.state,
formatIncome(d_row.income),
formatHsGrad(d_row.hs_grad)];
// Do a data join
// - Add a new row and then do a data join for the <td>'s
// - This way, it'll be only enter selection
// - From data structure above, we'll have three <td>'s
d3.select("table")
.append("tr")
.attr("class", "row_data")
.selectAll("td")
.data(d_row_filter)
.enter()
.append("td")
.attr("align", (d, i) => i == 0 ? "left" : "right")
.text(d => d);
}
</script>
</body>
state population income illiteracy life_exp murder hs_grad frost area
Alabama 3615 3624 2.1 69.05 15.1 0.413 20 50708
Alaska 365 6315 1.5 69.31 11.3 0.667 152 566432
Arizona 2212 4530 1.8 70.55 7.8 0.581 15 113417
Arkansas 2110 3378 1.9 70.66 10.1 0.399 65 51945
California 21198 5114 1.1 71.71 10.3 0.626 20 156361
Colorado 2541 4884 0.7 72.06 6.8 0.639 166 103766
Connecticut 3100 5348 1.1 72.48 3.1 0.56 139 4862
Delaware 579 4809 0.9 70.06 6.2 0.546 103 1982
Florida 8277 4815 1.3 70.66 10.7 0.526 11 54090
Georgia 4931 4091 2 68.54 13.9 0.406 60 58073
Hawaii 868 4963 1.9 73.6 6.2 0.619 0 6425
Idaho 813 4119 0.6 71.87 5.3 0.595 126 82677
Illinois 11197 5107 0.9 70.14 10.3 0.526 127 55748
Indiana 5313 4458 0.7 70.88 7.1 0.529 122 36097
Iowa 2861 4628 0.5 72.56 2.3 0.59 140 55941
Kansas 2280 4669 0.6 72.58 4.5 0.599 114 81787
Kentucky 3387 3712 1.6 70.1 10.6 0.385 95 39650
Louisiana 3806 3545 2.8 68.76 13.2 0.422 12 44930
Maine 1058 3694 0.7 70.39 2.7 0.547 161 30920
Maryland 4122 5299 0.9 70.22 8.5 0.523 101 9891
Massachusetts 5814 4755 1.1 71.83 3.3 0.585 103 7826
Michigan 9111 4751 0.9 70.63 11.1 0.528 125 56817
Minnesota 3921 4675 0.6 72.96 2.3 0.576 160 79289
Mississippi 2341 3098 2.4 68.09 12.5 0.41 50 47296
Missouri 4767 4254 0.8 70.69 9.3 0.488 108 68995
Montana 746 4347 0.6 70.56 5 0.592 155 145587
Nebraska 1544 4508 0.6 72.6 2.9 0.593 139 76483
Nevada 590 5149 0.5 69.03 11.5 0.652 188 109889
New Hampshire 812 4281 0.7 71.23 3.3 0.576 174 9027
New Jersey 7333 5237 1.1 70.93 5.2 0.525 115 7521
New Mexico 1144 3601 2.2 70.32 9.7 0.552 120 121412
New York 18076 4903 1.4 70.55 10.9 0.527 82 47831
North Carolina 5441 3875 1.8 69.21 11.1 0.385 80 48798
North Dakota 637 5087 0.8 72.78 1.4 0.503 186 69273
Ohio 10735 4561 0.8 70.82 7.4 0.532 124 40975
Oklahoma 2715 3983 1.1 71.42 6.4 0.516 82 68782
Oregon 2284 4660 0.6 72.13 4.2 0.6 44 96184
Pennsylvania 11860 4449 1 70.43 6.1 0.502 126 44966
Rhode Island 931 4558 1.3 71.9 2.4 0.464 127 1049
South Carolina 2816 3635 2.3 67.96 11.6 0.378 65 30225
South Dakota 681 4167 0.5 72.08 1.7 0.533 172 75955
Tennessee 4173 3821 1.7 70.11 11 0.418 70 41328
Texas 12237 4188 2.2 70.9 12.2 0.474 35 262134
Utah 1203 4022 0.6 72.9 4.5 0.673 137 82096
Vermont 472 3907 0.6 71.64 5.5 0.571 168 9267
Virginia 4981 4701 1.4 70.08 9.5 0.478 85 39780
Washington 3559 4864 0.6 71.72 4.3 0.635 32 66570
West Virginia 1799 3617 1.4 69.48 6.7 0.416 100 24070
Wisconsin 4589 4468 0.7 72.48 3 0.545 149 54464
Wyoming 376 4566 0.6 70.29 6.9 0.629 173 97203
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment