Last active
July 4, 2020 03:15
-
-
Save eightants/c7e876044de05a6fc9aa7a74bfa63921 to your computer and use it in GitHub Desktop.
D3 Customisable Dot Plot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Customizable Dot Plot (for D3 v3) | |
// by Anthony Teo | |
// adapted from code by Yan Holtz | |
/* | |
This function generates a responsive and customisable dot plot that can be used as | |
a lolipop plot, a dumbell plot, or other dot-plot-like graphs. | |
I needed a really lightweight dot plot function in D3 v3 that shows the domain in each plot without needing axes, | |
so I made this by adapting a basic Cleveland plot implementation that used D3 v4, and added configurations. | |
The config object is passed in for styling customisations. | |
*/ | |
function DotPlot(id, data, options) { | |
// set the default configs for the graph | |
// all these are customisable through passing in values for these fields in options | |
var cfg = { | |
margin: { top: 10, right: 30, bottom: 30, left: 30 }, | |
width: 500, // NOTE: width and height is only used for the aspect ratio of the graph container, the graph is always responsive | |
height: 200, | |
domain: [0, 1], // domain of x-axis, range of values of data | |
color: d3.scale.category10(), | |
radius: "6px", // radius of the dots | |
yoffset: 0, // if the plots aren't aligning with axes, change this | |
showAxes: true, | |
axesColor: "black", | |
axesWidth: "1px", | |
lineColor: "grey", //settings for the line that goes through all the points for each group | |
lineStroke: "2px", | |
lineToMatchAxes: false, // false means it is a regular Cleveland/Dumbell plot, | |
//if true, the line connecting all dots in a category is as long as the x-axis | |
// (useful if you are hiding the axes but still want to show the range) | |
}; | |
//Put all of the options into a variable called cfg | |
if ("undefined" !== typeof options) { | |
for (var i in options) { | |
if ("undefined" !== typeof options[i]) { | |
cfg[i] = options[i]; | |
} | |
} | |
} | |
// remove the previous graph in the container | |
d3.select(id).select("svg").remove(); | |
// append the svg object to the body of the page | |
var svg = d3 | |
.select(id) | |
.append("svg") | |
.attr("width", "100%") | |
.attr("height", "100%") | |
.attr( | |
"viewBox", | |
"0 0 " + cfg.width.toString() + " " + cfg.height.toString() | |
) | |
.attr("preserveAspectRatio", "xMinYMid") | |
.append("g") | |
.attr( | |
"transform", | |
"translate(" + cfg.margin.left + "," + cfg.margin.top + ")" | |
); | |
// Add X axis | |
var x = d3.scale | |
.linear() | |
.domain(cfg.domain) | |
.range([0, cfg.width - cfg.margin.left - cfg.margin.right]); | |
var xaxis = svg | |
.append("g") | |
.attr( | |
"transform", | |
"translate(0," + (cfg.height - cfg.margin.top - cfg.margin.bottom) + ")" | |
) | |
.attr("visibility", cfg.showAxes ? "visible" : "hidden") | |
.attr("stroke", cfg.axesColor) | |
.attr("fill", "none") | |
.attr("stroke-width", cfg.axesWidth) | |
.call(d3.svg.axis().scale(x)); | |
// Y axis | |
var y = d3.scale | |
.ordinal() | |
.rangeRoundBands([0, cfg.height - cfg.margin.top - cfg.margin.bottom]) | |
.domain( | |
data.map(function (d) { | |
return d.group; | |
}) | |
); | |
var yaxis = svg | |
.append("g") | |
.attr("visibility", cfg.showAxes ? "visible" : "hidden") | |
.attr("stroke", cfg.axesColor) | |
.attr("fill", "none") | |
.attr("stroke-width", cfg.axesWidth) | |
.call(d3.svg.axis().orient("left").scale(y)); | |
xaxis.selectAll("text").attr("fill", cfg.axesColor).attr("stroke-width", "0"); | |
yaxis.selectAll("text").attr("fill", cfg.axesColor).attr("stroke-width", "0"); | |
var keys = Object.keys(data[0]); | |
keys.splice(keys.indexOf("group"), 1); | |
// Lines | |
svg | |
.selectAll("myline") | |
.data(data) | |
.enter() | |
.append("line") | |
.attr("x1", function (d) { | |
return x(cfg.lineToMatchAxes ? cfg.domain[0] : d3.min(keys, (k) => d[k])); | |
}) // get min and max values for dumbell chart | |
.attr("x2", function (d) { | |
return x(cfg.lineToMatchAxes ? cfg.domain[1] : d3.max(keys, (k) => d[k])); | |
}) // if that is the config | |
.attr("y1", function (d) { | |
return (y(d.group) + cfg.yoffset); | |
}) | |
.attr("y2", function (d) { | |
return (y(d.group) + cfg.yoffset); | |
}) | |
.attr("stroke", cfg.lineColor) | |
.attr("stroke-width", cfg.lineStroke); | |
// gets all the circles on the plot | |
var circles = Object.keys(data[0]); | |
for (var i = 0; i < circles.length; i++) { | |
if (circles[i] != "group") { | |
// plot circles | |
svg | |
.selectAll("mycircle") | |
.data(data) | |
.enter() | |
.append("circle") | |
.attr("cx", function (d) { | |
return x(d[circles[i]]); | |
}) | |
.attr("cy", function (d) { | |
return (y(d.group) + cfg.yoffset); | |
}) | |
.attr("r", cfg.radius) | |
.style("fill", cfg.color(i - 1)); // takes the color of previous index because the chart title Group is one of the keys | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | |
<title>Customisable Dot Plot</title> | |
<script src="https://d3js.org/d3.v3.min.js"></script> | |
<script src="DotPlot.js"></script> | |
<style> | |
body { | |
overflow: hidden; | |
margin: 0; | |
font-size: 14px; | |
font-family: "Helvetica Neue", Helvetica; | |
} | |
#chart { | |
position: absolute; | |
width: 500px; | |
height: 300px; | |
top: 50px; | |
left: 100px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="body"> | |
<div id="chart"></div> | |
</div> | |
<script type="text/javascript" src="script.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var data = [ | |
{ | |
"group": "acousticness", | |
"Malaysia": 0.28191525423728814, | |
"Netherlands": 0.21346521739130436, | |
"United States": 0.23971917082765926 | |
}, | |
{ | |
"group": "danceability", | |
"Malaysia": 0.7773728813559321, | |
"Netherlands": 0.5810695652173913, | |
"United States": 0.6824333484642155 | |
}, | |
{ | |
"group": "energy", | |
"Malaysia": 0.5231186440677967, | |
"Netherlands": 0.6700869565217391, | |
"United States": 0.6471245271599334 | |
}, | |
{ | |
"group": "valence", | |
"Malaysia": 0.4376779661016949, | |
"Netherlands": 0.5521521739130435, | |
"United States": 0.4609040702072931 | |
}, | |
{ | |
"group": "liveness", | |
"Malaysia": 0.18754237288135592, | |
"Netherlands": 0.39381739130434783, | |
"United States": 0.59579073990013618 | |
}, | |
{ | |
"group": "speechiness", | |
"Malaysia": 0.08032203389830508, | |
"Netherlands": 0.10898260869565216, | |
"United States": 0.20012619155696778 | |
} | |
] | |
var config = { | |
margin: { top: 10, right: 30, bottom: 30, left: 120 }, | |
showAxes: true, | |
width: 500, | |
height: 300, | |
radius: "5px", | |
yoffset: 20, | |
lineStroke: "2px", | |
lineToMatchAxes: false, | |
} | |
DotPlot("#chart", data, config); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment