Getting Started with D3.js for Data Visualization

Aravind Sundaresan
13 min readSep 10, 2020

--

Learning about the fundamentals of D3.js and using it to build a dashboard.

What is D3.js?

D3.js (Data-Driven Documents) is a JavaScript library that can be used for visualizing data using web standards (such as HTML, Canvas or SVG). It lets users manipulate DOM (Document Object Model) objects, thereby enabling them to view the visualizations on web browsers. Users can bind data to DOM objects with D3 and transform the document to display that data through interactive visualizations.

Introducing SVGs

Before diving into D3 code, it’s important to understand what SVGs are and how they’re instrumental in displaying graphics on the Web. SVG (Scalable Vector Graphics) is an XML-based file format that can be used to display vector images on websites. The key advantage that SVG has over other image formats (such as JPG or PNG) is that SVG images retain their quality when they are scaled up or down in size. As a result, this image format is a great choice for building responsive websites.

D3 uses SVG to create and modify graphical elements of the visualization. It also provides various functionalities to change the style and attributes of SVG elements either through user interactions or using the data that is bound to the elements.

With that simple introduction to D3 and SVG, let’s get started with using them to build a dashboard. The dashboard will be based on restaurant data available on Yelp. We’ll make a dashboard that depicts various metrics pertaining to the performance of restaurants in the form of commonly used charts such as bar, line and pie charts.

Prerequisites

In order to follow the tutorial better and experiment with the code, it would be helpful if the readers have good knowledge on the basics of HTML, CSS and JavaScript. Specifically, familiarity with JS objects and a good understanding of the Document Object Model (DOM) could be considered as prerequisites.

Dataset Used

The Yelp Open dataset will be used to populate the dashboard. The dataset is a subset of businesses, reviews, and user data from the website for use in personal, educational, and academic purposes. For the purpose of this tutorial, I extracted a subset of the dataset corresponding to restaurants present only in the Tempe-Phoenix area in Arizona. I did this because the dataset is pretty huge and the emphasis of the tutorial is on the visualization components rather than the scale of data. I also processed some of the data using Python and Pandas library to make it easier to incorporate it in the D3 code. All the data files are present in the GitHub repo under the data folder.

D3.js Setup

For this tutorial, we’ll be using version 4 of D3.js. To use the library in the web application, the users must reference it in the index.html file which serves as the homepage of the application. There are 2 ways to import the library:

  1. Users can directly load the library using the URL of the D3 JavaScript file available on the D3.js Website. Inorder to do this, the following line must be added in the index.html file:
<script src="https://d3js.org/d3.v4.min.js"></script>

2. Alternatively, users can also download the D3 JavaScript file from the D3.js Website to their local system and import an offline copy of the library. Users must download the d3.v4.min.js file to the project folder and include the following line in the index.html file:

<script type="text/javascript" src="d3.v4.min.js"></script

Dashboard Components

The D3 dashboard that we’ll be building consists of the following components:

  1. Interactive map : This map is created using the Leaflet JavaScript library to depict the locations of restaurants. The user can select the desired restaurant on the map and the rest of the components would be populated with the data corresponding to the selected restaurant.
  2. Horizontal bar chart : This chart depicts the distribution of customer check-ins across the days of the week indicating the most active days of business.
  3. Line chart : This chart shows the variation in the annual average rating of the restaurant over the years (on a scale of 1 to 5) which can be used to gauge how the business has been doing.
  4. Bar chart : This chart shows the annual number of reviews received by a restaurant from its customers.
  5. Pie chart : This chart shows the sentiment split (positive, negative or neutral) across the customer reviews. I performed sentiment analysis on the reviews using the StanfordNLP Python library and used the results to construct this chart.
  6. Word bubble chart : This custom chart is a variant of a word cloud depicting the most frequently used words in the customer reviews which highlight the most liked/disliked aspects of the restaurant.

Leaflet Map

The first component of the dashboard is an interactive map built using the Leaflet JavaScript library. This map displays the restaurants present at a given location in the form on orange dots/circles. The map also lets the users pick their restaurant of interest by clicking on the orange points.

The map was created using Vanilla JavaScript and it does not involve the use of D3. Hence, let’s skim through the important parts of the code for creating the map before delving into the D3 components.

  1. The map is created using the L.map() function from Leaflet which instantiates a map object given the DOM ID of a div element.
    var map = L.map("map_div");
    The above line of code creates a map object to generate a map in the HTML div with id=”map_div”.
  2. The markers to denote the locations of restaurants on the map are generated using the L.circleMarker() function which instantiates a circle marker object given a geographical point. The latitude and longitude coordinates of restaurants obtained from the Yelp dataset are fed as inputs to generate the markers on the map.

Bar Chart

This chart depicts the annual number of reviews received by the restaurant from customers.

The steps to construct the chart are as follows:

1. Selection

D3 lets users select DOM elements using the name (for example: h1, p, etc.) or CSS selectors (such as id or class). d3.select() can be used to select the DOM element that would contain the bar chart. Here’s the code snippet to create an SVG named “reviews” that contains the bar chart:

var svg = d3.select("#charts")
.append("svg")
.attr("class", "reviews")
.style("display", "inline")
.attr("width", 500)
.attr("height", 350)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

In the above code snippet, an SVG is added to the “charts” div to create the bar chart. The .attr() method is used to add attributes to the selected element. It accepts 2 parameters: the name of the attribute and its value. In this case, we’ve added attributes such as class name, height and width corresponding to the bar chart svg. Transform and tranlsate functions can be used to set the position of the SVG elements on the browser window. Additionally, the .style() method can be used to add CSS styles to the elements.

2. Data Visualization

To generate the bars in the chart, we’ll be using the “rect” SVG element to construct rectangular bars. There are also other predefined shape elements (circle, line, etc.) available as SVGs which could be used with D3. The following code snippet demonstrates how to generate bars:

// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(barData)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.year); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.reviews); })
.attr("height", function(d) { return height - y(d.reviews); })
.on ("mouseover", function(d) {
var matrix = this.getScreenCTM()
.translate(+ this.getAttribute("x"), + this.getAttribute("y"));
div.style("height", "20px")
div.style("width", "60px")
div.transition()
.duration(200)
.style("opacity", 1);
div.html("Count:" + d.reviews)
.style("left", (window.pageXOffset + matrix.e) + "px")
.style("top", (window.pageYOffset + matrix.f - 30) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});

Each bar is an individual SVG “rect” element and we use the “.bar” class to represent the same. The values to populate the bar chart are fed as input using the .data() method. The values on the x and y axes are obtained from the data and specified as attributes “x” and “y”.

D3 also lets users add behaviors to the SVG elements that make the visualizations interactive. These interactions can be specified using the .on() method. In the above snippet, we’ve included behaviors to be displayed while hovering the mouse over the bars in the chart. The “mouseover” behavior displays the count of the reviews when the user hovers over a bar. The “mouseout” behavior ensures that the count value disappears or changes when the user moves away from the current bar.

3. Chart title and axes

A title can be added to the chart by selecting the SVG element corresponding to the bar chart and appending a “text” element. In the below code snippet, we’ve specified the title text using the .text() method. Additionally, the user must also provide attributes for the title such as the x and y coordinates of where it must be displayed along with the CSS style attributes.

// Adding the chart title 
svg.append("text")
.attr("x", (width / 2))
.attr("y", 5 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "18px")
.style("text-decoration", "underline")
.text("Annual Number of Reviews");
});

The procedure to add the x and y axes to the bar chart is similar to that of adding a title. Along with adding text elements to describe the axes titles and their positions, D3 provides two methods: d3.axisLeft() and d3.axisBottom() to explicitly define the axes.

 // Adding the x-axis
svg.append("text")
.attr("transform", "translate(" + (width/2) + " ," + (height + margin.top - 15) + ")")
.style("text-anchor", "middle")
.text("Year of Review");
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickFormat(d3.timeFormat("%Y")));
// Adding the y-axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 20 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Number of Reviews");
svg.append("g")
.call(d3.axisLeft(y));

Pie Chart

This chart shows the sentiment split (positive, negative or neutral) of customers across the reviews given by them for a particular restaurant.

The steps to construct the chart are as follows:

  1. Selection

Similar to what we had done for the bar chart, the d3.select() method can be used to select the DOM element that would contain the pie chart. The chart would be added as a new SVG element.

var svg = d3.select("#sentiment").append("svg")
.attr("id","pie-chart")
.attr("height", 600)
.style("display", "inline")
.style("float", "left")
.style("width", "50%")
.append("g")
.attr("transform", "translate(" + window.innerWidth / 4 + "," + 300 + ")")

2. Data Loading and Binding

To read data from CSV files using D3, the d3.csv() method can be used as follows:

d3.csv("/data/review_count_per_sentiment.csv").then(function(data) {
console.log(data[0]);
});

Once the data is read from the file, D3 allows users to load data into a DOM element using the .data() method. With the help of the .enter() method, users can then iterate through each element in the dataset.

// Binding data to the arcs in the pie chart
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");

3. Data Visualization

To create a pie chart in D3, the following methods must be used:

d3.pie()

The d3.pie() function takes a dataset as input and prepares the data to generate a pie chart in the SVG. It does so by calculating the start and end angles for each wedge in the pie. These angles can then be used to draw the paths for the wedges in the SVG.

// Defining the pie
var pie = d3.pie()
.sort(null)
.value(function(d) { return d; });

d3.arc()

The d3.arc() function is used to draw arcs which would depict the pie chart’s wedges. An arc requires an inner radius and outer radius to be provided as inputs. If the inner radius is 0, the result will be a piechart, otherwise the result will be a donut chart.

// Defining the arcs
var arc = d3.arc()
.outerRadius(radius)
.innerRadius(0);

SVG Path

Path elements are used to draw paths on the SVG. In this case, the path elements are used to render the arcs of the pie chart. In the following code snippet, we draw the arcs using path elements and also obtain the color of each arc from user-defined data.

// Drawing the arcs
svg.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data); });

4. Legend

Adding legends to charts is similar to adding elements like titles and axes to an SVG element. The following code snippet shows how to create a single entry in the legend consisting of a circle indicating the color of a category of sentiments as well as a text element denoting the category itself.

d3.select("#pie-chart")
.append("circle")
.attr("cx", 100)
.attr("cy", 130)
.attr("r", 6)
.style("fill", "#34cbcb")
d3.select("#pie-chart")
.append("text")
.attr("x", 110)
.attr("y", 130)
.text("Positive")
.style("font-size", "15px")
.attr("alignment-baseline","middle")

Replicating the above code for all the entries in the legend would result in the following legend:

Line Chart

This chart displays the variation in the annual average rating obtained by a restaurant from its customers.

The steps to construct the chart are as follows:

  1. The first step is to select a DOM element to which a new SVG must be appended to contain the line chart. The implementation is similar to what was done in the previous two charts.
  2. The axes of the chart can be created using the d3.axisLeft() and d3.axisBottom() functions. In order to avoid the duplication of content, I have not included the code for the axes. For a quick reference, please refer to the code snippet provided in the Bar Chart section to create the axes. The creation of the chart title is the same as the one mentioned in the above section as well.
  3. Creating a Line: The d3.line() function can be used to define a line that connects a sequence of points. The x and y coordinates of the data points present on the line must be provided as inputs as follows:
// d3 line generator
var line = d3.line()
.x(function(d) { return xScale(d.year); }) // x values for the line generator
.y(function(d) { return yScale(d.rating); }) // y values for the line generator

4. Appending a Path: Once the line is defined with the data points, we must append a path to the SVG element to draw the line plot. The dataset is passed to the path using the .datum() method. The “d” attribute gives the instructions to the SVG path about where to connect the points using the line object created in the previous step.

svg.append("path")
.datum(data) // Bind data to the line
.attr("class", "line")
.attr("d", line);

We pass our dataset using the datum method and then set the attributes of fill color, stroke color, and width. In the end, we set the attribute of d which actually gives instruction to the SVG path about where to connect the points of the path.

5. Displaying data points on the line: Finally, we highlight the data points on the line by drawing/plotting circles for each point. These points can be drawn using the “circle” SVG element and the location of the points can be specified with the x and y coordinates.

// Adding a circle for each datapoint
svg.selectAll(".dot")
.data(dat)
.enter()
.append("circle")
.attr("class", "dot")
.attr("cx", function(d) { return xScale(d.year) })
.attr("cy", function(d) { return yScale(d.rating) })
.attr("r", 5)

Word Bubble Chart

This is a slightly complex chart which has been included in this tutorial to highlight how D3 can be also used to generate unique visualizations. A word bubble is a variant of a word cloud visualization where the words and their counts are displayed in the form of bubbles with the sizes of bubbles corresponding to the values of word counts. A key advantage of a word bubble chart is that it makes it easier for the user to interpret the differences in the values better than a word cloud. Our word bubble chart depicts the most frequently used words in the reviews given by customers for a restaurant.

The steps to construct the chart are as follows:

  1. The first step is to select a DOM element and add a new SVG to contain the chart. The code for this is similar to the ones shared in the previous sections.

2. Defining a pack to lay out the hierarchy of nodes: The visualization can be thought of as visualizing a cluster of data points where each data point is a bubble. To create such a visualization, D3 enables users to define a hierarchy of data points. In this case, a packed layout is used to display the bubbles close to each other. This layout can be defined using the d3.pack() method.

var pack = d3.pack()
.size([size, size]) // size of each bubble
.padding(size*0.005);

3. Defining the hierarchy of nodes: The hierarchy of word bubbles can be defined using a d3.hierarchy object. It has a number of functions defined on it for retrieving things like ancestor, descendant and leaf nodes. Here, we define a root node with the words and word counts as the children nodes.

var root = d3.hierarchy({children: keys})
.sum(function(d) { return counts[d]; });

4. After creating the hierarchy of nodes, each node can be defined as an individual SVG element as follows:

// Defining the nodes for each word found in the hierarchy
var node = chart.selectAll(".node")
.data(pack(root)
.leaves())
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + (d.y + 30) + ")"; });

5. The next step is to draw the nodes/bubbles by creating an SVG circle element for each bubble.

// Drawing a circle for each node
node.append("circle")
.attr("id", function(d) { return d.data; })
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.data); });

6. Finally, the text to be displayed inside each word bubble can be added by appending text elements to the node SVGs. This is similar to adding text elements like chart titles or axes titles to SVG elements.

Conclusion

In this tutorial, we covered the basics of D3 and used the library to build a dashboard with multiple visualization components. While it may seem like a lot of code, there is usually a common pattern in the code while creating visualizations with D3 such as the selection of DOM elements and addition of new components. D3 is useful in scenarios where the visualizations must be delivered as part of a web application. Hence, the learning curve might be steep for users who are new to web development. Alternatively, tools such as Tableau and Power BI are more beginner-friendly. The advantage of using D3 is the flexibility to create custom visualizations that are different from the norm. Once users get familiar with JavaScript and D3, the development time to come up with creative visualizations significantly reduces.

The entire code for the dashboard has been uploaded on GitHub: https://github.com/aravind-sundaresan/d3-tutorial

--

--