Visualization of non-negative distribution plots in d3.
Powered using d3.js, jStat, and d3-legend.
Built with blockbuilder.org.
forked from SpaceActuary's block: Distributions
Visualization of non-negative distribution plots in d3.
Powered using d3.js, jStat, and d3-legend.
Built with blockbuilder.org.
forked from SpaceActuary's block: Distributions
| var gammaDist = function(){ | |
| var gamma = {}, | |
| shape, | |
| scale; | |
| var labeler = function(shape, scale){ | |
| return "Gamma(α = " + d3.round(shape, 3) + ", " + | |
| "θ = " + d3.round(scale, 3) + ")"; | |
| } | |
| gamma.shape = function(_) { | |
| if (!arguments.length) return shape; | |
| shape = _; | |
| gamma.label = labeler(shape, scale); | |
| return gamma; | |
| }; | |
| gamma.scale = function(_) { | |
| if (!arguments.length) return scale; | |
| scale = _; | |
| gamma.label = labeler(shape, scale); | |
| return gamma; | |
| }; | |
| gamma.mean = function(_) { | |
| if (!arguments.length) return jStat.gamma.mean(shape, scale); | |
| scale = _ / shape; | |
| gamma.label = labeler(shape, scale); | |
| return gamma; | |
| }; | |
| gamma.pdf = function(x){ return jStat.gamma.pdf(x, shape, scale); } | |
| gamma.cdf = function(x){ return jStat.gamma.cdf(x, shape, scale); } | |
| gamma.inv = function(p){ return jStat.gamma.inv(p, shape, scale); } | |
| return gamma; | |
| } | |
| var lognormalDist = function(){ | |
| var lognormal = {}, | |
| mu, | |
| sigma; | |
| var labeler = function(mu, sigma){ | |
| return "Lognormal(μ = " + d3.round(mu, 3) + ", " + | |
| "σ = " + d3.round(sigma, 3) + ")"; | |
| } | |
| lognormal.mu = function(_) { | |
| if (!arguments.length) return mu; | |
| mu = _; | |
| lognormal.label = labeler(mu, sigma); | |
| return lognormal; | |
| }; | |
| lognormal.sigma = function(_) { | |
| if (!arguments.length) return sigma; | |
| sigma = _; | |
| lognormal.label = labeler(mu, sigma); | |
| return lognormal; | |
| }; | |
| lognormal.mean = function(_) { | |
| if (!arguments.length) return jStat.lognormal.mean(mu, sigma); | |
| mu = Math.log(_) - (sigma * sigma) / 2; | |
| lognormal.label = labeler(mu, sigma); | |
| return lognormal; | |
| }; | |
| lognormal.pdf = function(x){ return jStat.lognormal.pdf(x, mu, sigma); } | |
| lognormal.cdf = function(x){ return jStat.lognormal.cdf(x, mu, sigma); } | |
| lognormal.inv = function(p){ return jStat.lognormal.inv(p, mu, sigma); } | |
| return lognormal; | |
| } | |
| var paretoDist = function(){ | |
| var pareto = {}, | |
| scale, | |
| shape; | |
| var labeler = function(scale, shape){ | |
| return "Pareto(x_m = " + d3.round(scale, 3) + ", " + | |
| "α = " + d3.round(shape, 3) + ")"; | |
| } | |
| pareto.shape = function(_) { | |
| if (!arguments.length) return shape; | |
| shape = _; | |
| pareto.label = labeler(scale, shape); | |
| return pareto; | |
| }; | |
| pareto.scale = function(_) { | |
| if (!arguments.length) return scale; | |
| scale = _; | |
| pareto.label = labeler(scale, shape); | |
| return pareto; | |
| }; | |
| pareto.mean = function(_) { | |
| if (!arguments.length) return jStat.pareto.mean(scale, shape); | |
| scale = _ * (shape - 1) / shape; | |
| pareto.label = labeler(scale, shape); | |
| return pareto; | |
| }; | |
| pareto.pdf = function(x){ return jStat.pareto.pdf(x, scale, shape); } | |
| pareto.cdf = function(x){ return jStat.pareto.cdf(x, scale, shape); } | |
| pareto.inv = function(p){ return jStat.pareto.inv(p, scale, shape); } | |
| return pareto; | |
| } | |
| var weibullDist = function(){ | |
| var weibull = {}, | |
| scale, | |
| shape; | |
| var labeler = function(scale, shape){ | |
| return "Weibull(λ = " + d3.round(scale, 3) + ", " + | |
| "k = " + d3.round(shape, 3) + ")"; | |
| } | |
| weibull.shape = function(_) { | |
| if (!arguments.length) return shape; | |
| shape = _; | |
| weibull.label = labeler(scale, shape); | |
| return weibull; | |
| }; | |
| weibull.scale = function(_) { | |
| if (!arguments.length) return scale; | |
| scale = _; | |
| weibull.label = labeler(scale, shape); | |
| return weibull; | |
| }; | |
| weibull.mean = function(_) { | |
| if (!arguments.length) return jStat.weibull.mean(scale, shape); | |
| scale = _ / jStat.gammafn(1 + 1/shape); | |
| weibull.label = labeler(scale, shape); | |
| return weibull; | |
| }; | |
| weibull.pdf = function(x){ return jStat.weibull.pdf(x, scale, shape); } | |
| weibull.cdf = function(x){ return jStat.weibull.cdf(x, scale, shape); } | |
| weibull.inv = function(p){ return jStat.weibull.inv(p, scale, shape); } | |
| return weibull; | |
| } | |
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/jstat/1.5.0/jstat.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.6.0/d3-legend.js"></script> | |
| <script src="distributions.js"></script> | |
| <style> | |
| body { | |
| margin: 0; | |
| position: fixed; | |
| top: 0; right: 0; bottom: 0; left: 0; | |
| font-family: Arial, Helvetica, sans-serif | |
| } | |
| svg { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| path { | |
| stroke-width: 1; | |
| fill: none; | |
| } | |
| .axis { | |
| /*stroke: lightgrey;*/ | |
| shape-rendering: crispEdges; | |
| } | |
| .x.axis line { stroke: lightgrey; } | |
| .x.axis .minor { stroke-opacity: .5; } | |
| .x.axis path { stroke: lightgrey; } | |
| .y.axis line, .y.axis path { | |
| fill: none; | |
| stroke: lightgrey; | |
| } | |
| .handle path { | |
| fill: silver; | |
| stroke: #000; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script> | |
| var margin = {top: 60, right: 20, bottom: 60, left: 50, inner: 60}; | |
| var width = 960 - margin.left - margin.inner - margin.right; | |
| var height = 500 - margin.top - margin.bottom; | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| var svg1 = svg.append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| var svg2 = svg.append("g") | |
| .attr("transform", "translate(" + (width/2 + margin.left + margin.inner) | |
| + "," + margin.top + ")"); | |
| var i = 300; | |
| var p = 0.01; | |
| var dists = [ | |
| /*gamma(1, 2), | |
| gamma(2, 2), | |
| gamma(3, 2),*/ | |
| gammaDist().shape(2.5).scale(2), | |
| //gammaDist().shape(5).scale(1), | |
| //gammaDist().shape(10).scale(0.5), | |
| //gammaDist().shape(20).scale(0.25), | |
| /*gamma(9, 0.5), | |
| gamma(7.5, 1)*/ | |
| lognormalDist().mu(Math.log(5)-(0.25^2)/2).sigma(0.25), | |
| //lognormalDist().mu(Math.log(5)-(0.5^2)/2).sigma(0.5), | |
| paretoDist().shape(2.5).scale(2), | |
| weibullDist().shape(1.5).mean(5), | |
| weibullDist().shape(2.5).mean(5) | |
| ] | |
| //console.log("pareto mean", jStat.pareto.mean(2.5, 2)) | |
| var x = d3.scale.linear() | |
| .range([0, width / 2]) | |
| .domain([0, d3.max(dists.map(function(dist){ | |
| return dist.inv(1 - p); | |
| }))]) | |
| .nice() | |
| .clamp(true); | |
| var points = d3.range(x.domain()[0], x.domain()[1], | |
| (x.domain()[1] - x.domain()[0]) / i); | |
| var y1 = d3.scale.linear() | |
| .range([height, 0]) | |
| .domain([0, d3.max(dists.map(function(dist){ | |
| return d3.max(points, function(pt){ return dist.pdf(pt); }) | |
| }))]) | |
| .nice(); | |
| var y2 = d3.scale.linear() | |
| .range([height, 0]) | |
| .domain([0, 1]); | |
| var color = d3.scale.category10(); | |
| var xAxis = d3.svg.axis().scale(x).orient("bottom"); | |
| svg1.append("g") | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(xAxis); | |
| svg2.append("g") | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(xAxis); | |
| var y1Axis = d3.svg.axis().scale(y1).orient("left"); | |
| var y2Axis = d3.svg.axis().scale(y2).orient("left"); | |
| svg1.append("g") | |
| .attr("class", "y y1 axis") | |
| .attr("transform", "translate(0,0)") | |
| .call(y1Axis); | |
| svg2.append("g") | |
| .attr("class", "y y2 axis") | |
| .attr("transform", "translate(0,0)") | |
| .call(y2Axis); | |
| var brush = d3.svg.brush() | |
| .x(x) | |
| .extent([0, 0]) | |
| .on("brush", brushed) | |
| .on("brushend", brushended); | |
| var pdf_line = function(dist){ | |
| return d3.svg.line() | |
| .x(function(d) { return x(d); }) | |
| .y(function(d) { return y1(dist.pdf(d)); }) | |
| .interpolate("linear"); | |
| } | |
| var cdf_line = function(dist){ | |
| return d3.svg.line() | |
| .x(function(d) { return x(d); }) | |
| .y(function(d) { return y2(dist.cdf(d)); }) | |
| .interpolate("linear"); | |
| } | |
| var pdf_lines = dists.map(pdf_line); | |
| var cdf_lines = dists.map(cdf_line); | |
| svg1.selectAll("path.pdf") | |
| .data(pdf_lines) | |
| .enter() | |
| .append("path") | |
| .attr("class", "pdf") | |
| .attr("d", function(dist){ return dist(points); }) | |
| .attr("stroke", function(dist, i){ return color(i); }); | |
| svg2.selectAll("path.cdf") | |
| .data(cdf_lines) | |
| .enter() | |
| .append("path") | |
| .attr("class", "cdf") | |
| .attr("d", function(dist){ return dist(points); }) | |
| .attr("stroke", function(dist, i){ return color(i); }); | |
| svg1.append("g") | |
| .attr("class", "legendOrdinal") | |
| .attr("transform", "translate(" + (width / 5) + ",20)"); | |
| var legendOrdinal = d3.legend.color() | |
| .shape("circle") | |
| .shapeRadius(7) | |
| .shapePadding(10) | |
| .scale(color) | |
| .labels(dists.map(function(dist){ return dist.label; })); | |
| svg.select(".legendOrdinal") | |
| .call(legendOrdinal); | |
| var slider = svg1.append("g") | |
| .attr("class", "slider") | |
| .call(brush); | |
| slider.selectAll(".extent,.resize") | |
| .remove(); | |
| slider.select(".background") | |
| .attr("height", height); | |
| var handle = slider.append("g") | |
| .attr("class", "handle") | |
| handle.append("path") | |
| .attr("d", d3.svg.symbol().type("triangle-up").size(200)) | |
| .attr("transform", "translate(0," + (height + margin.bottom * 0.6) + ")") | |
| slider | |
| .call(brush.extent([5, 5])) | |
| .call(brush.event); | |
| function redraw(value, duration){ | |
| dists = dists.map(function(d, i){ | |
| //console.log(i, d); | |
| d.mean(value); | |
| return d; | |
| }) | |
| pdf_lines = dists.map(pdf_line); | |
| cdf_lines = dists.map(cdf_line); | |
| svg1.selectAll("path.pdf") | |
| .data(pdf_lines) | |
| .transition().duration(duration) | |
| .attr("d", function(dist){ return dist(points); }); | |
| svg2.selectAll("path.cdf") | |
| .data(cdf_lines) | |
| .transition().duration(duration) | |
| .attr("d", function(dist){ return dist(points); }); | |
| svg.select(".legendOrdinal") | |
| .call(legendOrdinal.labels(dists.map(function(dist){ | |
| return dist.label; | |
| }))); | |
| } | |
| function rescale(value, duration){ | |
| handle.transition().duration(duration) | |
| .attr("transform", "translate(" + x(value) + ",0)"); | |
| /* | |
| y1.domain([0, d3.max(dists.map(function(dist){ | |
| return d3.max(points, function(pt){ return dist.pdf(pt); }) | |
| }))]); | |
| y1Axis.scale(y1); | |
| svg.selectAll("g.y1.axis").transition() | |
| .delay(duration) | |
| .duration(duration) | |
| .call(y1Axis); | |
| */ | |
| } | |
| function brushed() { | |
| var value = brush.extent()[0]; | |
| //console.log(value) | |
| if (d3.event.sourceEvent) { // not a programmatic event | |
| value = x.invert(d3.mouse(this)[0]); | |
| brush.extent([value, value]); | |
| } | |
| handle | |
| .attr("transform", "translate(" + x(value) + ",0)"); | |
| redraw(value, 0) | |
| } | |
| function brushended() { | |
| var value = brush.extent()[0]; | |
| if (!d3.event.sourceEvent) return; // only transition after input | |
| x = d3.scale.linear() | |
| .range([0, width / 2]) | |
| .domain([0, d3.max(dists.map(function(dist){ | |
| return dist.inv(1 - p); | |
| }))]) | |
| .nice() | |
| .clamp(true); | |
| points = d3.range(x.domain()[0], x.domain()[1], | |
| (x.domain()[1] - x.domain()[0]) / i); | |
| xAxis.scale(x); | |
| svg.selectAll("g.x.axis").transition().duration(1500).call(xAxis); | |
| rescale(value, 1500) | |
| redraw(value, 1500) | |
| } | |
| </script> | |
| </body> |