- Expert Data Visualization
- Jos Dirksen
- 553字
- 2025-04-04 19:31:10
Loading the data and setting up D3 scales
For this example, we load the data slightly different than we did in the other examples. The main reason is that this time we've got two data sources we want to load instead of just one. For this, we'll use the functionality provided by the d3.queue library:
var yearMax = 2014;
// holds the loaded data
var adjustedData;
var unadjustedData;
// load data asynchronously, call update when done.
d3.queue()
.defer(d3.csv, "./data/households.csv")
.defer(d3.csv, "./data/householdsU.csv")
.await(function (error, adjusted, unadjusted) {
// set the data to the global variables
adjustedData = adjusted;
unadjustedData = unadjusted;
// and call update
update();
});
In this code fragment you can see that we use the d3.queue() function call to load our two data sources. the households.csv file contains the indexed income, and the householdsU.csv file contains the unadjusted income. With the defer function, we queue the d3.csv function. The first defer function, when it is executed, will call d3.csv("./data/households.csv") function, and the second one will call the d3.csv("./data/householdsU.csv") function. Functions queued with the defer function will be executed (in parallel), when the await function is called. The await function takes a callback function as its argument, which is called when both resources have finished loading. In this callback, we just assign the incoming data to a global variable, and call the update() function.
This update() function looks like this:
function update(year) {
year = year || yearMax;
var yearIndex = (adjustedData.length - 1) - (yearMax - year);
var adjustedIndexedData = adjustedData.map(function(d)
{
return mapToindexed(d, adjustedData[yearIndex])
});
var unadjustedCleaned = unadjustedData.map(mapToIncome);
var maxAbove = Math.abs(100 - d3.max(adjustedIndexedData, function(d)
{
return d.indexed
}));
var maxBelow = Math.abs(100 - d3.min(adjustedIndexedData, function(d)
{
return d.indexed
}));
var xRangeAdjusted = Math.ceil(Math.max(maxAbove, maxBelow));
var xScale = d3.scaleLinear().range([0, width]).domain([1984,2014]);
var yIndexedScale = d3.scaleLinear()
.range([height, 0]).domain([100-xRangeAdjusted, 100+xRangeAdjusted]);
var incomeMin = d3.min(unadjustedCleaned, function (d)
{
return d.value
});
var incomeMax = d3.max(unadjustedCleaned, function (d)
{
return d.value
});
var yIncomeScale = d3.scaleLinear()
.range([height, 0])
.domain([
Math.floor(incomeMin/2000) * 2000,
Math.ceil(incomeMax/2000) * 2000
]);
addGradients(yIndexedScale);
addArea(xScale, yIndexedScale, adjustedIndexedData);
addIndexedLine(xScale, yIndexedScale, adjustedIndexedData);
addIncomeLine(xScale, yIncomeScale, unadjustedCleaned)
addAxis(yIncomeScale, yIndexedScale, xScale, xRangeAdjusted)
addMouseTracker(xScale, yIndexedScale, yIncomeScale, adjustedIndexedData, unadjustedCleaned);
}
function mapToindexed(row, refRow) {
var income = +row.MEHOINUSA672N;
var reference = +refRow.MEHOINUSA672N;
return { date: row.DATE.split('-')[0], indexed: (income/reference) * 100};
}
function mapToIncome(row) {
var income = +row.MEHOINUSA646N;
return { date: row.DATE.split('-')[0], value: income };
}
In this update function, we make some small changes to the two data arrays by calling the map function and changing the income column to an easier to understand name by passing it through the mapToIndexed and mapToIncome functions. Next, we set up a number of linear scales (d3.scaleLinear):
- xScale: Scales the years domain (from 1984 to 2014) to the width of the chart.
- yIndexedScale: Scales the adjustedIndexedData range (for example, 90 to 110) to the height of the chart.
- yIncomeScale: Scales the unadjustedCleaned range (for example, 22.123 to 53.453) to the height of the chart.
Nothing special happens here, except for one thing with the yIncomeScale parameter. What we do there is that we make sure the range is a multiple of 2000. We do this, so that the generated y-axis at the right side will always show the minimum and maximum value. The rest of the update function is pided into a number of function calls, which are explained in the following couple of sections.