Created
September 11, 2017 03:09
-
-
Save Zhenmao/fe05fd4811e0e601ab36a3319b1cd99a to your computer and use it in GitHub Desktop.
Best Months for Air Travel
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
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 |
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 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> |
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
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; | |
} |
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
// 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