Skip to content

Instantly share code, notes, and snippets.

@hmader
Last active October 27, 2015 22:11
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 hmader/4f37cc2ff6703c51a4a1 to your computer and use it in GitHub Desktop.
Save hmader/4f37cc2ff6703c51a4a1 to your computer and use it in GitHub Desktop.
Week4: Heatmap table
Country None Primary Secondary or higher Average U5MR
Afghanistan 19.9 27.2 37.7 28.2666666666667 97.3
Azerbaijan 51.4 25 51.4 42.6 34.2
Bangladesh 61.4 62.4 60.3 61.3666666666667 41.1
Benin 11.2 14.9 20.9 15.6666666666667 85.3
Bhutan 67.5 66.2 57.7 63.8 36.2
Bolivia 42 56.1 68.9 55.6666666666667 39.1
Brazil 64.1 71.9 81.8 72.6 13.7
Burkina-Faso 11.8 27 50.5 29.7666666666667 97.6
Burundi 17.5 24.7 41.1 27.7666666666667 82.9
Cambodia 42.5 50.2 57.3 50 37.9
Cameroon 4.1 21.6 40.2 21.9666666666667 94.5
Central African Republic 7.1 15.4 32.8 18.4333333333333 139.2
Chad 1.1 5 25.1 10.4 147.5
Colombia 71.8 80.3 78.7 76.9333333333333 16.9
Comoros 12.9 19.1 26.8 19.6 77.9
Congo Democratic Republic 11.1 15.7 30.2 19 118.5
Congo Republic 33.6 43.1 46.6 41.1 49.1
Costa Rica 58.5 76.2 76.7 70.4666666666667 9.6
Côte-d’Ivoire 13 25.8 30.3 23.0333333333333 100
Djibouti 12.6 20.8 33 22.1333333333333 69.6
Dominican Republic 69.8 75.2 71.2 72.0666666666667 28.1
Egypt 57.7 61.8 61.4 60.3 21.8
Ethiopia 22.2 35.7 62.2 40.0333333333333 64.4
Gabon 14 21.8 36.3 24.0333333333333 56.1
Ghana 26.1 34.3 39.6 33.3333333333333 78.4
Guatemala 19.4 38.4 68 41.9333333333333 31
Guinea 4.7 7.6 10.5 7.6 100.7
Guinea-Bissau 6.2 17.2 30.6 18 123.9
Guyana 21.9 40.4 43.8 35.3666666666667 36.6
Haiti 29.7 34.5 37.2 33.8 72.8
Honduras 64.2 73.2 74.5 70.6333333333333 22.2
India 52.1 60.1 60.2 57.4666666666667 52.7
Indonesia 43.4 61.8 63.1 56.1 29.3
Iraq 46.1 51.3 57.2 51.5333333333333 34
Jordan 45.6 53.4 62.2 53.7333333333333 18.7
Kenya 14.1 44.2 59.8 39.3666666666667 70.7
Lao PDR 38 54.3 52.1 48.1333333333333 71.4
Lesotho 32.1 40.2 55.7 42.6666666666667 98
Liberia 15.3 19.7 28.6 21.2 71.1
Madagascar 20.5 38.9 55.8 38.4 56
Malawi 40.3 46 53.2 46.5 67.9
Maldives 43.6 36.9 27.9 36.1333333333333 9.9
Mali 8.1 13.4 27.5 16.3333333333333 122.7
Mauritania 4.4 11.5 21.9 12.6 90.1
Mongolia 47 57.8 55 53.2666666666667 31.8
Morocco 61.2 62.9 68.8 64.3 30.4
Mozambique 5.5 11.6 31.7 16.2666666666667 87.2
Namibia 33.7 46.1 64.1 47.9666666666667 49.8
Nepal 52.8 47 46.8 48.8666666666667 39.7
Nicaragua 52.1 71.7 73 65.6 23.5
Niger 12 20.5 32.3 21.6 104.2
Nigeria 2.7 19.9 30.9 17.8333333333333 117.4
Pakistan 30.2 40.8 43 38 85.5
Peru 65.6 75.1 76.2 72.3 16.7
Philippines 29.3 52.9 56.2 46.1333333333333 29.9
Rwanda 43.3 52.6 60.3 52.0666666666667 52
Sao Tome and Principe 18.3 40.7 38 32.3333333333333 51
Senegal 12.8 26.9 32.8 24.1666666666667 55.3
Sierra Leone 14.3 19.3 26 19.8666666666667 160.6
Somalia 13.6 15.7 22.6 17.3 145.6
South Africa 33.2 47.2 64.6 48.3333333333333 43.9
Suriname 19.2 42.4 52 37.8666666666667 22.8
Swaziland 54.5 59.1 69.5 61.0333333333333 80
Syrian Arab Republic 45.2 57.5 64.5 55.7333333333333 14.6
Tajikistan 23.9 18.9 28.4 23.7333333333333 47.7
Thailand 63.8 79.5 73.6 72.3 13.1
Timor-Leste 15.9 25.8 26.3 22.6666666666667 54.6
Togo 11.3 17.3 20 16.2 84.7
Turkey 55.9 72.9 75.9 68.2333333333333 19.2
Uganda 17.9 28 44.2 30.0333333333333 66.1
Vanuatu 20.8 38.8 42.1 33.9 16.9
Vietnam 74.7 80.7 77.2 77.5333333333333 23.8
Yemen 23.1 34.2 42.1 33.1333333333333 51.3
Zambia 35 37.8 50.6 41.1333333333333 87.4
Zimbabwe 43 54.5 61.1 52.8666666666667 88.5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>CSV Data in a Table with D3</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
<link href='https://fonts.googleapis.com/css?family=Roboto:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
</head>
<body>
<h1 class="header">Contraception Usage by Education Level in Select Low-Income Countries</h1>
<p class="header">The data in this table shows the percentage of contraception usage in 75 low-income countries and the under-5 mortality rate (U5MR) in each country. The blue columns show the % contraception use for populations with no education, primary education only, and secondary education or greater, as well as the averaged usage. The red column shows the U5MR. </p>
<p class="header">Darker colors indicate a higher percentage of usage or higher mortality rate. The data is by default sorted alphabetically by country name, but columns are sortable by clicking on the column header.</p>
<p id="sources" class="header">Sources: <a href="http://www.who.int/">World Health Organization</a>; <a href="http://www.worldbank.org/">World Bank</a>. Only countries with complete data are represented.</p>
<div id="table"></div>
</body>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/stupidtable.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</html>
body {
box-sizing: border-box;
font-family: "Roboto", sans-serif;
}
.header {
width: 70%;
margin-left: 16px;
}
h1 {
font-size: 1.75em;
margin-top: 2em;
font-weight: 700;
color: #0099FF;
text-transform: uppercase;
}
p {
text-align: justify;
line-height: 1.25em;
}
table {
padding: .75em;
width: 80%;
background-color: rgb(255, 255, 255);
border: 7px solid #F4F4F4;
}
th {
cursor: pointer;
}
td, th {
padding: 1em;
text-align: left;
}
#sources {
font-size: 12px;
font-style: italic;
color: rgb(0, 0,0);
font-style: italic;
}
a {
color: #0099FF;
text-decoration: none;
}
a:hover {
color: rgb(255, 0, 153);
}
//Load in contents of CSV file, and do things to the data.
d3.csv("contraceptive-mortality-select-countries-chart.csv", function (error, myData) {
if (error) {
console.log("Had an error loading file.");
}
/*
myData.sort(function (a, b) { // sort data before using
return b.Average - a.Average; // by Average value
});
*/
// We'll be using simpler data as values, not objects.
var myArray = [];
var noEduVals = []; //-- just in case
var primaryEduVals = []; //-- just in case
var secondaryEduVals = []; //-- just in case
var avgVals = []; //-- just in case
var allVals = []; //-- use for scale
var mortalityVals = []; //-- use for scale
myData.forEach(function (d, i) {
d.Average = +d3.format(".2f")(d.Average);
d.None = +d.None;
d.Primary = +d.Primary;
d["Secondary or Higher"] = +d["Secondary or Higher"];
d.U5MR = +d.U5MR;
// Add a new array with the values of each:
myArray.push([d.Country, d.None, d.Primary, d["Secondary or higher"], d.Average, d.U5MR]);
noEduVals.push(d.None);
console.log("No edu: " + d.None);
primaryEduVals.push(d.Primary);
secondaryEduVals.push(d["Secondary or higher"]);
avgVals.push(d.Average);
mortalityVals.push(d.U5MR);
allVals.push(d.Average);
allVals.push(d.None);
allVals.push(d.Primary);
allVals.push(d["Secondary or higher"]);
});
console.log(noEduVals);
console.log(myData);
console.log(myArray);
// You could also have made the new array with a map function!
var table = d3.select("#table").append("table");
var header = table.append("thead").append("tr");
//--- need to create objects with sort type data for stupidtable
var headerObjs = [
{
label: "Country",
sort_type: "string"
},
{
label: "No Edu.",
sort_type: "int"
},
{
label: "Primary Edu.",
sort_type: "int"
},
{
label: "Secondary Edu.",
sort_type: "int"
},
{
label: "Averaged Edu.",
sort_type: "int"
},
{
label: "U5MR",
sort_type: "int"
},
];
//--- passing data object with table header labels and sort_type below
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(myArray)
.enter()
.append("tr");
// We built the rows using the nested array - now each row has its own array.
// let's talk about the scale - start at 0 or at lowest number?
console.log('No edu. extent is ', d3.extent(noEduVals));
console.log('Primary edu. extent is ', d3.extent(primaryEduVals));
console.log('Secondary edu. extent is ', d3.extent(secondaryEduVals));
console.log('Average edu. extent is ', d3.extent(avgVals));
console.log('Total edu. extent is ', d3.extent(allVals));
var colorScaleBlue = d3.scale.linear()
.domain(d3.extent(allVals))
.range(["#E6F5FF", "#0099FF"]);
var colorScaleRed = d3.scale.linear()
.domain(d3.extent(mortalityVals))
.range(["#FFE6E6", "#FF0000"]);
console.log("mortalityVals: " + mortalityVals);
console.log(d3.extent(mortalityVals));
cells = rows.selectAll("td")
// each row has data associated; we get it and enter it for the cells.
.data(function (d) {
return d;
})
.enter()
.append("td")
.style("background-color", function (d, i) {
// color the background for each value column corresponding to appropriate scale:
if ((i === 1) || (i === 2) || (i === 3) || (i === 4)) {
return colorScaleBlue(d);
}
if (i === 5) {
return colorScaleRed(d);
}
})
.text(function (d) {
return d;
});
/*--- See difference from above & below: to add the color scale, must append the style element with a function of the color scale.
cells = rows.selectAll("td")
// each row has data associated; we get it and enter it for the cells.
.data(function (d) {
console.log(d);
return d;
})
.enter()
.append("td")
.text(function (d) {
return d;
});
*/
//--- Using stupidtable below to make sortable
$("table").stupidtable();
});
// 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