Skip to content

Instantly share code, notes, and snippets.

@evaristoc
Last active January 22, 2016 22:15
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 evaristoc/1d19701fb9dfe5ecbbbc to your computer and use it in GitHub Desktop.
Save evaristoc/1d19701fb9dfe5ecbbbc to your computer and use it in GitHub Desktop.
FCC scatterplot countries' sessions vs demographics (test)

scatterplot for the analysis of sessions article (work in progress)

###FCC sessions, campsites, country internet pop and English

x-axis: number of campsites (transformed data, controlled by internet population); y-axis: number sessions (transformed data, controlled by internet population)

internet popultion: large circle: internet population over 20mill; small circle: internet population equal or smaller than 20mill

English profiency: orange: over-average EPI; green: lower or equal to average EPI

some references:

(function() {
// evaristoc: copied from http://bl.ocks.org/jensgrubert/7789216
// Inspired by http://informationandvisualization.de/blog/box-plot
d3.box = function() {
var width = 1,
height = 1,
duration = 0,
domain = null,
value = Number,
whiskers = boxWhiskers,
quartiles = boxQuartiles,
showLabels = true, // whether or not to show text labels
numBars = 4,
curBar = 1,
tickFormat = null;
// For each small multiple…
function box(g) {
g.each(function(data, i) {
//d = d.map(value).sort(d3.ascending);
//var boxIndex = data[0];
//var boxIndex = 1;
var d = data[1].sort(d3.ascending);
// console.log(boxIndex);
//console.log(d);
var g = d3.select(this),
n = d.length,
min = d[0],
max = d[n - 1];
// Compute quartiles. Must return exactly 3 elements.
var quartileData = d.quartiles = quartiles(d);
// Compute whiskers. Must return exactly 2 elements, or null.
var whiskerIndices = whiskers && whiskers.call(this, d, i),
whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; });
// Compute outliers. If no whiskers are specified, all data are "outliers".
// We compute the outliers as indices, so that we can join across transitions!
var outlierIndices = whiskerIndices
? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n))
: d3.range(n);
// Compute the new x-scale.
var x1 = d3.scale.linear()
.domain(domain && domain.call(this, d, i) || [min, max])
.range([height, 0]);
// Retrieve the old x-scale, if this is an update.
var x0 = this.__chart__ || d3.scale.linear()
.domain([0, Infinity])
// .domain([0, max])
.range(x1.range());
// Stash the new scale.
this.__chart__ = x1;
// Note: the box, median, and box tick elements are fixed in number,
// so we only have to handle enter and update. In contrast, the outliers
// and other elements are variable, so we need to exit them! Variable
// elements also fade in and out.
// Update center line: the vertical line spanning the whiskers.
var center = g.selectAll("line.center")
.data(whiskerData ? [whiskerData] : []);
//vertical line
center.enter().insert("line", "rect")
.attr("class", "center")
.attr("x1", width / 2)
.attr("y1", function(d) { return x0(d[0]); })
.attr("x2", width / 2)
.attr("y2", function(d) { return x0(d[1]); })
.style("opacity", 1e-6)
.transition()
.duration(duration)
.style("opacity", 1)
.attr("y1", function(d) { return x1(d[0]); })
.attr("y2", function(d) { return x1(d[1]); });
center.transition()
.duration(duration)
.style("opacity", 1)
.attr("y1", function(d) { return x1(d[0]); })
.attr("y2", function(d) { return x1(d[1]); });
center.exit().transition()
.duration(duration)
.style("opacity", 1e-6)
.attr("y1", function(d) { return x1(d[0]); })
.attr("y2", function(d) { return x1(d[1]); })
.remove();
// Update innerquartile box.
var box = g.selectAll("rect.box")
.data([quartileData]);
box.enter().append("rect")
.attr("class", "box")
.attr("x", 0)
.attr("y", function(d) { return x0(d[2]); })
.attr("width", width)
.attr("height", function(d) { return x0(d[0]) - x0(d[2]); })
.transition()
.duration(duration)
.attr("y", function(d) { return x1(d[2]); })
.attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
box.transition()
.duration(duration)
.attr("y", function(d) { return x1(d[2]); })
.attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
// Update median line.
var medianLine = g.selectAll("line.median")
.data([quartileData[1]]);
medianLine.enter().append("line")
.attr("class", "median")
.attr("x1", 0)
.attr("y1", x0)
.attr("x2", width)
.attr("y2", x0)
.transition()
.duration(duration)
.attr("y1", x1)
.attr("y2", x1);
medianLine.transition()
.duration(duration)
.attr("y1", x1)
.attr("y2", x1);
// Update whiskers.
var whisker = g.selectAll("line.whisker")
.data(whiskerData || []);
whisker.enter().insert("line", "circle, text")
.attr("class", "whisker")
.attr("x1", 0)
.attr("y1", x0)
.attr("x2", 0 + width)
.attr("y2", x0)
.style("opacity", 1e-6)
.transition()
.duration(duration)
.attr("y1", x1)
.attr("y2", x1)
.style("opacity", 1);
whisker.transition()
.duration(duration)
.attr("y1", x1)
.attr("y2", x1)
.style("opacity", 1);
whisker.exit().transition()
.duration(duration)
.attr("y1", x1)
.attr("y2", x1)
.style("opacity", 1e-6)
.remove();
// Update outliers.
var outlier = g.selectAll("circle.outlier")
.data(outlierIndices, Number);
outlier.enter().insert("circle", "text")
.attr("class", "outlier")
.attr("r", 5)
.attr("cx", width / 2)
.attr("cy", function(i) { return x0(d[i]); })
.style("opacity", 1e-6)
.transition()
.duration(duration)
.attr("cy", function(i) { return x1(d[i]); })
.style("opacity", 1);
outlier.transition()
.duration(duration)
.attr("cy", function(i) { return x1(d[i]); })
.style("opacity", 1);
outlier.exit().transition()
.duration(duration)
.attr("cy", function(i) { return x1(d[i]); })
.style("opacity", 1e-6)
.remove();
// Compute the tick format.
var format = tickFormat || x1.tickFormat(8);
// Update box ticks.
var boxTick = g.selectAll("text.box")
.data(quartileData);
if(showLabels == true) {
boxTick.enter().append("text")
.attr("class", "box")
.attr("dy", ".3em")
.attr("dx", function(d, i) { return i & 1 ? 6 : -6 })
.attr("x", function(d, i) { return i & 1 ? + width : 0 })
.attr("y", x0)
.attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; })
.text(format)
.transition()
.duration(duration)
.attr("y", x1);
}
boxTick.transition()
.duration(duration)
.text(format)
.attr("y", x1);
// Update whisker ticks. These are handled separately from the box
// ticks because they may or may not exist, and we want don't want
// to join box ticks pre-transition with whisker ticks post-.
var whiskerTick = g.selectAll("text.whisker")
.data(whiskerData || []);
if(showLabels == true) {
whiskerTick.enter().append("text")
.attr("class", "whisker")
.attr("dy", ".3em")
.attr("dx", 6)
.attr("x", width)
.attr("y", x0)
.text(format)
.style("opacity", 1e-6)
.transition()
.duration(duration)
.attr("y", x1)
.style("opacity", 1);
}
whiskerTick.transition()
.duration(duration)
.text(format)
.attr("y", x1)
.style("opacity", 1);
whiskerTick.exit().transition()
.duration(duration)
.attr("y", x1)
.style("opacity", 1e-6)
.remove();
});
d3.timer.flush();
}
box.width = function(x) {
if (!arguments.length) return width;
width = x;
return box;
};
box.height = function(x) {
if (!arguments.length) return height;
height = x;
return box;
};
box.tickFormat = function(x) {
if (!arguments.length) return tickFormat;
tickFormat = x;
return box;
};
box.duration = function(x) {
if (!arguments.length) return duration;
duration = x;
return box;
};
box.domain = function(x) {
if (!arguments.length) return domain;
domain = x == null ? x : d3.functor(x);
return box;
};
box.value = function(x) {
if (!arguments.length) return value;
value = x;
return box;
};
box.whiskers = function(x) {
if (!arguments.length) return whiskers;
whiskers = x;
return box;
};
box.showLabels = function(x) {
if (!arguments.length) return showLabels;
showLabels = x;
return box;
};
box.quartiles = function(x) {
if (!arguments.length) return quartiles;
quartiles = x;
return box;
};
return box;
};
function boxWhiskers(d) {
return [0, d.length - 1];
}
function boxQuartiles(d) {
return [
d3.quantile(d, .25),
d3.quantile(d, .5),
d3.quantile(d, .75)
];
}
})();
country ISO3136 contrcamsit contrses internetusers engprof engsep engint1 engint2
Kazakhstan KZ 0.413758185 0.935519875 9577924 47 1 1 0
Bangladesh BD 0.413758185 1.853657363 10637566 40 1 1 0
Nepal NP 0.674810535 1.367781217 10690000 35 1 1 0
Uzbekistan UZ 0.26105235 0.281855838 10948745 30 1 1 0
Venezuela VE 0.5221047 1.527202441 15624038 46 1 1 0
Saudi Arabia SA 0.5221047 1.206181693 16298448 40 1 1 0
Morocco MA 0.413758185 1.017622359 18283513 47 1 1 0
Thailand TH 0.413758185 1.446168863 19533675 45 1 1 0
Colombia CO 0.827516371 1.776714746 23650570 47 1 1 0
Iran IR 0.5221047 0.734014399 25074125 46 1 1 0
Pakistan PK 0.827516371 1.719393346 35063037 50 1 1 0
Turkey TR 0.5221047 1.737534408 37321199 48 1 1 0
Egypt EG 0.5221047 1.62707626 42271899 47 1 1 0
Mexico MX 0.99391895 1.894409239 51638402 51 1 1 0
Brazil BR 1.280955321 2.21206254 103720965 51 1 1 0
Maldives MV 0.26105235 2.237673176 173749 50 1 2 0
Malta MT 0 1.651517951 283427 45 1 2 0
Montenegro ME 0 1.947454711 371173 35 1 2 0
Mauritius MU 0 1.077296869 515673 50 1 2 0
Mongolia MN 0.26105235 2.062429966 521520 44 1 2 0
Nicaragua NI 0.413758185 1.297559401 897222 35 1 2 0
Cambodia KH 0.26105235 2.063503851 912332 39 1 2 0
Haiti HT 0 1.14606934 1048757 35 1 2 0
Kyrgyzstan KG 0.26105235 1.358531792 1298242 25 1 2 0
El Salvador SV 0 1.093821705 1411652 46 1 2 0
Armenia AM 0.26105235 1.378543665 1418756 35 1 2 0
Cameroon CM 0.413758185 1.441173222 1442210 40 1 2 0
Honduras HN 0.26105235 1.108729954 1503827 35 1 2 0
Panama PA 0.26105235 1.303909194 1526986 49 1 2 0
Qatar QA 0.26105235 1.409498806 1742205 44 1 2 0
Ethiopia ET 0.413758185 1.229414701 1783663 35 1 2 0
Albania AL 0.26105235 1.21246305 1809854 35 1 2 0
Afghanistan AF 0.26105235 0.725093778 1835377 35 1 2 0
Uruguay UY 0.26105235 1.337809658 1931511 50 1 2 0
Kuwait KW 0.26105235 1.104176126 2033885 43 1 2 0
Oman OM 0 0.779532623 2095922 46 1 2 0
Tanzania TZ 0.26105235 1.060073228 2123525 35 1 2 0
Georgia GE 0 1.286691442 2130070 35 1 2 0
Costa Rica CR 0.413758185 1.871825256 2158255 51 1 2 0
Paraguay PY 0.413758185 0.958512314 2443980 35 1 2 0
Senegal SN 0 0.540907737 2779786 35 1 2 0
Guatemala GT 0.26105235 1.214837297 2831574 50 1 2 0
Jordan JO 0.26105235 1.355351604 2865080 47 1 2 0
Lebanon LB 0.26105235 1.042978394 2912766 40 1 2 0
Iraq IQ 0.26105235 0.81773875 2930980 41 1 2 0
Croatia HR 0.413758185 2.332076587 2987663 50 1 2 0
Bulgaria BG 0.26105235 2.08829228 3704564 50 1 2 0
Serbia RS 0.606144786 2.833460897 3730149 45 1 2 0
Bolivia BO 0.413758185 0.955816977 4132116 35 1 2 0
Tunisia TN 0.26105235 1.398568267 4746112 45 1 2 0
Sri Lanka LK 0.5221047 1.408511048 4746967 48 1 2 0
United Arab Emirates AE 0.413758185 1.880896945 4817095 51 1 2 0
Belarus BY 0.413758185 2.114855026 5214344 35 1 2 0
Azerbaijan AZ 0.26105235 0.790862547 5629423 46 1 2 0
Syria SY 0 0.026625379 5883822 45 1 2 0
Algeria DZ 0.5221047 1.28564556 6284489 40 1 2 0
Sudan SD 0 0 7910476 40 1 2 0
Ukraine UA 1.019902971 2.817576877 18631600 53 2 0 1
Taiwan TW 0.26105235 2.478600371 18639773 53 2 0 1
Malaysia MY 0.674810535 1.961332103 19842134 60 2 0 1
South Africa ZA 0.5221047 1.882522553 23765937 90 2 0 1
Poland PL 0.903092754 2.480444632 24123917 63 2 0 1
Argentina AR 0.606144786 1.812549026 25523978 60 2 0 1
Italy IT 0.867197136 1.901306322 35942120 54 2 0 1
Spain ES 0.99391895 2.112001119 36721233 57 2 0 1
Philippines PH 0.78315705 2.137392909 39116638 90 2 0 1
Indonesia ID 0.78315705 1.644195407 39733532 53 2 0 1
Vietnam VN 0.5221047 1.787522351 40597779 54 2 0 1
South Korea KR 0.5221047 1.47734547 41499326 55 2 0 1
France FR 0.827516371 1.915522621 54027428 52 2 0 1
Nigeria NG 0.7328666 1.428596517 65675984 60 2 0 1
Germany DE 0.99391895 2.187733318 68131244 62 2 0 1
Russia RU 1.128249486 2.199958363 87495296 52 2 0 1
Japan JP 0.5221047 1.110871139 109755777 54 2 0 1
Barbados BB 0 1.598712203 216544 100 2 0 2
Guyana GY 0 1.650116563 243146 80 2 0 2
Iceland IS 0.26105235 1.824648318 304394 60 2 0 2
Cyprus CY 0.413758185 1.89228609 756267 60 2 0 2
Estonia EE 0.26105235 2.513663534 1013154 64 2 0 2
Jamaica JM 0.26105235 1.877249747 1099872 100 2 0 2
Slovenia SI 0.26105235 2.235159879 1448199 65 2 0 2
Latvia LV 0.26105235 1.894042511 1638939 57 2 0 2
Lithuania LT 0.26105235 2.252581097 2406707 55 2 0 2
Zimbabwe ZW 0.26105235 0.503986277 2438838 80 2 0 2
Ghana GH 0.26105235 1.651399673 3099552 60 2 0 2
New Zealand NZ 0.606144786 2.603470674 3613441 100 2 0 2
Ireland IE 0.5221047 2.703182215 3737096 100 2 0 2
Singapore SG 0 2.890630915 3986020 61 2 0 2
Slovakia SK 0.413758185 1.961962183 4274461 56 2 0 2
Dominican Republic DO 0.26105235 2.284797532 4690810 57 2 0 2
Finland FI 0.674810535 2.352761683 4819253 65 2 0 2
Norway NO 0.606144786 2.139410782 4834019 68 2 0 2
Denmark DK 0.5221047 2.118413978 5258054 70 2 0 2
Israel IL 0.5221047 2.570466905 5456586 60 2 0 2
Uganda UG 0.26105235 0.487426879 5630927 55 2 0 2
Ecuador EC 0.413758185 1.105955209 6230378 52 2 0 2
Greece GR 0.413758185 2.503304297 6449377 55 2 0 2
Austria AT 0.26105235 1.919635482 6628192 62 2 0 2
Portugal PT 0.5221047 2.439098292 6705872 61 2 0 2
Switzerland CH 0.606144786 1.861881019 6932555 58 2 0 2
Hungary HU 0.26105235 1.853894345 7220419 58 2 0 2
Czech Republic CZ 0.606144786 2.050627337 7862937 59 2 0 2
Belgium BE 0.413758185 2.166335046 8582076 59 2 0 2
Sweden SE 0.26105235 2.361138423 9144140 71 2 0 2
Romania RO 0.99391895 2.894145245 10843923 60 2 0 2
Chile CL 0.413758185 1.435302313 11449268 52 2 0 2
Peru PE 0.413758185 1.524110517 11700927 52 2 0 2
Netherlands NL 0.606144786 2.479699106 15789408 71 2 0 2
Kenya KE 0.413758185 1.654647618 17174686 55 2 0 2
Australia AU 0.903092754 2.841373455 18477876 100 2 0 2
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.axes path,
.axes line {
fill: none;
stroke:black;
shape-rendering: crispEdges;
}
.axes text {
font-family: sans-serif;
font-size: 11px;
}
.box {
font: 10px sans-serif;
}
.box line,
.box rect,
.box circle {
fill: steelblue;
stroke: #000;
stroke-width: 1px;
}
.box .center {
stroke-dasharray: 3,3;
}
.box .outlier {
fill: none;
stroke: #000;
}
</style>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.js"></script>
<script src="box.js"></script>
<script>
var margin = {top: 30, right: 10, bottom: 10, left: 10},
wfig2 = 200 - margin.right,
paddingfig = 60;
wfig1 = 960 - margin.left - paddingfig - wfig2,
hfig = 500 - margin.top - margin.bottom;
d3.csv("FCC_country_art_scatplot.csv", function(error, dataset){
//selecting svg
var svg = d3.select("body")
.append("svg")
.attr({
class: "scatplotcanvas",
width: margin.left + wfig1 + paddingfig + wfig2 + margin.right,
height: hfig + margin.top + margin.bottom
})
.append("g")
.attr({
transform:"translate(" + margin.left + "," + margin.top + ")"
});
//tooltips won't be a svg property in my example! so they have to be initialised apart because they are a HTML/CSS
//IT DOESN'T RAISE AN ERROR if appended directly to svg like svg.append("div"): it will create the markup BUT it won't be able to read it as svg method
var tooltip = d3.select("body")
.append("div") //<--- is a DIV (!)
.attr({
class: "tooltip"
})
.style({
opacity: "0.0",
position: "absolute",
textAlign: "center",
width: "60px",
//height: "28px",
//width: "65px",
height: "90px",
padding: "2px",
font: "12px sans-serif",
//font: "8px sans-serif",
background: "lightsteelblue",
border: "0px",
borderRadius: "8px",
pointerEvents: "none"
})
//scales, domains and ranges
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d){return d.contrcamsit})])
.range([margin.left + 40,wfig1]);
var yScale = d3.scale.linear()
.domain([0,d3.max(dataset, function(d){return d.contrses})])
.range([hfig - margin.bottom - 35, 20]);
//preparing data points: circles selection -> data binding -> entering placeholders -> updating
var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
//positioning with attr
circles
.attr({
class:"point",
cx: function(d){return xScale(d.contrcamsit)},
cy: function(d){return yScale(d.contrses)},
//r originally: 10 and 5
r: function(d){if(d.internetusers > 20000000){return 15}else{return 7}},
fill:function(d){if(d.engprof > 51){return "orange"}else{return "green"}}
})
.on("mouseover", function(d, i){
tooltip
.transition()
.duration(200)
.style({
opacity:"0.9"
});
tooltip
.html("<p>"+d.country+"</p><p>intpop. "+d.internetusers+"</p>")
.style({
left: (d3.event.pageX)+"px",
top: (d3.event.pageY - 28)+"px"
})
})
.on("mouseout", function(d){
tooltip
.transition()
.duration(500)
.style({
opacity:"0.0"
})
})
;
//labelling with text and attr
var pointlabel = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d){return d.ISO3136})
.attr({
class:"pointlabel",
//x originally xScale + sizecou
x: function(d){var sizecou; if(d.internetusers > 20000000){sizecou = 16}else{sizecou = 5}; return xScale(d.contrcamsit) + sizecou},
y: function(d){return yScale(d.contrses)+5}
})
//svg axes initialisation
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
//axes assigned to the canvas and modifications
svg.append("g")
.attr({
class:"axes",
transform: "translate(0,"+ (hfig - margin.bottom - 15) +")"
})
.call(xAxis);
svg.append("g")
.attr({
class:"axes",
transform: "translate("+ (margin.left+15) +", 0)"
})
.call(yAxis);
// boxplot, following http://bl.ocks.org/jensgrubert/7789216
var databox = new Array(2);
databox[0] = ["Hi_EPI", []];
databox[1] = ["Lo_EPI", []];
var min = Infinity,
max = -Infinity;
dataset.forEach(function(d){if(d.engprof > 47){databox[0][1].push(d.contrses)}else{databox[1][1].push(d.contrses)}});
//console.log("databox ", databox);
//databox.forEach(function(d){console.log(iqr(d,1.5)())});
// Returns a function to compute the interquartile range.
function iqr(k) {
return function(d, i) {
console.log(d);
var q1 = d.quartiles[0],
q3 = d.quartiles[2],
iqr = (q3 - q1) * k,
i = -1,
j = d.length;
while (d[++i] < q1 - iqr);
while (d[--j] > q3 + iqr);
return [i, j];
};
}
var box = d3.box()
.whiskers(iqr(1))
.height(hfig)
.domain(yScale.domain())
.showLabels(true);
//console.log(boxplot.whiskers())
var xScalebox = d3.scale.ordinal()
.domain(["0","1"])
.rangeRoundBands([ margin.left + wfig1 + paddingfig , margin.left + wfig1 + paddingfig + wfig2 ], 0.7, 1);
var boxplot = svg.selectAll(".boxplot")
.data(databox)
.enter()
.append("g");
boxplot
.call(box.width(xScalebox.rangeBand()))
.attr({
transform: function(d, i) { console.log(d[0]); return "translate(" + (wfig1 + paddingfig + i*60) + "," + margin.top + ")"; },
});
boxplot
.selectAll("line")
.attr({
fill:"black",
stroke:"#000"
});
boxplot
.selectAll("rect")
.attr({
fill:function(d, i){console.log("indexing ",d); if(d[1] > 1.7){return "orange"}else{return "green"}},
stroke: "#000",
strokeWidth: "1px"
})
.on("mouseover", function(d){
if(d[1] > 1.7){
circles
.transition()
.delay(200)
.attr({
opacity:function(dcir){if(dcir.engprof <= 51) return "0.2"}
});
pointlabel
.transition()
.delay(200)
.attr({
opacity: function(dcir){if(dcir.engprof <= 51) return "0.0"}
})
}else{
circles
.transition()
.delay(200)
.attr({
opacity:function(dcir){if(dcir.engprof > 51) return "0.2"}
});
pointlabel
.transition()
.delay(200)
.attr({
opacity: function(dcir){if(dcir.engprof > 51) return "0.0"}
})
}
})
.on("mouseout",function(d){
circles
.attr({
opacity:"1.0"
});
pointlabel
.attr({
opacity:"1.0"
})
});
boxplot
.selectAll("circle")
.attr("fill","steelblue");
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment