Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active January 27, 2022 17:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbostock/4349187 to your computer and use it in GitHub Desktop.
Save mbostock/4349187 to your computer and use it in GitHub Desktop.
Q-Q Plots
license: gpl-3.0
redirect: https://observablehq.com/@d3/q-q-plot
(function(){function t(t,n){var r=n.length-1;return n=n.slice().sort(d3.ascending),d3.range(t).map(function(a){return n[~~(a*r/t)]})}function n(t){return t.x}function r(t){return t.y}d3.qq=function(){function a(n){n.each(function(n,r){var a,y,g=d3.select(this),f=t(s,l.call(this,n,r)),m=t(s,d.call(this,n,r)),x=o&&o.call(this,n,r)||[d3.min(f),d3.max(f)],h=o&&o.call(this,n,r)||[d3.min(m),d3.max(m)],p=d3.scale.linear().domain(x).range([0,e]),v=d3.scale.linear().domain(h).range([i,0]);this.__chart__?(a=this.__chart__.x,y=this.__chart__.y):(a=d3.scale.linear().domain([0,1/0]).range(p.range()),y=d3.scale.linear().domain([0,1/0]).range(v.range())),this.__chart__={x:p,y:v};var _=g.selectAll("line.diagonal").data([null]);_.enter().append("svg:line").attr("class","diagonal").attr("x1",p(h[0])).attr("y1",v(x[0])).attr("x2",p(h[1])).attr("y2",v(x[1])),_.transition().duration(c).attr("x1",p(h[0])).attr("y1",v(x[0])).attr("x2",p(h[1])).attr("y2",v(x[1]));var k=g.selectAll("circle").data(d3.range(s).map(function(t){return{x:f[t],y:m[t]}}));k.enter().append("svg:circle").attr("class","quantile").attr("r",4.5).attr("cx",function(t){return a(t.x)}).attr("cy",function(t){return y(t.y)}).style("opacity",1e-6).transition().duration(c).attr("cx",function(t){return p(t.x)}).attr("cy",function(t){return v(t.y)}).style("opacity",1),k.transition().duration(c).attr("cx",function(t){return p(t.x)}).attr("cy",function(t){return v(t.y)}).style("opacity",1),k.exit().transition().duration(c).attr("cx",function(t){return p(t.x)}).attr("cy",function(t){return v(t.y)}).style("opacity",1e-6).remove();var A=u||p.tickFormat(4),q=u||v.tickFormat(4),F=function(t){return"translate("+p(t)+","+i+")"},C=function(t){return"translate(0,"+v(t)+")"},w=g.selectAll("g.x.tick").data(p.ticks(4),function(t){return this.textContent||A(t)}),b=w.enter().append("svg:g").attr("class","x tick").attr("transform",function(t){return"translate("+a(t)+","+i+")"}).style("opacity",1e-6);b.append("svg:line").attr("y1",0).attr("y2",-6),b.append("svg:text").attr("text-anchor","middle").attr("dy","1em").text(A),b.transition().duration(c).attr("transform",F).style("opacity",1),w.transition().duration(c).attr("transform",F).style("opacity",1),w.exit().transition().duration(c).attr("transform",F).style("opacity",1e-6).remove();var j=g.selectAll("g.y.tick").data(v.ticks(4),function(t){return this.textContent||q(t)}),z=j.enter().append("svg:g").attr("class","y tick").attr("transform",function(t){return"translate(0,"+y(t)+")"}).style("opacity",1e-6);z.append("svg:line").attr("x1",0).attr("x2",6),z.append("svg:text").attr("text-anchor","end").attr("dx","-.5em").attr("dy",".3em").text(q),z.transition().duration(c).attr("transform",C).style("opacity",1),j.transition().duration(c).attr("transform",C).style("opacity",1),j.exit().transition().duration(c).attr("transform",C).style("opacity",1e-6).remove()})}var e=1,i=1,c=0,o=null,u=null,s=100,l=n,d=r;return a.width=function(t){return arguments.length?(e=t,a):e},a.height=function(t){return arguments.length?(i=t,a):i},a.duration=function(t){return arguments.length?(c=t,a):c},a.domain=function(t){return arguments.length?(o=null==t?t:d3.functor(t),a):o},a.count=function(t){return arguments.length?(s=t,a):s},a.x=function(t){return arguments.length?(l=t,a):l},a.y=function(t){return arguments.length?(d=t,a):d},a.tickFormat=function(t){return arguments.length?(u=t,a):u},a}})();
<!DOCTYPE html>
<meta charset="utf-8">
<title>Q-Q Plots</title>
<style>
body {
font: 10px sans-serif;
width: 960px;
height: 310px;
}
.qq .box,
.qq .tick line,
.qq .quantile,
.qq .diagonal {
stroke: #aaa;
fill: none;
}
.qq .quantile {
stroke: #000;
}
.qq g + g .y.tick {
display: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="d3.qq.min.js"></script>
<script>
var width = 270,
height = 270,
margin = {top: 20, right: 10, bottom: 20, left: 35},
n = 10000; // number of samples to generate
var chart = d3.qq()
.width(width)
.height(height)
.domain([-.1, 1.1])
.tickFormat(function(d) { return ~~(d * 100); });
var vis = d3.select("body").append("svg")
.attr("width", (width + margin.right + margin.left) * 3)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("turkers.json", function(error, turkers) {
if (error) throw error;
var tm = mean(turkers),
td = Math.sqrt(variance(turkers)),
dd = [
[0.10306430789206111, 0.0036139086950272735, 0.30498647327844536],
[0.5924252668569606, 0.0462763685758622, 0.4340870312025223],
[0.9847627827855167, 2.352350767874714e-4, 0.2609264955190324]
];
var g = vis.selectAll("g")
.data([{
x: d3.range(n).map(Math.random),
y: turkers,
label: "Uniform Distribution"
}, {
x: d3.range(n).map(normal1(tm, td)),
y: turkers,
label: "Gaussian (Normal) Distribution"
}, {
x: d3.range(n).map(normal3(dd)),
y: turkers,
label: "Mixture of 3 Gaussians"
}])
.enter().append("g")
.attr("class", "qq")
.attr("transform", function(d, i) { return "translate(" + (width + margin.right + margin.left) * i + ")"; });
g.append("rect")
.attr("class", "box")
.attr("width", width)
.attr("height", height);
g.call(chart);
g.append("text")
.attr("dy", "1.3em")
.attr("dx", ".6em")
.text(function(d) { return d.label; });
chart.duration(1000);
window.transition = function() {
g.datum(randomize).call(chart);
};
});
function randomize(d) {
d.y = d3.range(n).map(Math.random);
return d;
}
// Sample from a normal distribution with mean 0, stddev 1.
function normal() {
var x = 0, y = 0, rds, c;
do {
x = Math.random() * 2 - 1;
y = Math.random() * 2 - 1;
rds = x * x + y * y;
} while (rds == 0 || rds > 1);
c = Math.sqrt(-2 * Math.log(rds) / rds); // Box-Muller transform
return x * c; // throw away extra sample y * c
}
// Simple 1D Gaussian (normal) distribution
function normal1(mean, deviation) {
return function() {
return mean + deviation * normal();
};
}
// Gaussian Mixture Model (k=3) fit using E-M algorithm
function normal3(dd) {
return function() {
var r = Math.random(),
i = r < dd[0][2] ? 0 : r < dd[0][2] + dd[1][2] ? 1 : 2,
d = dd[i];
return d[0] + Math.sqrt(d[1]) * normal();
}
}
// Welford's algorithm.
function mean(x) {
var n = x.length;
if (n === 0) return NaN;
var m = 0,
i = -1;
while (++i < n) m += (x[i] - m) / (i + 1);
return m;
}
// Unbiased estimate of a sample's variance.
// Also known as the sample variance, where the denominator is n - 1.
function variance(x) {
var n = x.length;
if (n < 1) return NaN;
if (n === 1) return 0;
var m = mean(x),
i = -1,
s = 0;
while (++i < n) {
var v = x[i] - m;
s += v * v;
}
return s / (n - 1);
}
</script>
[0.009259259,0.014285714,0.014285714,0.016666667,0.016666667,0.017857143,0.018518519,0.027777778,0.028571429,0.028571429,0.028571429,0.033333333,0.033333333,0.035714286,0.0375,0.041666667,0.041666667,0.041666667,0.041666667,0.042857143,0.042857143,0.042857143,0.05,0.055555556,0.069444444,0.083333333,0.083333333,0.083333333,0.083333333,0.083333333,0.083333333,0.085714286,0.1,0.1,0.101851852,0.104166667,0.111111111,0.111111111,0.114285714,0.114285714,0.116666667,0.12037037,0.125,0.125,0.128571429,0.133333333,0.138888889,0.141666667,0.142857143,0.142857143,0.15,0.152777778,0.158333333,0.166666667,0.171428571,0.183333333,0.185714286,0.185714286,0.1875,0.190140845,0.194444444,0.2,0.204545455,0.208333333,0.214285714,0.214285714,0.253521127,0.271428571,0.277777778,0.291666667,0.3,0.3,0.307017544,0.324074074,0.328571429,0.333333333,0.333333333,0.342857143,0.357142857,0.358333333,0.378787879,0.381355932,0.395833333,0.4,0.414285714,0.414285714,0.414285714,0.414285714,0.43,0.433333333,0.4375,0.445833333,0.450704225,0.453333333,0.458333333,0.466666667,0.476666667,0.494736842,0.5,0.516666667,0.533333333,0.55,0.557142857,0.56884058,0.569444444,0.571428571,0.585714286,0.61,0.622222222,0.657407407,0.666666667,0.678947368,0.685714286,0.685714286,0.69047619,0.7,0.7,0.7,0.711538462,0.763888889,0.771428571,0.788888889,0.8,0.8,0.808333333,0.824712644,0.828571429,0.836842105,0.839285714,0.839285714,0.84,0.842857143,0.842857143,0.842857143,0.85,0.859649123,0.869791667,0.871428571,0.871428571,0.892344498,0.914285714,0.928571429,0.933908046,0.953703704,0.973684211,0.975,0.981481481,0.983333333,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.985714286,0.987096774,0.990740741,0.991666667,0.992,0.994047619,0.996666667,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
@pombredanne
Copy link

Is this GPL or ISC licensed?
Here the Gist says GPL. But the link at https://gist.github.com/mbostock/4349187 says ISC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment