Created
February 7, 2016 18:34
-
-
Save CafeConVega/bc09d99262b2683d0e0f to your computer and use it in GitHub Desktop.
Week4: Sortable Heatmap Table
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
h1, | |
h2 { | |
font-family: 'Graduate', cursive; | |
text-align: center; | |
text-transform: uppercase; | |
} | |
p { | |
font-family: 'Roboto', sans-serif; | |
text-align: center; | |
} | |
table { | |
font-family: 'Roboto', sans-serif; | |
font-size: .9em; | |
width: 80vw; | |
margin-left: auto; | |
margin-right: auto; | |
text-align: center; | |
table-layout: fixed; | |
border-collapse: collapse; | |
border-spacing: .5em; | |
} | |
td { | |
padding: .5em; | |
border-right: 1px dotted #E6E6E6; | |
} | |
td:last-of-type { | |
border-right: 0px; | |
} | |
thead tr { | |
border-bottom: 1px solid black; | |
cursor: pointer; | |
} | |
thead tr:hover { | |
cursor: grab; | |
} | |
tbody tr:hover { | |
color: white; | |
background-color: darkgray; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>MIA vs. HOU Game Stats</title> | |
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script> | |
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.0.min.js"></script> | |
<script type="text/javascript" src="stupidtable.js"></script> | |
<link href="custom.css" rel="stylesheet" type="text/css" /> | |
<link href='https://fonts.googleapis.com/css?family=Graduate|Roboto' rel='stylesheet' type='text/css'> | |
</head> | |
<body> | |
<h1>Houston Texans at Miami Dolphins</h1> | |
<h2>October 26, 2015 1:00 PM - Sun Life Stadium - Miami, FL</h2> | |
<p>Source: <a href="http://www.pro-football-reference.com/" target="_blank">Pro-Football-Reference</a></p> | |
<h2>Individual Game Statistics - Receptions</h2> | |
<div id="table_rec" class="table"></div> | |
<script> | |
//Load in contents of CSV file | |
d3.csv("mia-hou_off_individual_stats.csv", function (error, data) { | |
if (error) { | |
console.log(error); //Log the error. | |
} else { | |
console.log(data); //Log the data. | |
} | |
//Vars for subsets of the raw data for different tables | |
var data_pass = []; | |
var data_rush = []; | |
var data_rec = []; | |
var rec_data = []; | |
var rec_yards_data = []; | |
var rec_avg_data = []; | |
// Format number fields | |
data.forEach(function (d) { | |
d.pass_comp = +d.pass_comp; | |
d.pass_att = +d.pass_att; | |
d.pass_comp_rate = Math.round(d.pass_comp/d.pass_att*100); //Calculated field | |
d.pass_yards = +d.pass_yards; | |
d.pass_avg_comp = Math.round(d.pass_yards/d.pass_comp); //Calculated field | |
d.pass_td = +d.pass_td; | |
d.pass_int = +d.pass_int; | |
d.pass_sack = +d.pass_sack; | |
d.pass_sack_yards = +d.pass_sack_yards; | |
d.pass_long = +d.pass_long; | |
d.pass_qbr = +d.pass_qbr; | |
d.rush_att = +d.rush_att; | |
d.rush_yards = +d.rush_yards; | |
d.rush_avg = Math.round(d.rush_yards/d.rush_att); //Calculated field | |
d.rush_td = +d.rush_td; | |
d.rush_long = +d.rush_long; | |
d.rec_target = +d.rec_target; | |
d.rec_rec = +d.rec_rec; | |
d.rev_catch_rate = Math.round(d.rec_rec/d.rec_target*100); //Calculated field | |
d.rec_yards = +d.rec_yards; | |
d.rec_avg = Math.round(d.rec_yards/d.rec_rec); //Calculated field | |
d.rec_td = +d.rec_td; | |
d.rec_long = +d.rec_long; | |
d.fmb = +d.fmb; | |
d.fmb_lost = +d.fmb_lost; | |
//Fix averages | |
if(d.rec_rec === 0) { | |
d.rec_avg = 0; | |
} | |
}); | |
//Sort data by reception yards | |
data.sort(function (a, b) { | |
return b.rec_yards - a.rec_yards; | |
}); | |
//Add data to reception arrays | |
data.forEach(function (d) { | |
if(d.rec_target > 0) { | |
data_rec.push([d.team, d.player, d.position, d.rec_target, d.rec_rec, d.rec_yards, d.rec_avg, d.rec_long, d.rec_td, d.fmb, d.fmb_lost]); | |
rec_data.push([d.rec_rec]); | |
rec_yards_data.push([d.rec_yards]); | |
rec_avg_data.push([d.rec_avg]); | |
} | |
}); | |
console.log(data_rec); | |
//Pass Table | |
var table = d3.select("#table_rec").append("table"); | |
var header = table.append("thead").append("tr"); | |
var headerObjs = [ | |
{ label: "TEAM", sort_type: "string" }, | |
{ label: "PLAYER", sort_type: "string" }, | |
{ label: "POSITION", sort_type: "string" }, | |
{ label: "TARGETS", sort_type: "float" }, | |
{ label: "RECEPTIONS", sort_type: "float" }, | |
{ label: "YARDS", sort_type: "float" }, | |
{ label: "AVG YARDS", sort_type: "float" }, | |
{ label: "LONG RECEPTION", sort_type: "float" }, | |
{ label: "TD", sort_type: "float" }, | |
{ label: "FUMBLES", sort_type: "float" }, | |
{ label: "LOST FUMBLES", sort_type: "float" }, | |
]; | |
header | |
.selectAll("th") | |
.data(headerObjs) | |
.enter() | |
.append("th") | |
.attr("data-sort", function (d) { return d.sort_type; }) | |
.text(function(d) { return d.label; }); | |
var tablebody = table.append("tbody"); | |
rows = tablebody | |
.selectAll("tr") | |
.data(data_rec) | |
.enter() | |
.append("tr"); | |
var white = "#FFFFFF"; | |
var red = "#980000"; | |
var green = "#7DC15A"; | |
//Colorscale receptions | |
var colorScale_rec = d3.scale.linear() | |
.domain(d3.extent(rec_data)) | |
.range([white, green]); | |
//Colorscale yards | |
var colorScale_yards = d3.scale.linear() | |
.domain(d3.extent(rec_yards_data)) | |
.range([white, green]); | |
//Colorscale yards avg | |
var colorScale_rec_avg = d3.scale.linear() | |
.domain(d3.extent(rec_avg_data)) | |
.range([white, green]); | |
cells = rows.selectAll("td") | |
.data(function(d) { | |
return d; | |
}) | |
.enter() | |
.append("td") | |
.style("background-color", function(d,i) { | |
// Color receptions background | |
if (i === 4) { | |
return colorScale_rec(d); | |
} | |
// Color yards background | |
if (i === 5) { | |
return colorScale_yards(d); | |
} | |
// Color avg background | |
if (i === 6) { | |
return colorScale_rec_avg(d); | |
} | |
}) | |
.text(function(d) { | |
return d; | |
}); | |
$("#table_rec table").stupidtable(); | |
}); | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
player | team | position | pass_comp | pass_att | pass_yards | pass_td | pass_int | pass_sack | pass_sack_yards | pass_long | pass_qbr | rush_att | rush_yards | rush_td | rush_long | rec_target | rec_rec | rec_yards | rec_td | rec_long | fmb | fmb_lost | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Brian Hoyer | HOU | QB | 23 | 49 | 273 | 3 | 1 | 4 | 22 | 27 | 76.3 | 0 | 0 | 0 | 2 | 0 | |||||||
Arian Foster | HOU | RB | 18 | 59 | 1 | 11 | 7 | 5 | 66 | 1 | 26 | ||||||||||||
Nate Washington | HOU | WR | 16 | 9 | 127 | 2 | 27 | ||||||||||||||||
DeAndre Hopkins | HOU | WR | 12 | 6 | 50 | 0 | 11 | 1 | 0 | ||||||||||||||
Chris Polk | HOU | RB | 4 | 4 | 0 | 5 | 3 | 2 | 14 | 0 | 8 | ||||||||||||
Keith Mumphery | HOU | WR | 3 | 1 | 16 | 0 | 16 | ||||||||||||||||
Alfred Blue | HOU | RB | 3 | 8 | 0 | 3 | 0 | 0 | 0 | ||||||||||||||
Jaelen Strong | HOU | WR | 1 | 0 | 0 | 0 | 0 | ||||||||||||||||
Garrett Graham | HOU | TE | 4 | 0 | 0 | 0 | 0 | ||||||||||||||||
Jonathan Grimes | HOU | RB | 1 | 0 | 0 | 0 | 0 | ||||||||||||||||
C.J. Fiedorowicz | HOU | TE | 2 | 0 | 0 | 0 | 0 | ||||||||||||||||
Lamar Miller | MIA | RB | 14 | 175 | 1 | 85 | 3 | 3 | 61 | 1 | 54 | ||||||||||||
Ryan Tannehill | MIA | QB | 18 | 19 | 282 | 4 | 0 | 4 | 41 | 54 | 158.3 | 1 | 3 | 0 | 3 | 0 | 0 | 0 | |||||
Jarvis Landry | MIA | WR | 1 | 5 | 0 | 5 | 5 | 5 | 83 | 2 | 50 | ||||||||||||
Rishard Matthews | MIA | WR | 3 | 3 | 75 | 1 | 53 | ||||||||||||||||
Jonas Gray | MIA | RB | 12 | 48 | 0 | 16 | 1 | 1 | 10 | 0 | 10 | ||||||||||||
Greg Jennings | MIA | WR | 2 | 2 | 37 | 0 | 23 | ||||||||||||||||
Damien Williams | MIA | RB | 4 | 19 | 0 | 19 | 2 | 2 | 10 | 0 | 8 | 1 | 1 | ||||||||||
Jordan Cameron | MIA | TE | 2 | 2 | 23 | 0 | 12 | ||||||||||||||||
Matt Moore | MIA | QB | 1 | 1 | 14 | 0 | 0 | 0 | 0 | 14 | 118.7 | 3 | -2 | 0 | 0 | 0 | 0 | 0 | |||||
Dion Sims | MIA | TE | 2 | 1 | -3 | 0 | -3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Stupid jQuery table plugin. | |
(function($) { | |
$.fn.stupidtable = function(sortFns) { | |
return this.each(function() { | |
var $table = $(this); | |
sortFns = sortFns || {}; | |
sortFns = $.extend({}, $.fn.stupidtable.default_sort_fns, sortFns); | |
$table.data('sortFns', sortFns); | |
$table.on("click.stupidtable", "thead th", function() { | |
$(this).stupidsort(); | |
}); | |
}); | |
}; | |
// Expects $("#mytable").stupidtable() to have already been called. | |
// Call on a table header. | |
$.fn.stupidsort = function(force_direction){ | |
var $this_th = $(this); | |
var th_index = 0; // we'll increment this soon | |
var dir = $.fn.stupidtable.dir; | |
var $table = $this_th.closest("table"); | |
var datatype = $this_th.data("sort") || null; | |
// No datatype? Nothing to do. | |
if (datatype === null) { | |
return; | |
} | |
// Account for colspans | |
$this_th.parents("tr").find("th").slice(0, $(this).index()).each(function() { | |
var cols = $(this).attr("colspan") || 1; | |
th_index += parseInt(cols,10); | |
}); | |
var sort_dir; | |
if(arguments.length == 1){ | |
sort_dir = force_direction; | |
} | |
else{ | |
sort_dir = force_direction || $this_th.data("sort-default") || dir.ASC; | |
if ($this_th.data("sort-dir")) | |
sort_dir = $this_th.data("sort-dir") === dir.ASC ? dir.DESC : dir.ASC; | |
} | |
$table.trigger("beforetablesort", {column: th_index, direction: sort_dir}); | |
// More reliable method of forcing a redraw | |
$table.css("display"); | |
// Run sorting asynchronously on a timout to force browser redraw after | |
// `beforetablesort` callback. Also avoids locking up the browser too much. | |
setTimeout(function() { | |
// Gather the elements for this column | |
var column = []; | |
var sortFns = $table.data('sortFns'); | |
var sortMethod = sortFns[datatype]; | |
var trs = $table.children("tbody").children("tr"); | |
// Extract the data for the column that needs to be sorted and pair it up | |
// with the TR itself into a tuple. This way sorting the values will | |
// incidentally sort the trs. | |
trs.each(function(index,tr) { | |
var $e = $(tr).children().eq(th_index); | |
var sort_val = $e.data("sort-value"); | |
// Store and read from the .data cache for display text only sorts | |
// instead of looking through the DOM every time | |
if(typeof(sort_val) === "undefined"){ | |
var txt = $e.text(); | |
$e.data('sort-value', txt); | |
sort_val = txt; | |
} | |
column.push([sort_val, tr]); | |
}); | |
// Sort by the data-order-by value | |
column.sort(function(a, b) { return sortMethod(a[0], b[0]); }); | |
if (sort_dir != dir.ASC) | |
column.reverse(); | |
// Replace the content of tbody with the sorted rows. Strangely | |
// enough, .append accomplishes this for us. | |
trs = $.map(column, function(kv) { return kv[1]; }); | |
$table.children("tbody").append(trs); | |
// Reset siblings | |
$table.find("th").data("sort-dir", null).removeClass("sorting-desc sorting-asc"); | |
$this_th.data("sort-dir", sort_dir).addClass("sorting-"+sort_dir); | |
$table.trigger("aftertablesort", {column: th_index, direction: sort_dir}); | |
$table.css("display"); | |
}, 10); | |
return $this_th; | |
}; | |
// Call on a sortable td to update its value in the sort. This should be the | |
// only mechanism used to update a cell's sort value. If your display value is | |
// different from your sort value, use jQuery's .text() or .html() to update | |
// the td contents, Assumes stupidtable has already been called for the table. | |
$.fn.updateSortVal = function(new_sort_val){ | |
var $this_td = $(this); | |
if($this_td.is('[data-sort-value]')){ | |
// For visual consistency with the .data cache | |
$this_td.attr('data-sort-value', new_sort_val); | |
} | |
$this_td.data("sort-value", new_sort_val); | |
return $this_td; | |
}; | |
// ------------------------------------------------------------------ | |
// Default settings | |
// ------------------------------------------------------------------ | |
$.fn.stupidtable.dir = {ASC: "asc", DESC: "desc"}; | |
$.fn.stupidtable.default_sort_fns = { | |
"int": function(a, b) { | |
return parseInt(a, 10) - parseInt(b, 10); | |
}, | |
"float": function(a, b) { | |
return parseFloat(a) - parseFloat(b); | |
}, | |
"string": function(a, b) { | |
return a.localeCompare(b); | |
}, | |
"string-ins": function(a, b) { | |
a = a.toLocaleLowerCase(); | |
b = b.toLocaleLowerCase(); | |
return a.localeCompare(b); | |
} | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment