Skip to content

Instantly share code, notes, and snippets.

@nikolay-shenkov
Created July 1, 2015 00:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nikolay-shenkov/3c05dd0ec4b86cdbb968 to your computer and use it in GitHub Desktop.
Save nikolay-shenkov/3c05dd0ec4b86cdbb968 to your computer and use it in GitHub Desktop.
function createVis(error, data, completion_type) {
// output error message, if any:
if (error) { console.log(error); }
// use a global variable to store parsed_data
// so that we have access to it from other functions
data_parsed = data;
// set a default completion type:
// if completion type is undefined, set it to female completion
completion_type = completion_type || "female_completion";
// -------------------------------------
// Create a subset of the data to be used to draw scatter points
var data_subset = [];
// a function to label countries as extreme or not
// extreme = if the difference between male and female completion
// is greater than 10%
function is_extreme(completion_1, completion_2) {
var threshold = 10;
return Math.abs(completion_1 - completion_2) > threshold
}
for (var i = 0; i < data.length; i++) {
var new_elem = {
"country": data[i]["country"],
"gdp_per_capita": data[i]["gdp_per_capita"],
"extreme": is_extreme(data[i]["female_completion"], data[i]["male_completion"]),
"female_completion": data[i]["female_completion"],
"male_completion": data[i]["male_completion"]
};
data_subset.push(new_elem);
}
renderVis(data_subset, completion_type)
}
function renderVis(data, completion_type) {
// bind data to html elements - circles
var selection = bodyG.selectAll("circle.country")
.data(data, function(d) {return d.country;});
// tooltip formatting function to display tooltip text
var tooltip_format = function(country, gdp, female_completion, male_completion) {
return country + " GDP: " + (d3.format(".3n"))(gdp) + "$" +
"<br>" + "female completion: " + (d3.format(".3n"))(female_completion) + "%" +
"<br>" + "male completion: " + (d3.format(".3n"))(male_completion) + "%";
}
// enter selection
// all points are colored blue by default
selection.enter().append("circle")
.attr("class", "country")
.attr("fill", "steelblue");
function colorPoints(d) {
if (d.extreme)
return "rgb(183, 32, 81)";
else
return "steelblue";
}
selection.exit().remove();
// update selection handles animation so we use the duration()
// to specify a 2 second transition
selection.transition().duration(2000)
// we use the log and linear scales to go from
// data elements (e.g. gdp per capita) to pixel locations cx and cy
.attr("cx", function(d) {return log(d["gdp_per_capita"]);})
.attr("cy", function(d) {return linear(d[completion_type])})
.attr("r", radius)
.attr("fill", colorPoints);
// specify a tooltip on mouseover
bodyG.selectAll("circle.country")
.on("mouseover", function(d) {
// use a d3.event.target to access the circle element
// that is being hovered on
d3.select(d3.event.target).transition().duration(200)
.attr("fill", "rgb(255, 128, 22)");
tooltip.transition().duration(200).style("opacity", 1);
tooltip.html(tooltip_format(d["country"], d["gdp_per_capita"],
d["female_completion"], d["male_completion"]))
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 10) + "px");
})
.on("mouseout", function(d) {
d3.select(d3.event.target).transition().duration(500)
.attr("fill", colorPoints(d));
tooltip.transition().duration(500).style("opacity", 0);
});
}
country female_completion male_completion gdp_per_capita
Algeria 97.35583 95.47622 12289.027258652
Argentina 104.62379 100.66476 13873.3973747516
Armenia 103.02998 99.7926 7161.2441370035
Aruba 95.14321 91.48936 40558.4156394559
Austria 103.69019 103.22096 43875.8634527672
Azerbaijan 98.49185 97.80458 13018.2734364597
Bahamas 100.88828 103.81398 25225.1398744822
Barbados 95.43974 88.11736 16001.6991077427
Belarus 92.11484 93.55611 12872.3053191926
Belgium 91.54804 87.76347 41629.864205951
Belize 95.98029 102.45714 8074.5799280273
Bolivia 98.22902 98.73953 4916.4463328266
Botswana 96.12179 92.22388 13354.0627855201
Brunei 119.28105 115.3892 75642.526825659
Bulgaria 97.84627 98.70502 14277.7716688769
Burkina Faso 29.07386 37.00692 1320.8512826941
Burundi 38.41644 45.39401 684.1413387599
Cambodia 90.70509 91.22084 2300.0155974524
Cameroon 51.7478 62.42028 2552.1910686736
Cape Verde 95.45072 90.66958 5389.3860432535
Chile 89.15037 102.06293 18300.544727773
Colombia 113.52849 109.10287 10179.6857083615
Comoros 64.2246 69.74026 1427.6538614206
Congo, Dem. Rep. 40.73085 60.14872 634.9647187339
Congo, Rep. 73.75983 78.42818 4778.3310537773
Costa Rica 93.9137 90.94273 11886.2649985042
Cote d'Ivoire 38.87391 56.23429 2806.7911809677
Croatia 100.83056 101.30729 21999.66217205
Cuba 91.74624 91.71979 16898.847345711
Cyprus 99.95973 99.94413 32446.93265415
Denmark 101.73883 101.20512 45458.6887565343
Dominica 97.48892 92.10526 9385.9718784079
Ecuador 106.17944 104.84761 8751.4085971644
Egypt 93.27361 98.17595 9471.8567039559
El Salvador 90.53235 87.00895 7373.386032085
Eritrea 41.15218 52.72595 1238.7070714377
Estonia 98.15987 99.56909 25545.6192270155
Ethiopia 43.08178 53.5593 862.9580679191
Fiji 103.49723 102.24421 7310.197791402
Finland 98.42159 98.21577 42137.1452843968
Gambia 73.26979 71.59998 1502.6924943453
Georgia 92.07035 96.20418 5735.0966766016
Germany 103.12148 102.38585 41056.4935311553
Ghana 75.4757 80.22254 2718.7657368381
Greece 100.49263 101.3603 32359.9090125418
Grenada 98.22596 97.06691 12023.1007427025
Guatemala 74.03614 80.62982 6899.3281952802
Guinea 54.53389 72.61478 1192.6101476025
Guyana 102.00366 95.2538 5069.8721341213
Hong Kong, China 101.19709 101.59161 46028.7878896375
Hungary 95.01021 96.45459 23316.5642698646
Iceland 96.95633 96.89337 44510.3862304649
India 92.4569 96.08657 3828.4101334308
Indonesia 101.14018 103.40563 7116.6998217769
Iran 95.36778 96.08603 14549.674156445
Iraq 55.39513 74.4742 11614.5107574296
Israel 104.9814 102.1888 27913.4445067022
Italy 100.97996 101.56501 38610.1930368561
Jamaica 85.36357 83.32319 8967.3775831749
Jordan 104.2686 103.60543 10393.5387070097
Kazakhstan 102.02182 100.826 18329.6768934656
Kiribati 104.27948 138.58333 1781.9579747226
Kuwait 114.22918 110.15804 102060.798623929
Latvia 92.05514 95.76749 22045.6755474358
Lebanon 87.7129 83.49108 12744.7570667222
Lesotho 81.54746 60.16019 1980.7097312589
Liberia 63.13395 74.85203 640.2942969144
Lithuania 95.97445 95.38484 21275.1386445689
Macao, China 96.8486 99.05603 79875.4769183965
Macedonia, FYR 92.85767 92.81246 10367.1381181208
Madagascar 62.31528 62.80986 1449.9454430682
Malawi 59.84461 58.76665 642.5597294405
Mali 40.1807 56.36193 1572.3480839379
Malta 95.04831 96.6348 27172.2250562066
Marshall Islands 119.12351 113.39623 3436.4504161557
Mauritania 56.72536 56.67185 2678.5330715377
Mauritius 95.6031 92.44457 13667.1513935432
Mexico 104.13633 103.08345 16206.3244596671
Moldova 92.87085 92.95444 3585.0677220772
Mongolia 109.32974 106.9648 6385.2413771627
Morocco 79.80282 87.30273 5825.4031883929
Mozambique 39.04196 52.51468 810.1118916146
Myanmar 99.71613 93.35262 2846.230357341
Namibia 86.06281 77.12673 8042.89339644
Nicaragua 79.79047 72.83059 3986.1928627597
Niger 31.58022 47.03505 769.2240545399
Nigeria 71.96312 89.86682 4382.2164558205
Norway 97.51715 97.20876 65044.59870358
Pakistan 53.49888 67.53208 4216.7751613046
Panama 98.46349 97.50738 12814.659858148
Paraguay 95.54136 93.84992 6453.3096243645
Peru 103.34836 103.60842 8609.3719582645
Philippines 94.67851 88.94463 5155.8645440919
Qatar 97.81575 106.14365 125625.606269164
Romania 116.01544 114.85452 17010.999147557
Samoa 108.76242 107.43632 5662.4806412001
Sao Tome and Principe 73.22154 70.07444 2384.6063270466
Saudi Arabia 92.23329 94.55005 40949.5304450474
Senegal 50.54852 53.70338 2123.5023668099
Serbia 100.68998 100.35878 12536.1500490148
Seychelles 120.59748 118.76833 21641.6413452145
Slovak Republic 93.1476 92.81648 23788.7100585809
Spain 98.64053 98.77576 34477.7995197823
Sri Lanka 101.52506 101.69981 6518.3431999269
Suriname 80.92048 72.04956 13484.5781250747
Swaziland 70.77358 75.69924 6251.9150067393
Sweden 95.91131 95.44557 43574.8860156294
Switzerland 93.77574 91.54495 54581.8997228911
Syria 103.02075 104.0705 6093.6236925529
Tajikistan 92.80384 96.9551 1864.8506264769
Tanzania 81.36033 85.1121 1383.8006538396
Trinidad and Tobago 93.38731 92.81595 30246.5474249106
Tunisia 101.51381 104.55751 9584.5811448304
Turkey 93.24417 98.44286 16633.6004186782
Uganda 51.26265 55.88286 1168.6341159396
Ukraine 102.17759 101.93806 8530.3907998738
United States 101.44458 98.99668 51136.3700877917
Uruguay 105.73136 102.86746 14153.5126626263
Uzbekistan 96.42169 98.39212 3413.6822577465
Vanuatu 83.37147 80.20796 2816.0438714665
Venezuela 99.89733 95.69116 17279.8346380165
West Bank and Gaza 86.80834 86.63195 4014.5956833598
Zambia 86.88768 98.40848 3096.2364213116
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Female/Male School Completion vs GDP per capita</title>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
</head>
<body>
<header id="title-container">
<h1 id="title">Gender inequality in primary school completion</h1>
</header>
<section id="scatter-container"></section>
<div id="buttons-container">
<button id="girls" class="buttons">Girls</button>
</br>
<button id="boys" class="buttons">Boys</button>
</div>
<div id="notes-container">
<p class="metrics">Primary school completion rate is the percentage of female or male students completing the last year of primary school. The ratio can exceed 100% due to over-aged and under-aged children.</p>
<p class="metrics">In this visualization color and motion display the difference between femaile and male completion rates for each country. Colors are encoded as follows:</p>
</div>
<footer><h2>References:</h2>
<ol>
<li>Data from <a href="http://www.gapminder.org/data/">Gapminder</a></li>
<li><a href="http://bl.ocks.org/weiglemc/6185069">Scatterplot example in d3</a></li>
<li><a href="http://zeroviscosity.com/d3-js-step-by-step/step-5-adding-tooltips">Adding tooltips to a visualization</a></li>
<li><a href="http://bl.ocks.org/zanarmstrong/raw/05c1e95bf7aa16c4768e/">Interactive number formatter in d3</a></li>
</ol>
</footer>
<script src="initialize_figure.js"></script>
<script src="create_vis.js"></script>
</body>
<script>
d3.csv("data/edu-gdp.csv", function(d) {
d["gdp_per_capita"] = +d["gdp_per_capita"];
d['female_completion'] = +d['female_completion'];
d["male_completion"] = +d["male_completion"];
return d;
}, createVis);
</script>
</html>
// global variable to hold data once we load it with d3.csv
var data_parsed;
var height = 600, width = 850,
margin = 50, radius = 10;
// create the svg element that contains visualization
var svg = d3.select("#scatter-container").append("svg")
.attr("id", "scatter")
.attr("width", width + 2*margin)
.attr("height", height);
// change svg background from white to light gray
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "rgb(240, 232, 226)");
// keep scale ranges constant
var minX = 635, minY = 29.1, maxX = 126000, maxY = 130;
// create two scales - linear (y) and logarithmic (x)
var log = d3.scale.log()
.domain([minX - 100, maxX])
.range([0, width - 2*margin]);
var linear = d3.scale.linear()
.domain([minY - 5, maxY + 10])
.range([height - 2*margin, 0]);
// a "g" element to hold axes
var axesG = svg.append("g").attr("class", "axes");
// create axis elements
var xAxis = d3.svg.axis().scale(log).orient("bottom")
.tickValues([1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000])
.tickFormat(d3.format(".1e"))
yAxis = d3.svg.axis().scale(linear).orient("left");
// helper function to create the "translate(x, y)" string
function translator(x, y) {
return "translate(" + x + "," + y + ")";
}
// append axes elements to axesG container
axesG.append("g")
.attr("class", "y-axis all-axis")
// location for the y-axis: margin, margin
.attr("transform", translator(margin, margin))
.call(yAxis)
// add y-axis label
.append("text")
.attr("id", "y-axis")
.attr("class", "axis-label")
// rotate text so that it is vertical
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
// position text appropriately along the axis
.style("text-anchor", "end")
// finally, specify the actual label
.text("Primary school completion male/female");
axesG.append("g")
.attr("class", "x-axis all-axis")
.attr("transform", translator(margin, height - margin))
.call(xAxis)
.append("text")
.attr("id", "x-label")
.attr("class", "axis-label")
.attr("x", width - 2*margin)
.attr("y", -6)
.style("text-anchor", "end")
.text("Income per person (GDP/capita $)");;
// add grid-lines for the x-axis
d3.selectAll("g.x-axis g.tick")
.append("line")
.classed("grid-line", true)
// they start at (0, 0) and go to (0, height - 2*margin)
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", - (height - 2*margin));
// add tooltip, but make it transparent for now
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// container for the scatter points
var bodyG = svg.append("g")
.attr("class", "scatter-g")
.attr("transform", translator(margin, margin));
// --------------------------------------
// Creating buttons and binding data to them
// note the "completion" field in each of the objects
var buttonsData = [{"text": "Girls", "completion": "female_completion"},
{"text": "Boys", "completion": "male_completion"}];
// bind the buttons (girls and boys) to data
var buttons_selection = d3.selectAll(".buttons").data(buttonsData)
// make girls button look active (pressed)
// we are using a 1 sec (1000 ms) transition for a smooth effect
d3.select("#girls").transition().duration(1000).
style("background", "steelBlue").style("color", "white");
// and boys button look inactive (not pressed)
d3.select("#boys").transition().duration(1000)
.style("background", "rgb(220, 244, 252)").style("color", "black")
// note that it is partially transparent
.style("opacity", 0.6);
// declare a click handler for both of the buttons
buttons_selection.on("click", function(d) {
// make both buttons look inactve
d3.selectAll(".buttons").transition().duration(500)
.style("background", "rgb(220, 244, 252)").style("color", "black")
.style("opacity", 0.6);
// make the button that is pressed (we can access it using the this parameter)
// look pressed
d3.select(this).transition().duration(500)
.style("background", "steelBlue").style("color", "white")
.style("opacity", 1);
// finally call the createVis function to update the visualization!
createVis(null, data_parsed, d.completion);
});
function addLegend() {
var legend_svg = d3.select("#notes-container").append("svg")
.attr("id", "legend")
.attr("width", 350)
.attr("height", 80);
var legendG = legend_svg.append("g").attr("id", "lengendG");
var legend_data = [
{"text": "- difference between completions <10%",
"location": 0, "fill": "steelblue"},
{"text": "- difference between completions >10%",
"location": 50, "fill": "#b72051"}
];
var cx = 10, cy_offset = 10, radius = 10, text_offset = 30,
alpha = 0.7;
var selection = legendG.selectAll("circle.items").data(legend_data);
selection.enter().append("circle")
.attr("class", "items")
.attr("fill", function(d) { return d["fill"];} )
.attr("cx", cx)
.attr("cy", function(d) {return d["location"] + cy_offset;} )
.attr("r", radius)
.attr("opacity", alpha)
var textLabels = legendG.selectAll("text").data(legend_data)
.enter().append("text")
.attr("x", text_offset)
.attr("y", function(d) {return d["location"] + cy_offset + radius/2;} )
.text(function(d) {return d["text"]; })
.attr("font-size", "18px")
.attr("font-family", "Helvetica");
}
addLegend();
#scatter-container {
width: 70%;
float: left;
margin-left: 50px;
}
#buttons-container {
margin-top: 50px;
margin-left: 50px;
}
.buttons {
font-size: 22px;
margin-bottom: 30px;
font-family: Verdana;
border-radius: 10px;
}
#title {
font-family: Verdana;
text-align: center;
font-size: 30px;
font-weight: normal;
}
footer {
width: 100%;
float: left;
display: block;
}
.all-axis path,
.all-axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.country {
opacity: 0.6;
}
.grid-line {
opacity: 0.2;
}
.tooltip {
position: absolute;
width: 350px;
font-size: 20px;
font-family: sans-serif;
pointer-events: none;
text-align: center;
}
.metrics, ol {
font-size: 18px;
font-family: Helvetica;
}
h2 {
font-family: Verdana;
font-weight: normal;
font-size: 20px;
}
footer {
margin-left: 50px;
margin-bottom: 20px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment