A variation on Hannah Fairfield's classic connected scatterplot on gas prices. Shows the different trends for cost as "dollars per gallon" and cost as "dollars per mile." The peaks and valleys are similar, but factoring in steadily increasing fuel efficiency makes recent gas price increases look less extreme relative to to the 1979 energy crisis and earlier years.
Miles per capita computed from US Census population estimates and FWHA's Vehicle Miles Traveled. Fuel prices per gallon from Table 9.4 of the EIA's Energy Review. Prices look a little different than the NYT chart because it uses the average regular grade price throughout (presumably leaded for earlier years and unleaded for later years), whereas this uses the broadest average available for a year: regular leaded through 1975, regular unleaded for 1976-77, and all grades for 1978-2013. Fuel prices per mile computed as price per gallon divided by overall fleet efficiency, which is calculated using miles driven and EIA data on barrels of finished motor fuel supplied per day. All prices in 2014 dollars.
xxxxxxxxxx
<meta charset="utf-8">
<style>
* {
margin: 0;
padding: 0;
font: 14px Helvetica, Arial, sans-serif;
}
path,
line {
fill: none;
stroke: #000;
}
.axis path,
.axis line {
shape-rendering: crispEdges;
stroke: #ccc;
}
.axis path {
display: none;
}
.connection {
stroke-width: 2px;
}
circle {
fill: #fff;
stroke: #000;
stroke-width: 1px;
}
div.controls {
padding-top: 0.5em;
text-align: center;
}
div.button {
background-color: #fff;
border: 1px solid #ccc;
color: #333;
padding: 0.5em 1em;
line-height: 140%;
vertical-align: middle;
cursor: pointer;
text-align: center;
display: inline-block;
}
g.year text {
font-size: 12px;
letter-spacing: 0.03em;
}
div.button:hover,
div.button:focus,
div.button:active,
div.button.active {
background-color: #ebebeb;
border-color: #adadad;
}
div.button:active,
div.button.active {
color: #000;
outline: 0;
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
div.outer {
width: 960px;
margin: 0 auto;
}
</style>
<body>
<div class="outer">
<div class="controls">
<div class="button active">Cost per gallon</div><div class="button">Cost per mile</div>
</div>
<div class="chart"></div>
</div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = { top: 10, right: 110, bottom: 40, left: 10 },
width = 960 - margin.left - margin.right,
height = 440 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var highlight = [1949,1974,1980,1990,2000,2008,2013];
var yTicks = {
gasPriceAdjusted: {
ticks: d3.range(1.5, 4.5, 0.5),
domain: [1.07715, 4.41543],
suffix: "per gallon"
},
dollarsPerMile: {
ticks: d3.range(0.09, 0.27, 0.03),
domain: [0.06463, 0.26492],
suffix: "per mile"
}
};
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.tickFormat(function(d) {
return d3.format(",.2")(d) + " mi.";
})
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(-width)
.tickValues(yTicks.gasPriceAdjusted.ticks)
.tickFormat(yFormat("gasPriceAdjusted"))
.orient("right");
var line = d3.svg.line()
.x(get("milesPerCapita", x));
var point = function(d) {
return "translate(" + line.x()(d) + " " + line.y()(d) + ")";
};
var svg = d3.select(".chart")
.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 + ")");
d3.csv("gas-prices.csv",function(error, data) {
data.forEach(function(d) {
for (var key in d) {
d[key] = +d[key];
}
});
x.domain([2500, 10500]);
y.domain(yTicks.gasPriceAdjusted.domain);
line.y(get("gasPriceAdjusted", y));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.text("Miles driven per capita")
.attr("transform", "translate(" + width + ")")
.attr("dy", "2.5em")
.attr("text-anchor", "end");
var yg = svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + width + ")")
.call(yAxis);
yg.append("text")
.text("Avg. gas price")
.attr("dy", "2.5em");
var path = svg.append("path")
.attr("class", "connection")
.datum(data)
.attr("d", line);
var circles = svg.selectAll("g.year")
.data(data)
.enter()
.append("g")
.attr("class", "year")
.attr("transform", point);
circles.append("circle")
.attr("r", 5);
circles.filter(function(d) {
return highlight.indexOf(d.year) !== -1;
})
.append("text")
.text(get("year"))
.attr("dy","-0.6em")
.attr("dx",function(d) {
return (d.year === 2008 ? "0.45em" : "-0.6em");
})
.attr("text-anchor",function(d) {
return (d.year === 2008 ? "start" : "end");
});
var buttons = d3.selectAll(".button")
.data(["gasPriceAdjusted", "dollarsPerMile"])
.on("click", update);
function update(yVar) {
buttons.classed("active",function(d) {
return d === yVar;
});
y.domain(yTicks[yVar].domain);
yAxis.tickValues(yTicks[yVar].ticks)
.tickFormat(yFormat(yVar));
line.y(get(yVar, y));
yg.transition()
.duration(250)
.call(yAxis);
path.transition()
.duration(250)
.attr("d", line);
circles.transition()
.duration(250)
.attr("transform", point);
}
});
function yFormat(key) {
return function(d, i) {
var isLast = (i === yTicks[key].ticks.length - 1);
return d3.format("$.2f")(d) + (isLast ? " " + yTicks[key].suffix : "");
};
}
function get(p, f) {
if (f) {
return function(d) {
return f(d[p]);
};
}
return function(d) {
return d[p];
};
}
</script>
https://d3js.org/d3.v3.min.js