Skip to content

Instantly share code, notes, and snippets.

@Zhenmao
Created September 11, 2017 03:09
Show Gist options
  • Save Zhenmao/fe05fd4811e0e601ab36a3319b1cd99a to your computer and use it in GitHub Desktop.
Save Zhenmao/fe05fd4811e0e601ab36a3319b1cd99a to your computer and use it in GitHub Desktop.
Best Months for Air Travel
Month DepDelayed ArrDelayed Cancelled
1 0.20331811841225558 0.23635403167895142 0.028572136059362954
2 0.2278720952293952 0.2647127026400298 0.036181829680484015
3 0.21672969858299923 0.2459153695077018 0.02626726614617994
4 0.16516252428418093 0.19563269277710715 0.017312405747284015
5 0.15858833930129493 0.18948759098323747 0.01027391046903065
6 0.22487575267183096 0.2606934849219193 0.017958975791280918
7 0.19669040069689187 0.21524020951346565 0.016877650569887454
8 0.17315145546393065 0.18937445184303234 0.016062938627651773
9 0.10826979819118963 0.12540579913774616 0.018326591583041847
10 0.10811661168094497 0.12530092322075495 0.005841371436790392
11 0.12694736198382486 0.14950159763946858 0.008519469797734257
12 0.26337809519265704 0.29982310563382863 0.032624532532782344
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Best Months for Air Travel</title>
<link rel="stylesheet" href="my_css.css">
</head>
<body>
<h2>Best Months for Air Travel</h2>
<div>
<p>Air travel delays and cancellations can range from being inconvenient to being a catastrophe that ruins the travel plan. </p>
<p>The bubble chart shows the percentages of departure delays (x axis), arrival delays (y axis) and cancellation (bubble area) of each month of 2008 for US air travels.<sup><a href="#fn1" id="ref1">1</a></sup> </br>The bar charts show the ranking of months according to the percentages. </br>Colors represnt the four seasons: <span class="season-spring">spring</span>, <span class="season-summer">summer</span>, <span class="season-fall">fall</span>, <span class="season-winter">winter</span>.</p>
<p>We can clearly see that <span class="season-fall">fall</span> months are great time for air travel. On the other hand, <span class="season-winter">winter</span> has some of the worst months, especially during December the holidy season.</p>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="my_js.js"></script>
</body>
<footer>
<sup id="fn1">1. The data originally comes from <a href="https://www.transtats.bts.gov/OT_Delay/OT_DelayCause1.asp">RITA</a> for the year <a href="http://stat-computing.org/dataexpo/2009/the-data.html">2008</a>.<a href="#ref1" title="Jump back to footnote 1 in the text.">↩</a></sup>
</footer>
</html>
body {
font: 12px sans-serif;
}
h2 {
text-align: center;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.diagonal {
stroke: #000;
shape-rendering: crispEdges;
}
.label {
fill: #000;
}
.bubble,
.bar {
opacity: 0.6;
}
.bubble-label,
.bar-label {
fill: #fff;
font-weight: bold
}
.season-winter {
color: #80B1D3;
}
.month-12,
.month-1,
.month-2 {
fill: #80B1D3;
}
.season-spring {
color: #B5CF6B;
}
.month-3,
.month-4,
.month-5 {
fill: #B5CF6B;
}
.season-summer {
color: #D6616B;
}
.month-6,
.month-7,
.month-8 {
fill: #D6616B;
}
.season-fall {
color: #E7BA52;
}
.month-9,
.month-10,
.month-11 {
fill: #E7BA52;
}
.tooltip {
position: absolute;
text-align: center;
min-width: 100px;
height: auto;
padding: 8px;
margin-top: -20px;
font: 11px sans-serif;
background: none repeat scroll 0 0 #fff;
border: 1px solid #eee;
opacity: 0.9;
pointer-events: none;
text-shadow: 1px 1px 0px #fff;
}
// Set margin, width and height
var margin = {top: 30, right: 30, bottom: 30, left: 30}
height = 600 - margin.top - margin.bottom
width = 450 - margin.left -margin.right
// Append a svg for bubble chart
var chart1 = d3.select("body")
.append("div")
.attr("class", "chart")
.style("display", "inline-block")
.append("svg")
.attr("id", "chart1")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "bubble-chart")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Append a svg for bar charts
var chart2 = d3.select("body")
.append("div")
.attr("class", "chart")
.style("display", "inline-block")
.append("svg")
.attr("id", "chart2")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "bar-charts")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Get the data, plot the charts
d3.csv("2008_rates_by_month.csv", function(data) {
data.forEach(function(d) {
d["Departure Delay"] = +d["DepDelayed"];
d["Arrival Delay"] = +d["ArrDelayed"];
d["Cancellation"] = +d["Cancelled"];
});
bubbleChart(data);
barCharts(data);
});
// Manually set all the axes with the same max and min values for consistency
var axisMax_delay_rate = 0.31,
axisMin_delay_rate = 0.1,
axisMax_cancel_rate = 0.08;
function bubbleChart(data) {
/*
Bubble Chart
x-axis: Percentage of Departure Delays
y-axis: Percentage of Arrival Delays
bubble area: Percentage of Cancellation
*/
// Define the scales
var xScale = d3.scaleLinear()
.range([0, width]);
var yScale = d3.scaleLinear()
.range([height, 0]);
var rScale = d3.scaleSqrt()
.range([0, 60]);
// Define the axes
var xAxis = d3.axisBottom()
.scale(xScale)
.tickFormat(function(d) { return Math.floor(parseFloat(d) * 100); })
.ticks(5);
var yAxis = d3.axisLeft()
.scale(yScale)
.tickFormat(function(d) { return Math.floor(parseFloat(d) * 100); })
.ticks(5);
// Set the domains
xScale.domain([axisMin_delay_rate, axisMax_delay_rate]).nice();
yScale.domain([axisMin_delay_rate, axisMax_delay_rate]).nice();
rScale.domain([0, d3.max(data, function(d) { return Math.sqrt(d["Cancellation"]); })]).nice();
// Add axes
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.append("tspan")
.style("font-weight", "bold")
.text("Departure Delay")
.append("tspan")
.style("font-weight", "normal")
.text(" (%)");
chart1.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.append("tspan")
.style("font-weight", "bold")
.text("Arrival Delay")
.append("tspan")
.style("font-weight", "normal")
.text(" (%)");
// Add diagonal line
chart1.append("line")
.attr("class", "diagonal")
.attr("x1", xScale(axisMin_delay_rate))
.attr("y1", yScale(axisMin_delay_rate))
.attr("x2", xScale(axisMax_delay_rate))
.attr("y2", yScale(axisMax_delay_rate))
.attr("stroke-dasharray", ("5, 5"));
var bubbles = chart1.selectAll(".bubble")
.data(data)
.enter();
bubbles.append("circle")
.attr("class", function(d) { return "bubble month-" + d["Month"]; })
.attr("r", function(d) { return rScale(d["Cancellation"]); })
.attr("cx", function(d) { return xScale(d["Departure Delay"]); })
.attr("cy", function(d) { return yScale(d["Arrival Delay"]); })
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
bubbles.append("text")
.attr("class", function(d) { return "bubble-label month-label-" + d["Month"]; })
.attr("x", function(d){ return xScale(d["Departure Delay"]); })
.attr("y", function(d){ return yScale(d["Arrival Delay"]) + 5; })
.attr("text-anchor", "middle")
.text(function(d) { return d["Month"]; })
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
}
function barCharts(data) {
/*
Bar Chart 1
x-axis: Percentage of Departure Delay
y-axis: Month
Bar Chart 2
x-axis: Percentage of Arrival Delay
y-axis: Month
Bar Chart 3
x-axis: Percentage of Cancellation
y-axis: Month
*/
// Create a bar plot for each data column
n = 3;
for (var i = 0; i < n; i ++) {
barChart(data, i);
}
}
// Sort the data so each bar chart is sorted in increasing order of monthly rates
function sortMonths(data, varName) {
dataSorted = data.sort(function(a, b) { return a[varName] - b[varName]; });
return dataSorted;
}
function barChart(data, idx) {
// Get the data column name
var column_names = ["Departure Delay", "Arrival Delay", "Cancellation"]
var varName = column_names[idx];
// Sort the data by the column varName
var dataSorted = sortMonths(data, varName)
// Define the scales
var xScale = d3.scaleBand()
.range([0, width])
.paddingInner(0.1)
.paddingOuter(0.6);
var yAxisHeight = height / n - 20;
var yScale = d3.scaleLinear()
.range([yAxisHeight, 0])
// Define the axes
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(12);
var yAxis = d3.axisLeft()
.scale(yScale)
.tickFormat(function(d) { return Math.floor(parseFloat(d) * 100); })
.ticks(4);
// Set the domains
xScale.domain(dataSorted.map(function(d) { return d["Month"]; }));
if (varName == "Departure Delay" || varName == "Arrival Delay") {
yScale.domain([0, axisMax_delay_rate]);
} else if (varName == "Cancellation") {
yScale.domain([0, axisMax_cancel_rate]);
}
// Append a g for bar chart
var barChart = chart2.append("g")
.attr("class", "bar-chart")
.attr("transform", "translate(0," + (idx * height / n + 20) + ")");
// Add the axes
barChart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.append("tspan")
.style("font-weight", "bold")
.text(varName)
.append("tspan")
.style("font-weight", "normal")
.text(" (%)");
var bars = barChart.selectAll(".bar")
.data(dataSorted)
.enter();
bars.append("rect")
.attr("class", function(d) { return "bar month-" + d["Month"]; })
.attr("x", function(d) { return xScale(d["Month"]); })
.attr("width", xScale.bandwidth())
.attr("y", function(d) { return yScale(d[varName]); })
.attr("height", function(d) { return (yAxisHeight - yScale(d[varName])); })
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
bars.append("text")
.attr("class", function(d) { return "bar-label month-label-" + d["Month"]; })
.attr("x", function(d) { return xScale(d["Month"]) + xScale.bandwidth() / 2; })
.attr("y", function(d) { return yScale(d[varName]); })
.attr("dy", "1em")
.attr("text-anchor", "middle")
.text(function(d) { return d["Month"]; });
}
// Add tooltip
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("display", "none");
// Tooltip number formats
var formatTooltipPercent = d3.format(".1%");
function mouseover() {
div.style("display", "inline-block");
var month = d3.select(this).datum()["Month"];
d3.selectAll(".month-" + month)
.style("opacity", 0.9);
d3.select(".bubble.month-" + month)
.transition()
.style("stroke", "#333")
.style("stroke-width", 2)
.duration(500);
d3.selectAll(".month-label-" + month)
.style("fill", "#333");
// Solve month 9 and 10 overlap problem
if (month == 9) {
d3.select(".month-label-10")
.style("opacity", "0");
}
}
function mousemove(d) {
div.style("left", d3.event.pageX + 20 + "px")
.style("top", d3.event.pageY - 10 + "px")
.html("Month: <b>" + d["Month"] + "</b><br />" +
"Percentage of<br />" +
"Departure Delay: <b>" + formatTooltipPercent(d["Departure Delay"]) + "</b><br />" +
"Arrival Delay: <b>" + formatTooltipPercent(d["Arrival Delay"]) + "</b><br />" +
"Cancellation: <b>" + formatTooltipPercent(d["Cancellation"]) + "</b>");
}
function mouseout() {
div.style("display", "none");
var month = d3.select(this).datum()["Month"];
d3.selectAll(".month-" + month)
.style("opacity", 0.6);
d3.select(".bubble.month-" + month)
.transition()
.style("stroke", "none")
.duration(500);
d3.selectAll(".month-label-" + month)
.style("fill", "#fff");
// Solve month 9 and 10 overlap problem
d3.select(".month-label-10")
.style("opacity", "1");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment