GitBook Example

How rCharts Works?

This is a multi-part series on how rCharts works. My objective is to show you how easy it is to integrate a javascript visualization library into rCharts, and take advantage of a single-unified interface and other functionalities.

In this tutorial, I will go over the steps to integrate uvCharts, a new-entrant to the rapidly expanding set of wrapper libraries to d3js. Specifically, we will recreate this barchart, which was created using uvCharts.

Browse through the resources, javascript and html tabs in the jsFiddle to get a sense of how the chart works. rCharts will need to create each of these sections, and we will go through them one by one.

Resources

The resources required by a chart include the external javascript <script src = "...js"> and css files <link href="...css" rel="stylesheet"> that are included either in the head of the html document or at the end. rCharts uses a convention over configuration approach, and so the resources are specified using a config.yml file as shown below.

The config file basically provides paths to the js/css files, both locally and online from a cdn. The jshead key specifies that these js files need to be included in the head of the document.

uvcharts:
  jshead: [js/d3.v3.min.js, js/uvcharts.js]
  cdn:
    jshead:
      - "http://cdnjs.cloudflare.com/ajax/libs/d3/3.2.2/d3.v3.min.js"
      - "http://imaginea.github.io/uvCharts/js/uvcharts.js"

At this point, the uvCharts library folder will look like this

  • js
    • d3.v3.min.js
    • uvcharts.js
  • config.yml


Javascript

Our next step is to split the javascript for the chart into a mustache layout, and then populate it using a json payload processed by R.

By default, rCharts bundles all data and parameters into a single json variable chartParams. In addition to chartParams, it also sends chartId to the layout. So, we begin by replacing all data in the javascript with a placeholder {{{ chartParams }}}. rCharts uses the whisker package to replace {{{ chartParams }}} with the json payload.

Layout

<script>
  var graphdef = {{{ chartParams }}}
  var config = {
    meta: {
      position: "#{{ chartId }}"
    }
  }
  var chart = uv.chart(graphdef.type, graphdef, config)
</script>

Following rCharts conventions, we save this layout to layouts/chart.html. At this point our uvCharts library folder is going to look like this

  • js
    • d3.v3.min.js
    • uvcharts.js
  • layouts
    • chart.html
  • config.yml


Payload

Different d3js wrapper libraries tend to use different data structures. Our goal with rCharts is to provide a single-unified interface for specifying a chart, so that a user does not need to worry about the underlying format used by a specific library. This can be easily achieved by writing functions that transform data (usually a data.frame) to different formats, as required by the library.

Shown below is the dataset that we will be working with. We need to convert it into an R object, that when converted to JSON will look like the object dataset seen in the javascript shown above.

hair_eye_male <- subset(as.data.frame(HairEyeColor), Sex == "Male")
head(hair_eye_male)
   Hair   Eye  Sex Freq
1 Black Brown Male   32
2 Brown Brown Male   53
3   Red Brown Male   10
4 Blond Brown Male    3
5 Black  Blue Male   11
6 Brown  Blue Male   50

We will now write a data transformation function in R that takes x, y, data and group as inputs, and returns a data structure that when converted to JSON will match what we need. The basic idea behind make_dataset is to (a) rename the x, y columns to name, value, (b) split the data based on group and (c) convert each piece into a JSON array.

data_to_json <- function(x, y, data, group = NULL){
  require(plyr)
  dat <- rename(data, setNames(c('name', 'value'), c(x, y)))
  dat <- dat[c('name', 'value', group)]
  if (!is.null(group)){
    dlply(dat, group, toJSONArray, json = F)
  } else {
    list(main = toJSONArray(dat, json = F)) 
  }
}

You can run the code below to check that it produces the same dataset variable seen in the javascript above.

dataset = data_to_json('Hair', 'Freq', hair_eye_male, group = 'Eye')
cat(RJSONIO::toJSON(dataset))

Chart Creation

It is now time to create the chart from R. We initialize the chart as an instance of rCharts, use the setLib method to point to the location of the library folder, and then pass the data payload using the set method.

library(rCharts)
u1 <- rCharts$new()
u1$setLib("uvcharts")
u1$set(
  type = 'Bar',
  categories = names(dataset),
  dataset = dataset,
  dom = 'chart1'
)
u1

For the above code to work, you need to make sure that you have downloaded this repo, and are referring to the correct path to the uvCharts library.

Alternately, you can point to the online version of the library http://rcharts.github.io/howitworks/libraries/widgets/uvcharts in setLib, and not have to download anything!!

You could save all this code in an file named myChart.R and then publish the chart along with the code by running the code below. The published chart can be seen here

For many libraries rCharts already simplifies the steps. Similarly, you can simplify the chart creation interface further, by wrapping everything into a uPlot function

uPlot <- function(x, y, data, group = NULL, ...){
  dataset = data_to_json(x = x, y = y, data = data, group = group)
  u1 <- rCharts::rCharts$new()
  u1$setLib("uvcharts")
  u1$set(
    categories = names(dataset),
    dataset = dataset,
    ...
  )
  return(u1)
}

Let us create a StackedBar chart using uPlot

uPlot("Hair", "Freq", 
  data = hair_eye_male, 
  group = "Eye",
  type = 'StackedBar'
)

So you can see for yourself, how easy it is to integrate a javascript visualization library into rCharts.

The uvCharts API provides more functionality to customize the chart. While, we can use nested lists with the set method to access this functionality, it would be cleaner to write custom methods that follow the unified interface and make things easier. We will go over this in Part 2 of this series.

Acknowledgements

I would like to thank TimelyPortfolio for his valuable suggestions and comments, which helped improve this post.