body { font-family: "Open Sans", sans-serif; font-size: 12px; font-weight: 400; padding-top: 10px; padding-bottom: 100px; } html { overflow-y: scroll; } h1 { color: steelblue; font-weight: 800; font-size: 1.7em; } h2 { color: steelblue; font-size: 1.3em; padding-bottom: 10px; } h3 { color: gray; font-size: 1.2em; padding-bottom: 10px; } footer a, footer a:hover, footer a:visited { color: #D2A000; } .text-small { font-size: 12px; font-style: italic; } footer { color: white; padding-top: 5px; border-top: 1px solid gray; font-size: 12px; position: fixed; left: 0; bottom: 0; height: 50px; width: 100%; background: black; text-align: center; } pre { font-size: 11px; } .tooltip { font-size: 15px; font-weight: bold; width: auto; height: auto; padding: 5px; background: white; border: 2px solid gray; border-radius: 5px; pointer-events: none; } .tooltip-selected { background: #FFD4C2; border: 2px solid red; } #price { width: 75px; height: 35px; }
D3 Inflation Explorer
A ZMW600.00 item in 2022 will cost ZMW211.55 in 1980.
Instructions
Mouseover to view details on relative CPI and prices. Click on a tile to freeze details
2022 years are highlighted in gold.
Details
This project is based on the CPI Inflation Calculator provided by the Bureau of Labor Statistics.
This calculator measures the buying power of the dollar based on the Consumer Price Index (CPI). In general, the relative price formula is:
targetPrice = basePrice x (targetCPI / baseCPI)
A specific example given the 1986 price and calculating the 2011 price:
The average CPI for 1986 = 55.83 The average CPI for 2011 = 6.43 2011 Price = 1986 Price x (2011 CPI / 1986 CPI) = ZMW20.00 x (55.83 / 6.43) = ZMW173.6
(function() { /** * Initialize D3 by making AJAX call to tsv data */ d3.tsv("inflation.tsv", function(error, data) { data = buildInflation(data); vis = {}; //init vis object buildVis(data); }); /** * Initiate and build vis */ function buildVis(data) { // vis attributes vis.margin = { top: 50, right: 50, bottom: 0, left: 50 }; vis.width = 450 - vis.margin.left - vis.margin.right; vis.height = 450 - vis.margin.top - vis.margin.bottom; vis.gridSize = Math.floor(vis.width / Math.sqrt(data.length)); vis.basePrice = 600; vis.ratio = 1; // create main svg vis.svg = d3.select("#vis").append("svg") .attr("width", vis.width + vis.margin.left + vis.margin.right) .attr("height", vis.height + vis.margin.top + vis.margin.bottom) .append("g") .attr("transform", "translate(" + vis.margin.left + ", " + vis.margin.top + ")"); // create vis colorscale vis.colorScale = d3.scale.quantile() .domain([0, d3.max(data, function(d) { return d.value; })]) .range(colorbrewer.RdYlBu[10].reverse()); // create selected tooltip vis.tooltipSelected = d3.select("body").append("div") .classed("tooltip", true) .classed("tooltip-selected", true) .style("opacity", 0); // create tooltip vis.tooltip = d3.select("body").append("div") .classed("tooltip", true) .style("opacity", 0); // add x-label (target year) vis.svg.append("text") .classed("x-label", true) .attr("text-anchor", "middle") .attr("x", vis.width / 2) .attr("y", -10) .text("Base Year"); // add y-label (base year) vis.svg.append("text") .classed("y-label", true) .attr("text-anchor", "middle") .attr("x", -vis.width / 2) .attr("y", -15) .attr("dy", ".75em") .attr("transform", "rotate(-90)") .text("Target Year"); // create heatmap tiles vis.tiles = vis.svg.selectAll(".tiles") .data(data).enter() .append("rect").classed("tiles", true) .attr("x", function(d) { return d.x * vis.gridSize; }) .attr("y", function(d) { return d.y * vis.gridSize; }) .attr("rx", 1) .attr("ry", 1) .attr("stroke", function(d) { return d.targetYear == 2022 || d.baseYear == 2022 ? "orange" : "lightgray"; }) .attr("stroke-width", "1px") .attr("width", vis.gridSize) .attr("height", vis.gridSize) .attr("fill", function(d) { return vis.colorScale(d.value); }) .on("mouseover", function(d) { tooltipShow(vis.tooltip, d); d3.select(this).attr("stroke", "red"); }) .on("mouseleave", function(d) { tooltipHide(vis.tooltip); d3.select(this) .attr("stroke", function(d) { return d.targetYear == 2022 || d.baseYear == 2022 ? "orange" : "lightgray"; }); }) .on("click", function(d) { tooltipHide(vis.tooltipSelected); tooltipShow(vis.tooltipSelected, d); updateYears(d); }); d3.select("#price").on("input", function() { updatePrice(+this.value); }); } /** * Helper function to build inflation data from tsv data file */ function buildInflation(data) { result = []; for (var x = 0; x < data.length; x++) { baseCPI = +data[x].cpi; baseYear = +data[x].year; for (var y = 0; y < data.length; y++) { targetCPI = +data[y].cpi; targetYear = +data[y].year; d = { baseYear: baseYear, targetYear: targetYear, baseCPI: baseCPI, targetCPI: targetCPI, value: targetCPI / baseCPI, x: x, y: y }; result.push(d); } } return result; } /** * Helper function to update price */ function updatePrice(price) { vis.basePrice = price; d3.selectAll(".base-price").text(price.toFixed(2)); d3.selectAll(".target-price").text((vis.ratio * vis.basePrice).toFixed(2)); } function updateYears(d) { vis.ratio = +d.value; d3.select(".base-year").text(+d.baseYear); d3.select(".target-year").text(+d.targetYear); d3.select(".target-price").text((+d.value * vis.basePrice).toFixed(2)); } /** * Helper function to show tooltip */ function tooltipShow(tooltip, d) { tooltip.style("opacity", 0.9) .html( "ZMW<span class='base-price'>" + vis.basePrice.toFixed(2) + "</span>" + "<sub> " + d.baseYear + " (CPI: " + d.baseCPI.toFixed(2) + ") " + "</sub>" + "<br />" + "ZMW<span class='target-price'>" + (vis.basePrice * d.value).toFixed(2) + "</span>" + "<sub> " + d.targetYear + " (CPI: " + d.targetCPI.toFixed(2) + ") " + "</sub>" ) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY) + "px"); } /** * Helper function to hide tooltip */ function tooltipHide(tooltip) { tooltip.style("opacity", 0); } })();