Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@justlebeau
Last active April 3, 2019 08:51
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 justlebeau/6e1517a4cd138a2ccf78e9110731d1ee to your computer and use it in GitHub Desktop.
Save justlebeau/6e1517a4cd138a2ccf78e9110731d1ee to your computer and use it in GitHub Desktop.
Scatter plot + Brush
license: mit

Data is some ficticious student data.

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

forked from sebg's block: Scatter plot + Brush

Name grade type term number category variable1 variable2 variable3
Ryan Richards 50 class Fall 2015 1 M 60 95 60
Robert Mcdermed 55 class Fall 2015 1 M 70 93 70
Faisal el-Younis 57 class Fall 2015 1 M 87 71 90
Shafeeq el-Saladin 62 class Fall 2015 1 M 66 94 70
Kristopher Santeramo 65 class Fall 2015 1 M 85 71 90
Sergio Cordova 72 class Fall 2015 1 M 76 71 80
Ira Lopez 78 class Fall 2015 1 M 80 86 80
Thanysla Calvert 80 class Fall 2015 1 M 73 84 70
Aliya Mitchell 85 class Fall 2015 1 F 74 92 70
Krystal Wilson 86 class Fall 2015 1 F 71 75 70
Leioka Hyden-Terry 87 class Fall 2015 1 F 90 96 90
Aashtin Crane 90 class Fall 2015 1 F 78 74 80
Lara Ely 92 class Fall 2015 1 F 85 61 90
Lena Lee 82 class Fall 2015 1 F 74 86 70
Isabel Vierra 83 class Fall 2015 1 F 72 69 70
Kimberly Larson 84 class Fall 2015 1 F 68 64 70
Michelle Haynie 84 class Fall 2015 1 F 90 73 90
Gercia Allen 84 class Fall 2015 1 F 84 65 80
Sara Donaldson 85 class Fall 2015 1 F 74 77 70
Ryan Richards 55 class Spring 2016 2 M 62 82 60
Robert Mcdermed 53 class Spring 2016 2 M 87 62 90
Faisal el-Younis 56 class Spring 2016 2 M 60 61 60
Shafeeq el-Saladin 66 class Spring 2016 2 M 75 88 80
Kristopher Santeramo 70 class Spring 2016 2 M 79 67 80
Sergio Cordova 70 class Spring 2016 2 M 71 77 70
Ira Lopez 81 class Spring 2016 2 M 76 66 80
Thanysla Calvert 82 class Spring 2016 2 M 82 74 80
Aliya Mitchell 83 class Spring 2016 2 F 76 75 80
Krystal Wilson 87 class Spring 2016 2 F 81 88 80
Leioka Hyden-Terry 90 class Spring 2016 2 F 82 65 80
Aashtin Crane 95 class Spring 2016 2 F 74 71 70
Lara Ely 92 class Spring 2016 2 F 87 93 90
Lena Lee 84 class Spring 2016 2 F 86 71 90
Isabel Vierra 85 class Spring 2016 2 F 65 67 70
Kimberly Larson 82 class Spring 2016 2 F 89 82 90
Michelle Haynie 83 class Spring 2016 2 F 86 67 90
Gercia Allen 83 class Spring 2016 2 F 80 75 80
Sara Donaldson 85 class Spring 2016 2 F 90 71 90
Ryan Richards 53 class Fall 2016 3 M 61 94 60
Robert Mcdermed 55 class Fall 2016 3 M 61 97 60
Faisal el-Younis 54 class Fall 2016 3 M 72 63 70
Shafeeq el-Saladin 68 class Fall 2016 3 M 83 76 80
Kristopher Santeramo 67 class Fall 2016 3 M 63 75 60
Sergio Cordova 71 class Fall 2016 3 M 77 84 80
Ira Lopez 78 class Fall 2016 3 M 64 79 60
Thanysla Calvert 81 class Fall 2016 3 M 75 77 80
Aliya Mitchell 87 class Fall 2016 3 F 87 95 90
Krystal Wilson 91 class Fall 2016 3 F 65 65 70
Leioka Hyden-Terry 91 class Fall 2016 3 F 78 88 80
Aashtin Crane 95 class Fall 2016 3 F 63 85 60
Lara Ely 95 class Fall 2016 3 F 73 73 70
Lena Lee 83 class Fall 2016 3 F 80 85 80
Isabel Vierra 88 class Fall 2016 3 F 79 74 80
Kimberly Larson 82 class Fall 2016 3 F 64 70 60
Michelle Haynie 88 class Fall 2016 3 F 84 77 80
Gercia Allen 88 class Fall 2016 3 F 83 71 80
Sara Donaldson 86 class Fall 2016 3 F 68 80 70
Ryan Richards 50 class Spring 2017 4 M 65 77 70
Robert Mcdermed 58 class Spring 2017 4 M 77 84 80
Faisal el-Younis 56 class Spring 2017 4 M 69 95 70
Shafeeq el-Saladin 66 class Spring 2017 4 M 62 62 60
Kristopher Santeramo 72 class Spring 2017 4 M 63 85 60
Sergio Cordova 69 class Spring 2017 4 M 77 92 80
Ira Lopez 77 class Spring 2017 4 M 72 66 70
Thanysla Calvert 85 class Spring 2017 4 M 75 88 80
Aliya Mitchell 88 class Spring 2017 4 F 74 90 70
Krystal Wilson 94 class Spring 2017 4 F 70 69 70
Leioka Hyden-Terry 89 class Spring 2017 4 F 89 66 90
Aashtin Crane 95 class Spring 2017 4 F 61 75 60
Lara Ely 95 class Spring 2017 4 F 82 63 80
Lena Lee 87 class Spring 2017 4 F 77 68 80
Isabel Vierra 88 class Spring 2017 4 F 81 95 80
Kimberly Larson 87 class Spring 2017 4 F 61 87 60
Michelle Haynie 87 class Spring 2017 4 F 76 86 80
Gercia Allen 85 class Spring 2017 4 F 71 93 70
Sara Donaldson 90 class Spring 2017 4 F 78 72 80
Ryan Richards 47 class Fall 2017 5 M 66 71 70
Robert Mcdermed 56 class Fall 2017 5 M 85 72 90
Faisal el-Younis 53 class Fall 2017 5 M 69 60 70
Shafeeq el-Saladin 70 class Fall 2017 5 M 76 79 80
Kristopher Santeramo 77 class Fall 2017 5 M 74 87 70
Sergio Cordova 67 class Fall 2017 5 M 78 73 80
Ira Lopez 76 class Fall 2017 5 M 76 63 80
Thanysla Calvert 83 class Fall 2017 5 M 65 88 70
Aliya Mitchell 87 class Fall 2017 5 F 62 60 60
Krystal Wilson 93 class Fall 2017 5 F 85 87 90
Leioka Hyden-Terry 88 class Fall 2017 5 F 65 85 70
Aashtin Crane 97 class Fall 2017 5 F 79 61 80
Lara Ely 99 class Fall 2017 5 F 75 96 80
Lena Lee 92 class Fall 2017 5 F 74 88 70
Isabel Vierra 93 class Fall 2017 5 F 79 73 80
Kimberly Larson 88 class Fall 2017 5 F 78 70 80
Michelle Haynie 91 class Fall 2017 5 F 76 75 80
Gercia Allen 90 class Fall 2017 5 F 85 68 90
Sara Donaldson 92 class Fall 2017 5 F 66 87 70
<!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>Name</th>
<th>Grade</th>
<th>Variable1</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(""),
// 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("book1.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.variable1),
d_extent_y = d3.extent(d, d => +d.grade);
// 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("Quiz 1");
// Label the Y Axis
d3.select("#axis_y")
.append("text")
.attr("transform", "rotate(-90) translate(-20, 15)")
.text("Grades");
// 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.variable1); })
.attr("cy", function(d) { return y(+d.grade); })
.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(d_row => 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.Name,
formatVariable1(d_row.variable1),
formatGrade(d_row.grade)];
// 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment