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
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> |