D3 Lessons - How to Create a basic Column Chart

  • template - Basic HTML framework
  • index - The home page
  • 01 Lesson - Basic javascript framework
  • 02 Lesson - Write basic javascript code
  • 03 Lesson - Load a csv file
  • 04 Lesson - Data type conversion
  • 05 Lesson - Create a SVG element
  • 06 Lesson - Add Rectangles
  • 07 Lesson - Rectangle X coordinates
  • 08 Lesson - Rectangle Y coordinates
  • 09 Lesson - Flip chart
  • 10 Lesson - Create Y axis
  • 11 Lesson - Create X axis
  • 12 Lesson - Add Legend
  • 13 Lesson - Add X,Y axis labels and a title

The basic structure of each lesson will look like the following:

  • static folder - Here we store any css, javascript, pictures, flat files, etc
  • templates - Here you will find all of the HTML files
  • template.html - This is where we will create the structure of our HTML pages
  • index.html - This is our home page, very basic. It is built off the template
  • nn - Lesson.html - These are the lessons that are built off the template
  • BasicServer.py - This is our Tornado server we will to serve the html files

How to start the server and see the lessons?

If you are confused by the tornado server feel free to check out the Learn Tornado series. Enjoy!

template - Basic HTML framework

This is the template we will be using. These pieces are all HTML.

In [1]:
# <!DOCTYPE html>
# <head>
#    <title>{{ escape(main_title) }}</title>
#    <script src="http://d3js.org/d3.v3.min.js"></script>
# </head>
#
# <style type="text/css">
# <!-- Style code goes here -->
# </style>
#
# <body>
#
#    <div>
#      <h2>{{ escape(page_title) }}</h2>
#      <br>
#    </div>
#
#    <div>
#      {% block content %}
#      {% end block %}
#    </div>
#
#</body>
#</html>

This code gives the web page a dynamic title.

In [2]:
# <title>{{ escape(main_title) }}</title>

Here we load the D3 library from the web.

In [3]:
# <script src="http://d3js.org/d3.v3.min.js"></script>

This code gives the web page a dynamic heading.

In [4]:
# <h2>{{ escape(page_title) }}</h2>

The second DIV section will be where our D3 code will reside

In [5]:
# <div>
#   {% block content %}
#     <!-- D3 code goes here -->
#   {% end block %}
# </div>

index - The home page

In [6]:
# {% extends "template.html" %}
# {% block content %}
#
# {% end block %}

This code simply imports the template we created earlier

In [7]:
# {% extends "template.html" %}

Any code we place here will go on the second DIV of our template. In this example we did not add any code.

In [8]:
# {% block content %}
#
# {% end block %}

01 Lesson - Basic javascript framework

All of our D3 code will go between out script tags. From here on out, I will be ommiting the extends and block code to save space.

In [9]:
# {% extends "template.html" %}
# {% block content %}
#        
#    <script type="text/javascript">
#        // D3 code goes here
#    </script>
#        
# {% end block %}

02 Lesson - Write basic javascript code

In [10]:
# <script type="text/javascript">
# 
#   // Create Global Variable
#   var dataset;
#                
#   // Add data to variable
#   dataset = [1,2,3,4,5,6,7,8,9,10]
#                
#   // Print to console
#   console.log(dataset)
#                
# </script>

This is how we comment out lines.

In [11]:
# // Anything past the two slashes will be ignored

This is how we declare variables.

In [12]:
# // Create Global Variable
# var dataset;

This is how we set variables.

In [13]:
# // Add data to variable
# dataset = [1,2,3,4,5,6,7,8,9,10]

This is the equivalent to print. If you are in chrome, right click on web page and click on inspect element, then go to console and expand Array[10].

In [14]:
# // Print to console
# console.log(dataset)

03 Lesson - Load a csv file

Here we load the csv file. After the csv file is loaded the function is called. This function sets the variable dataset and gives it the contents of the csv file. Then the Graph function is called and we pass the "dataset" variable to it. The Graph function then prints the contents of the object that is passed to it.

In [15]:
# // Create Global Variable
# var dataset;
#
# // Load csv file
# d3.csv("{{ static_url('data/SingleColumn.csv') }}", function(data) {
#                
#       dataset = data;
#
#       // Call function
#       Graph(dataset);
#                        
# });
#
# // Create function
# function Graph(input) {
#
#       // Print to console
#       console.log(dataset)
#
# };

04 Lesson - Data type conversion

The only difference between the previous lesson is the code below. We convert the data type of the column Revenue. by default js will treat the values in the column as strings. Adding a plus sign will do the conversion. If you look at the console, you will see Revenue: 1, the number one is no longer in quotes like in the previous lesson.

In [16]:
# // Convert from string to numeric
# dataset.forEach(function(d) {
#        d.Revenue = +d.Revenue;
# });

05 Lesson - Create a SVG element

We first select the body of the page and append an svg element to it. This is our first svg element. You can think of this as our canvas to draw on that is 1200px x 800px (pixels). We can specify other supported dimensions like (em, pt, in, cm, and mm).

In [17]:
# //Create SVG element
# var svg = d3.select("body")
#            .append("svg")
#            .attr("width", 1200)
#            .attr("height", 800);
In [18]:
# If you go to "inspect element" you will see:
# <svg width="1200" height="800"></svg>

06 Lesson - Add Rectangles

Now that we have our svg canvas in place. We will attempt to make a column chart by adding rectangle shapes driven by data.

We select the "rect" that will be created when we call .append("rect")
We add the data by .data(input)
We add attributes to the rectangles with .attr():

  • x = horizontal position of the rectangle
  • y = vertical position of the rectangle
  • width = width of rectangle
  • height = height of rectangle

When you run the code you will only see one rectangle. The reason is that the (x,y) position of every rectangle is the same. The height of each triangle should be relative the the data you feed it, but in this lesson we hard coded it to be 50. Both of these issues will need to be resolved in future lessons.

In [19]:
# //Add rectangles
# svg.selectAll("rect")
#    .data(input)
#    .enter()
#    .append("rect")
#    .attr("fill", "red")
#    .attr("x", 0)
#    .attr("y", 0)
#    .attr("width", 20)
#    .attr("height", 50);

07 Lesson - Rectangle X coordinates

To get each rectangle to show, we need a way to give each one a different x coordinate. We start by declaring a new set of variables.

In [20]:
# // Declare Variables
# var margin = {top: 40, right: 40, bottom: 40, left:40},
#         w = 960 - margin.left - margin.right,
#         h = 500 - margin.top - margin.bottom;

We now create a scale for the x axis.

domain = from zero to the number of records in data set.
range = from zero to the width of the svg

In [21]:
# //Create X Scale
# var xScale = d3.scale.linear()
#                .domain([0, input.length])
#                .range([0, w]);  

We then modify the svg element to take into consideration our new variables. The transforms will create a smaller box inside the bigger box (the w and h ignoring the margins). This maller box will be where we draw our graphs.

In [22]:
# //Create SVG element
# var svg = d3.select("body")
#             .append("svg")
#             .attr("width", w + margin.left + margin.right)
#             .attr("height", h + margin.top + margin.bottom)
#             .append('g')
#             .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

Here is where we actually give each rectangle their own unique x coordiante.

function(d,i), the d is the data set, the i is the index (0,1,2...). Zero based index.

In [23]:
# .attr("x", function(d, i) { return xScale(i); })

08 Lesson - Rectangle Y coordinates

To fix the issue of the height of the rectangles being tied to the data, we simply make a function that does just that. Every rectangle will look at the "Revenue" column and use that number as its height. Before we do this, we first make a Y scale.

In [24]:
# //Create Y Scale
# var yScale = d3.scale.linear()
#                .domain([0,d3.max(input, function(d) { return d.Revenue; })])
#                .range([h, 0]);

We then replace the hard coded number with this.

In [25]:
# .attr("height", function(d) { return h - yScale(d.Revenue) });

09 Lesson - Flip chart

If you have not noticed the chart has been upside down this whole time. We correct this by adjusting the "y" attribute.

In [26]:
# .attr("y", function(d) { return yScale(d.Revenue) })

10 Lesson - Create Y axis

Lets crete a Y axis. We start by letting d3 know where to place the axis.

In [27]:
# //Create Y Axis
# var yAxis = d3.svg.axis()
#                   .scale(yScale)
#                   .orient('left');

and finally add the axis.

In [28]:
# //Draw Y axis
# svg.append("g")
#    .attr("class", "y axis")
#    .call(yAxis);

11 Lesson - Create X axis

At this point we will change our data set from one with one column to one with multiple columns.

State,CustomerCount
FL,200
GA,210
NY,250
TX,225
ALL,1400

We use an ordinal scale since our data for the X axis is not numeric.

In [29]:
# //Create X Scale
# var xScale = d3.scale.ordinal()
#   			 .domain(input.map(function (d){ return d.State;}))
#   			 .rangeRoundBands([0, w], 0.5);

Here we create the X axis and orient it to the bottom of our chart.

In [30]:
# //Create X Axis	
# var xAxis = d3.svg.axis()
# 			  .scale(xScale)
# 			  .orient("bottom");

This is where the X axis is actually drawn.

In [31]:
# //Draw X axis	
# svg.append("g")
#    .attr("class", "x axis")
#    .attr("transform", "translate(0," + h + ")")
#	.call(xAxis);

12 Lesson - Add Legend

We first are going to place our bars into a class. This step is very important since it will allow us to easily target the bars.

We change...

In [32]:
# //Add rectangles
# svg.selectAll("rect")
#    .data(input)
#	.enter()
#	.append("rect")

to...

In [33]:
# //Add rectangles
# svg.selectAll(".bar")
#    .data(input)
#    .enter()
#    .append("rect")
#    .attr("class", "bar")

We are also going to map each rectangle to a specific color. We first create color scale.

In [34]:
# //Create color scale                      
# var cScale = d3.scale.linear()
#                .domain([0,d3.max(input, function(d) { return d.CustomerCount; })])
#                .range(["blue","red"]);

Then we apply the colors to their respective bars.

In [35]:
# .attr("fill",function(d) {return cScale(d.CustomerCount);})

Now we can add the legend.

In [36]:
# // Add Legend
# var legend = svg.append("g")
#                 .attr("class", "legend")
#	 		    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

Here we create the rectabgles.

In [37]:
# legend.selectAll('rect')
# 	  .data(input)
# 	  .enter()
# 	  .append('rect')
# 	  .attr("x", d3.max(xScale.range()) + xScale.rangeBand() + 40 )
# 	  .attr("y", function(d, i){ return i *  25;})
# 	  .attr("width", 18)
# 	  .attr("height", 18)
#       .attr("fill",function(d) {return cScale(d.CustomerCount);});

We now add the text that goes next to the rectangles.

In [38]:
# legend.selectAll('text')
# 	  .data(input)
# 	  .enter()
# 	  .append('text')
# 	  .attr("x", d3.max(xScale.range()) + xScale.rangeBand() + 40 )
# 	  .attr("y", function(d, i){ return (i *  25) + 8;})
#       .attr("dy", ".35em")
#       .style("text-anchor", "end")
#       .text(function(d) { return d.State; });

13 Lesson - Add X,Y axis labels and a title

In [39]:
# //Draw title 
# svg.append("text")
#    .attr("x", w / 2 )
#    .attr("y",  yScale(d3.max(input, function(d) { return d.CustomerCount; })) - 40 )
#    .style("text-anchor", "middle")
#    .text("Title of Graph");
In [40]:
# //Create X axis label   
# svg.append("text")
#    .attr("x", w / 2 )
#    .attr("y",  yScale(0) + 40 )
#    .style("text-anchor", "middle")
#    .text("State");
In [41]:
# //Create Y axis label
# svg.append("text")
#    .attr("transform", "rotate(-90)")
#    .attr("y", xScale(0) - 80 )
#    .attr("x",0 - (h / 2))
#    .attr("dy", "1em")
#    .style("text-anchor", "middle")
#    .text("Revenue");