This week's learning objective is to become familiar with the basic four-step process of going from data to visualization.
Study the following example carefully.
data.countries = [{name: 'China', pop: 1393783836},
{name: 'India', pop: 1267401849},
{name: 'USA', pop: 322583006},
{name: 'Indonesia', pop: 25281224}]
For each data point, think about how we want to visualize it. In particular, we define how to map a data attribute to a visual attribute. In this simplest example, we are mapping the order (i-th) data attribute to the x visual attribute of a rectangle.
<rect x="${d.x}"
width="20"
height="100"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
There is a special object template
that holds user-defined template strings.
Note that a name of this particular template string is set to be foo
. The
content of the string can be accessed by template.foo
in a code block.
Next, for each data point, we want to map it to a viz object that defines the attribute values we like to populate the template with. In this simplest example, there's only one attribute, x.
function computeX(d, i) {
return i * 20
}
data.viz = _.map(data.countries, function(d, i){
return {
x: computeX(d, i)
}
})
We obtain the following array of data objects.
[ { "x": 0 }, { "x": 20 }, { "x": 40 }, { "x": 60 } ]
The final step is to populate the template with the viz data calculated earlier.
// compile the template.foo into a template function
var compiled = _.template(template.foo)
var result = _.map(data.viz, function(d){
// invoke the compiled template function on each viz data
return compiled({d: d})
})
return result.join('\n')
The resulting svg tags are rendered as below
<rect x="0"
width="20"
height="100"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="20"
width="20"
height="100"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="40"
width="20"
height="100"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="60"
width="20"
height="100"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
Now it is your turn. The following exercises are incomplete. Your learning task is to add code to complete each exercise.
data.countries = [{name: 'China', pop: 1393783836},
{name: 'India', pop: 1267401849},
{name: 'USA', pop: 322583006},
{name: 'Indonesia', pop: 25281224}]
function computeX(d, i) {
return i * 20
}
function computeHeight(d, i){
return d.pop/1000000/4;
}
data.viz = _.map(data.countries, function(d, i){
return {
x: computeX(d, i),
height: computeHeight(d, i)
}
})
<rect x="${d.x}"
width="20"
height="${d.height}"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
// compile the template.foo into a template function
var compiled = _.template(template.foo)
var result = _.map(data.viz, function(d){
// invoke the compiled template function on each viz data
return compiled({d: d})
})
return result.join('\n')
The resulting svg tags are rendered as below
<rect x="0"
width="20"
height="348.445959"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="20"
width="20"
height="316.85046225"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="40"
width="20"
height="80.6457515"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="60"
width="20"
height="6.320306"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
data.countries = [{name: 'China', pop: 1393783836},
{name: 'India', pop: 1267401849},
{name: 'USA', pop: 322583006},
{name: 'Indonesia', pop: 25281224}]
var scale = 1/1000000/4;
// set the x based on the sum of the preceding widths (there's got to be a better way...)
function computeX(d, i, countries) {
if (i== 0) {return 0;}
var shortArr = _.slice(countries,0,i);
var x = _.reduce(shortArr, function(total,e){
return total + e.pop*scale;
}, 0); // accum = 0
return x;
}
function computeSize(d, i){
return d.pop*scale;
}
// TODO: add a new mapper function for width
data.viz = _.map(data.countries, function(d, i, countries){
var squareSide = computeSize(d, i);
return {
x: computeX(d, i, countries),
height: squareSide,
width: squareSide
}
})
(TODO: add template variables for width and height)
<rect x="${d.x}"
width="${d.width}"
height="${d.height}"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
// compile the template.foo into a template function
var compiled = _.template(template.foo)
var result = _.map(data.viz, function(d){
// invoke the compiled template function on each viz data
return compiled({d: d})
})
return result.join('\n')
The resulting svg tags are rendered as below
<rect x="0"
width="348.44595899999996"
height="348.44595899999996"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="348.44595899999996"
width="316.85046224999996"
height="316.85046224999996"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="665.2964212499999"
width="80.6457515"
height="80.6457515"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
<rect x="745.9421727499998"
width="6.3203059999999995"
height="6.3203059999999995"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />