Skip to content

Instantly share code, notes, and snippets.

@mikelotis
Last active August 9, 2018 23:08
Show Gist options
  • Save mikelotis/fa824557ccbb76051b0ce7e382723f4a to your computer and use it in GitHub Desktop.
Save mikelotis/fa824557ccbb76051b0ce7e382723f4a to your computer and use it in GitHub Desktop.
Edmonton - 311 Requests - Joy Plot
width: 720
height: 660
{
"name": "data",
"children": [
{
"name": 2014,
"children": [
{
"name": "Jan",
"value": 14289
},
{
"name": "Feb",
"value": 2054
},
{
"name": "Mar",
"value": 4951
},
{
"name": "Apr",
"value": 4832
},
{
"name": "May",
"value": 4676
},
{
"name": "Jun",
"value": 5543
},
{
"name": "Jul",
"value": 6634
},
{
"name": "Aug",
"value": 4415
},
{
"name": "Sep",
"value": 3746
},
{
"name": "Oct",
"value": 3040
},
{
"name": "Nov",
"value": 2642
},
{
"name": "Dec",
"value": 4936
}
]
},
{
"name": 2015,
"children": [
{
"name": "Jan",
"value": 5195
},
{
"name": "Feb",
"value": 5062
},
{
"name": "Mar",
"value": 7702
},
{
"name": "Apr",
"value": 5323
},
{
"name": "May",
"value": 5212
},
{
"name": "Jun",
"value": 5284
},
{
"name": "Jul",
"value": 4861
},
{
"name": "Aug",
"value": 3922
},
{
"name": "Sep",
"value": 3657
},
{
"name": "Oct",
"value": 3069
},
{
"name": "Nov",
"value": 2602
},
{
"name": "Dec",
"value": 1788
}
]
},
{
"name": 2016,
"children": [
{
"name": "Jan",
"value": 2368
},
{
"name": "Feb",
"value": 3561
},
{
"name": "Mar",
"value": 2815
},
{
"name": "Apr",
"value": 4199
},
{
"name": "May",
"value": 4214
},
{
"name": "Jun",
"value": 5182
},
{
"name": "Jul",
"value": 5269
},
{
"name": "Aug",
"value": 4805
},
{
"name": "Sep",
"value": 3591
},
{
"name": "Oct",
"value": 2887
},
{
"name": "Nov",
"value": 2476
},
{
"name": "Dec",
"value": 1923
}
]
},
{
"name": 2017,
"children": [
{
"name": "Jan",
"value": 3160
},
{
"name": "Feb",
"value": 3194
},
{
"name": "Mar",
"value": 6309
},
{
"name": "Apr",
"value": 6894
},
{
"name": "May",
"value": 10683
},
{
"name": "Jun",
"value": 11066
},
{
"name": "Jul",
"value": 8365
},
{
"name": "Aug",
"value": 8092
},
{
"name": "Sep",
"value": 6859
},
{
"name": "Oct",
"value": 6156
},
{
"name": "Nov",
"value": 5897
},
{
"name": "Dec",
"value": 7784
}
]
},
{
"name": 2018,
"children": [
{
"name": "Jan",
"value": 6103
},
{
"name": "Feb",
"value": 8713
},
{
"name": "Mar",
"value": 10029
},
{
"name": "Apr",
"value": 9436
},
{
"name": "May",
"value": 10612
},
{
"name": "Jun",
"value": 11233
},
{
"name": "Jul",
"value": 5524
}
]
}
]
}
svg {
display: block;
margin: 0 auto;
background-color: whitesmoke;
margin-top: 30px;
}
text{
font: 1em 'Roboto', sans-serif;
}
.axis .domain {
display: none;
}
.axis--x text, .text--title{
fill: #999;
}
.text--title {
font-size: 1.2em;
}
.axis--x line {
stroke: #aaa;
}
.axis--year .tick line {
display: none;
}
.axis--year text {
font-size: 1.1em;
fill: #777;
}
.axis--year .tick:nth-child(odd) text {
fill: #222;
}
.line {
fill: none;
stroke: whitesmoke;
stroke-width: 1.5px;
}
.area {
fill: #448cab;
opacity: 0.9;
}
.year:nth-child(odd) .area {
fill: #5ca3c1;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link rel="stylesheet" href="index.css">
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.5.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="index.js" lang="babel" type="text/babel"></script>
</body>
</html>
const log = console.log;
const margin = {top: 150, right: 0, bottom: 20, left: 80};
const width = 700 - margin.left - margin.right;
const height = 600 - margin.top - margin.bottom;
// define svg
const svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// load pre-processed data
d3.json("311-Flattened-(July-20-2018).json")
.then(ridgelinePlot);
// Creates the ridgeline plot/ joy plot
function ridgelinePlot(responses) {
// accessors and pre-processed data
const data = responses.children;
const accessorName = d => d.name;
const accesorValue = d => d.value;
const overlap = 1.5;
const allValues = data.map(d => { return d.children.map(accesorValue); })
.reduce((acc, curVal) => acc.concat(curVal), []);
// chart x axis
const xScale = d3.scaleBand().domain(data[0].children.map(accessorName)).range([0, width]);
const xValue = d => xScale(accessorName(d));
const xAxis = d3.axisBottom(xScale);
// chart y axis
const yearScale = d3.scaleBand().domain(data.map(accessorName)).range([height, 0]);
const yearValue = d => yearScale(accessorName(d));
const yearAxis = d3.axisLeft(yearScale);
// area scale
const areaChartHeight = (1 + overlap) * (height / yearScale.domain().length);
const yScale = d3.scaleLinear().domain(d3.extent(allValues)).range([areaChartHeight, 0]);
const yValue = d => yScale(accesorValue(d));
// area and line generators
const area = d3.area().x(xValue).y1(yValue).curve(d3.curveBasis);
const line = area.lineY1();
// ordered to get a pleasing overlapping effect
// However 2016 area is still behind 2017 area - d3 raise solves the issue
// Further learning required to avoid this
// https://beta.observablehq.com/@mbostock/d3-ridgeline-plot
// https://beta.observablehq.com/@pstuffa/nyc-building-permits-api-neighborhood-trends
const order = [2016, 2015, 2014, 2018, 2017].map(d => data.filter(p => p.name == d)[0]);
// year groups
const gYear= svg.append('g').attr("class", "years")
.selectAll(".year").data(order)
.enter().append('g')
.attr("class",d => "year year--" + d.name)
.attr("transform", (d) => {
const ty = yearValue(d) - yearScale.bandwidth() - 110;
return `translate(0,${ty})`;
});
area.y0(yScale(0));
// add x axis
svg.append('g').attr("class", "axis axis--x")
.attr("transform", `translate(-25,${height - 30})`)
.call(xAxis);
// add y axis
svg.append('g').attr("class", "axis axis--year")
.attr("transform", "translate(0,2)")
.call(yearAxis);
// add area paths
gYear.append('path').attr('class', 'area')
.datum(d => d.children)
.attr('d', area);
// add line paths
gYear.append('path').attr('class', 'line')
.datum(d => d.children)
.attr('d', line);
// Add title texts
svg.append("text")
.attr("class", "text--title")
.attr("x", 400)
.attr("y", -60)
.html("311 Requests");
svg.append("text")
.attr("class", "text--title")
.attr("x", 400)
.attr("y", -38)
.html("Yearly Trends");
// gets the desired overlapping effect
order.slice(0, 3).forEach(d => {
d3.select(`.year--${d.name}`).raise();
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment