Creating a D3.js bar chart in R

By Data Tricks, 14 February 2020

There are several online articles providing very simple examples of how to create D3 visualisations in R. However, the more complex examples are somewhat difficult to follow and there are very few step-by-step guides.

This is my first foray into D3 charts in R so I created this tutorial of how to produce a relatively simple bar chart in D3 from R, including styling the axes and the beginnings of making it interactive.

r2d3 package

Developed by RStudio, the r2d3 package is a suite of tools for creating D3 visualisation straight from R. It handles the tasks of transforming R objects into D3 friendly data structures, rendering D3 scripts and publishing visualisations to the web.

As with d3.js, you will still need to know how to write js, and knowledge of css will also help. However, r2d3 makes the process of creating a D3 visualisation easier, not least by removing the need to write js code to load data (although you’ll still need to do this in R), create an SVG and establish a width and height for the visualisation.

To create a visualisation with D3 in R, you will need to create a .js file to handle the construction of the visualisation and a .R file to handle the data and to call the r2d3 package.

Get started by installing r2d3 and loading in the package to R:

devtools::install_github("rstudio/r2d3")
library(r2d3)

Create data

Get started by installing r2d3 and loading in the package to R:

devtools::install_github("rstudio/r2d3")
library(r2d3)

Let’s create some data to practice with:

data <- data.frame(year=c(2011:2016), value=c(0.45, 0.47, 0.52, 0.7, 0.59, 0.47))

Send data to D3

Next we’ll send this data to r2d3 to create the visualisation. This won’t work yet, as we’ll need to write the js code, but I’m including it at this stage as we’re specifying some important variables that we’ll pass to the js code and which we’ll use later.

r2d3(data=data, script = "Bar chart.js", options = list(margin = 50, barPadding = 2, colour = "rgba(255,0,0,1)", xLabel = "Year", yLabel = "Value", chartTitle = "Chart Title"))

Save your R code as Bar chart.R.

Create the js code

First we’ll set some initial useful values. As already mentioned, r2d3 removes the need to create an SVG and establish a width and height for the visualisation. The r2d3 package does this for us, but we can still make changes to these variable to customise our visualisation.

Open a new script and enter the following.

var margin = options.margin,
    barPadding = options.barPadding,
    width = width-(2*margin),
    height = height-(2*margin),
    barWidth = Math.floor(width/data.length),
    xmax = d3.max(data, function(d) { return d.year; }),
    xmin = d3.min(data, function(d) { return d.year; }),
    ymax = d3.max(data, function(d) { return d.value; });

Note that in the above code we use the options. command to retrieve the variables that we sent in the options list when we called the r2d3 package. In the above example we’ve retrieved the margin and barPadding.

The last three lines dynamically find the maximum and minimum values from our data so that we can ensure our x and y axes are appropriately scaled later on.

Next we will create the chart.

svg.selectAll('rect')
   .data(data)
   .enter()
   .append('rect')
   .attr('height', function(d) { return d.value/ymax * height; })
   .attr('width', barWidth-barPadding)
   .attr('x', function(d, i) { return (margin+(i * barWidth)); })
   .attr('y', function(d) { return (height+margin-(d.value/ymax * height)); })
   .attr('fill', options.colour)

While I won’t explain in detail what each line of the code does, I’ll pick out a few important aspects. The 5th line sets the height of each of the bars in the chart, using the maximum y value, ymax, and the height of the chart, both defined earlier. The 7th line defines the position of each bar on the x-axis (the barWidth, defined earlier, plus the margin). The last line defines the position of the top edge of each bar on the y-axis – ie. the height of the chart minus the height of the bar (d.value/ymax * height) plus some margin.

Save your js code as Bar chart.js in the same folder as your Bar chart.R file.

Run the code for a simple bar chart

Now go back to your Bar chart.R file and run the whole script, and you should get the following chart in the viewer.

Adding axes

We will now create the x and y axis labels, markers and values.

//Create the x axis
var x = d3.scaleBand()
          .domain(data.map(function(d) { return d.year; }))
          .range([0, width-barPadding]);
svg.append("g")
   .attr("transform", "translate(" + margin + "," + (height+margin) + ")")
   .call(d3.axisBottom(x));

The code above will add the x axis markers and values (years). We are also using css (transform and translate) to move the x axis down and across to the correct position, as by default it will appear in the top left of our chart.

We can then add some code to print an axis title and position it correctly.

svg.append("text")             
  .attr("transform", "translate(" + (width/2) + " ," + (height+2*margin) + ")")
  .attr("dx", "1em")
  .style("text-anchor", "middle")
  .style("font-family", "Tahoma, Geneva, sans-serif")
  .style("font-size", "12pt")
  .text(options.xLabel);

Note that the final line retrieves the xLabel variable that was specified when we called the r2d3 package in the R script.

Using similar logic, we can create the y axis.

//Create the y axis
var y = d3.scaleLinear()
          .range([height, 0])
          .domain([0, ymax]);
svg.append("g")
  .attr("transform", "translate(" + margin + ", " + margin + ")")
  .call(d3.axisLeft(y));
svg.append("text")
  .attr("transform", "translate(" + 0 + " ," + ((height+2*margin)/2) + ") rotate(-90)")
  .attr("dy", "1em")
  .style("text-anchor", "middle")
  .style("font-family", "Tahoma, Geneva, sans-serif")
  .style("font-size", "12pt")
  .text(options.yLabel);

Note we are using additional css to rotate the text through 90degrees. Our chart now looks like this:

Adding a chart title

We will now add our chart title.

//Create the chart title
svg.append("text")
  .attr("x", (width / 2))             
  .attr("y", (margin/2))
  .attr("text-anchor", "middle")
  .attr("dx", "1em")
  .style("font-size", "16pt")
  .style("font-family", "Tahoma, Geneva, sans-serif")
  .text(options.chartTitle);

Adding hover effects

One of the benefits of D3 visualisations is that they can be interactive. Some simple hover effects can be added to our visualisation by appending the following code to our code to create the chart.

//Hover effects
   .on('mouseover', function (d, i) {
     d3.select(this).transition()
       .duration('50')
       .attr('opacity', '.5')})
   .on('mouseout', function (d, i) {
     d3.select(this).transition()
       .duration('300')
       .attr('opacity', '1')})

This creates a simple effect of changing the opacity of each bar when the mouse is hovered over it. Whilst not very useful in itself, provides a foundation which can be built upon, perhaps by adding mouse-over text boxes.

Full source code

Bar chart.R script:

rm(list=ls())
library(r2d3)
data <- data.frame(year=c(2011:2016), value=c(0.45, 0.47, 0.52, 0.7, 0.59, 0.47))
r2d3(data=data, script = "Bar chart.js", options = list(margin = 50,
                                                        barPadding = 2,
                                                        colour = "rgba(255,0,0,1)",
                                                        xLabel = "Year",
                                                        yLabel = "Value",
                                                        chartTitle = "Chart Title"))

Bar chart.js script:

//Set some initial values
var margin = options.margin,
    barPadding = options.barPadding,
    width = width-(2*margin),
    height = height-(2*margin),
    barWidth = Math.floor(width/data.length),
    xmax = d3.max(data, function(d) { return d.year; }),
    xmin = d3.min(data, function(d) { return d.year; }),
    ymax = d3.max(data, function(d) { return d.value; });
//Create the chart
svg.selectAll('rect')
   .data(data)
   .enter()
   .append('rect')
   .attr('height', function(d) { return d.value/ymax * height; })
   .attr('width', barWidth-barPadding)
   .attr('x', function(d, i) { return (margin+(i * barWidth)); })
   .attr('y', function(d) { return (height+margin-(d.value/ymax * height)); })
   .attr('fill', options.colour)
   //Hover effects
   .on('mouseover', function (d, i) {
     d3.select(this).transition()
       .duration('50')
       .attr('opacity', '.5')})
   .on('mouseout', function (d, i) {
     d3.select(this).transition()
       .duration('300')
       .attr('opacity', '1')})
//Create the x axis
var x = d3.scaleBand()
          .domain(data.map(function(d) { return d.year; }))
          .range([0, width-barPadding]);
svg.append("g")
  .attr("transform", "translate(" + margin + "," + (height+margin) + ")")
  .call(d3.axisBottom(x));
svg.append("text")             
  .attr("transform", "translate(" + (width/2) + " ," + (height+2*margin) + ")")
  .attr("dx", "1em")
  .style("text-anchor", "middle")
  .style("font-family", "Tahoma, Geneva, sans-serif")
  .style("font-size", "12pt")
  .text(options.xLabel);
//Create the y axis
var y = d3.scaleLinear()
          .range([height, 0])
          .domain([0, ymax]);
svg.append("g")
  .attr("transform", "translate(" + margin + ", " + margin + ")")
  .call(d3.axisLeft(y));
svg.append("text")
  .attr("transform", "translate(" + 0 + " ," + ((height+2*margin)/2) + ") rotate(-90)")
  .attr("dy", "1em")
  .style("text-anchor", "middle")
  .style("font-family", "Tahoma, Geneva, sans-serif")
  .style("font-size", "12pt")
  .text(options.yLabel);
//Create the chart title
svg.append("text")
  .attr("x", (width / 2))             
  .attr("y", (margin/2))
  .attr("text-anchor", "middle")
  .attr("dx", "1em")
  .style("font-size", "16pt")
  .style("font-family", "Tahoma, Geneva, sans-serif")
  .text(options.chartTitle);

Tags: , , ,

4 thoughts on “Creating a D3.js bar chart in R”

  1. D. Woods says:

    Great post, very helpful. I fully agree that there aren’t enough r2d3 examples out there yet.

    I happily hacked my way through this example and I now have a working version. As a result, I think there is an error in the post above. There are several places in your JavaScript where `2margin` appears but I think you mean `2 * margin`.

  2. Data Tricks says:

    Hi, I’m glad you found it helpful. You’re absolutely right about the error in the JavaScript. I’ve updated the code now. Many thanks and good spot!

  3. Data Tricks says:

    Check out an update to this tutorial here -> https://datatricks.co.uk/animated-d3-js-bar-chart-in-r.

    Includes animating the bars when the chart is loaded and more hover effects including tooltips.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please note that your first comment on this site will be moderated, after which you will be able to comment freely.