book

Data to Viz in Four Steps

This week's learning objective is to become familiar with the basic four-step process of going from data to visualization.

  • Load Data
  • Template
  • Map
  • Populate

Study the following example carefully.

Step 1: Load Data

data.countries = [{name: 'China', pop: 1393783836},
 {name: 'India', pop: 1267401849},
 {name: 'USA', pop: 322583006},
 {name: 'Indonesia', pop: 25281224}]

Step 2: Template

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.

template.foo

<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.

Step 3: Map

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
 }
]

Step 4: Populate

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)" />


Your Turn

Now it is your turn. The following exercises are incomplete. Your learning task is to add code to complete each exercise.

Exercise 1. Use the height of each bar to represent population

Step 1: Import Data

data.countries = [{name: 'China', pop: 1393783836},
 {name: 'India', pop: 1267401849},
 {name: 'USA', pop: 322583006},
 {name: 'Indonesia', pop: 25281224}]

Step 2: Define Mappers

function computeX(d, i) {
    return i * 20
}

function computeHeight(d, i){
    return d.pop/10000000
}

data.viz = _.map(data.countries, function(d, i){
        return {
            x: computeX(d, i),
            height: computeHeight(d, i)
        }    
    })

Step 3: Template

template.foo

<rect x="${d.x}"
     width="20"
     height="${d.height}"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />

Step 4: Populate

// 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="139.3783836"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />




<rect x="20"
     width="20"
     height="126.7401849"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />




<rect x="40"
     width="20"
     height="32.2583006"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />




<rect x="60"
     width="20"
     height="2.5281224"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />


Exercise 2. Use both the height and width of each bar to represent population

Step 1: Import Data

data.countries = [{name: 'China', pop: 1393783836},
 {name: 'India', pop: 1267401849},
 {name: 'USA', pop: 322583006},
 {name: 'Indonesia', pop: 25281224}]

Step 2: Define Mappers

function computeX(d, i) {
    return i * 20
}

function computeHeight(d, i){
    return d.pop/10000000
}

function computeWidth(d, i){
    return 20
}

data.viz = _.map(data.countries, function(d, i){
        // TODO: add a new attribute to each viz object
        return {
            x: computeX(d, i),
            height: computeHeight(d, i),
            width: computeWidth(d, i)
        }    
    })

Step 3: Template

(TODO: add template variables for width and height)

template.foo

<rect x="${d.x}"
     width="${d.width}"
     height="${d.height}"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />

Step 4: Populate

// 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="139.3783836"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />




<rect x="20"
     width="20"
     height="126.7401849"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />




<rect x="40"
     width="20"
     height="32.2583006"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />




<rect x="60"
     width="20"
     height="2.5281224"
     style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />