= list(
series list(
data = c(29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4)
) )
Thinking in highcharter
- How to build any Highcharts plot in R
Rstudio’s Mine Cetinkaya-Rundel had a post about the highcharter
package, a wrapper for the Highcharts javascripts library that lets you create super sweet interactive charts in R.
Joshua Kunst’s highcharter
package has become my go-to plotting package once I reach the production phase and know I will be using HTML. This is mainly for 3 reasons:
- Beautiful interactive charts
- Extremely customizable
- Great documentation (via Highcharts API). Requires understanding how a
highcharter
object is built and translating between the Highcharts the API.
ggplot2
is wonderfully customizable and the plotly
wrapper can make interactive charts. I’m sure that plotly
objects are customizable, as are other htmlwidget
graphing packages, but I think Highcharts graphs are the most impressive. Hence, highcharter
is where I have decided to dig deep.
As Mine noted, all products in this library are free for non-commercial use. If you plan to use highcharter
in R
for commericial use, please purchase Highcharts: https://shop.highsoft.com/
Also, a special thanks to Joshua Kunst for developing this package. I’ve learned a lot about R
and javascript
thanks to you. Y, gracias a ti mi amigo, siempre estaré empleado.
Goal of this Post
My goal is to show you how I think about, learn about, and then build more complicated highcharter
objects in R
. I want you to be able to see a graph on the Highcharts demo page and think to yourself, “yeah, I can build that”. This will means some bouncing between the Highcharts demo and API websites. I do this a lot myself and I hope, by the end of this document, you’ll find it a useful habit.
But I also want to say that my approach to building plots in highcharter
can feel a bit complicated. Generally, I like to build plots from the ground up. However, if I have tidy data and I know the structure is well suited for plotting in highcharter
, I will opt to use the hchart()
. Otherwise, for almost anything more complicated, I will build the data structure (known as a “series”) from scratch and use the highchart()
and hc_add_series_list()
functions. This process is the result after having built many, many plots, hacking away at the great many functions in highcharter
. While it may seem complicated, I assure you that it is the easiest and cleanest way to make complicated plots.
What I will not cover
- Straightforward, simple plotting using
highcharter
. Mine’s post and Joshua Kunst’s online documentation are better resources for that. I will use simple examples, but as a stepping stone to more complicated plots and to learn how to “translate” Highcharts tohighcharter
. - How to make Highstocks or Highmaps using
highcharter
. That said, the way I go about building Highcharts plot will likely translate over to Highstocks and Highmaps, so this post may prove useful if you’re interested in making timeseries or map plots.
Prerequisites
This post assumes that you have a good handle of the tidyverse
as well as basic object and list construction. In short, this is not a beginner’s tutorial but also not an advanced R
tutorial.
Series: highcharter
and Highcharts building blocks
Series are the building blocks of a Highcharts plot. Series contain the sets of data you want to plot. As a data scientist who wants to use highcharter
to make spiffy plots, the key to building any plot in highcharter
is understanding how to build a series in R
and how it relates to the structure of a series in Highcharts.
I learn best by example. I will do the same in this document.
Series in highcharter
are a list of lists with a specific structure
Think of any series you would like to plot in highcharter
as a list of lists. The Highcharts equivalent is, at the very least, an array of with a single data object or, a its most complicated, an array of many objects and arrays.
Here is a simple Highcharts plot:
$(function () {
.chart('container', {
Highchartsseries: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}];
}); })
Ignore everything but series
. A series array (series: [ ]
) with a single series object ({data: [ ] }
) is the simplest Highcharts plot possible. Translated to R
, a series would be a list with a single sublist with named elements. Note that “named elements” means I explicitly assign the elements of a list to a value i.e. unnamed elements list(c(1,2,3))
vs named list(x = c(1,2,3))
. The named element in this case is data
resulting in list structure list(data = c( ))
.
This series can be plotted using highchart()
and hc_add_series_list()
library(highcharter)
library(tidyverse)
highchart() %>%
hc_chart(backgroundColor = "white") %>%
hc_add_series_list(series)
By default, highchart()
assumes you are construction a Highcharts line chart. It also provides default series names (i.e. Series 1
, Series 2
etc) and colors if these values are left unspecified.
Let’s change the series name and color to 'Hola linea'
and 'red'
. In Highcharts, the series array object would look like this:
$(function () {
.chart('container', {
Highchartsseries: [{
name: 'Hola linea',
color: 'red',
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}];
}); })
Translated to R
, the series would be
= list(
series list(
name = 'Hola linea',
color = 'red',
data = c(29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4)
)
)highchart() %>%
hc_chart(backgroundColor = "white") %>%
hc_add_series_list(series)
Let’s add a second series. The series array object now contains two series. Each series is an object (contain in { }
, separated by ,
) with named elements (aka “members”).
$(function () {
.chart('container', {
Highchartsseries: [{
name: 'Hola linea',
color: 'red',
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
,
}// there's a comma between objects in { }
{ name: 'Reverse!',
color: 'green',
data: [54.4, 95.6, 194.1, 216.4, 148.5, 135.6, 176, 144, 129.2, 106.4, 71.5, 29.9]
}];
}); })
And in R
= list(
series list(
name = 'Hola linea',
color = 'red',
data = c(29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4)
),list(
name = 'Reverse!',
color = 'green',
data = c(54.4, 95.6, 194.1, 216.4, 148.5, 135.6, 176, 144, 129.2, 106.4, 71.5, 29.9)
)
)
highchart() %>%
hc_chart(backgroundColor = "white") %>%
hc_add_series_list(series)
Important: Naming matters. The Highcharts API for line series explicitly looks for object elements like
name
,color
,data
etc. Likewise, this means that the element names when building lists inR
also matter. Try changingdata
todatas
(or anything else) and see that nothing will be plotted. Likewise, try changingname
to something else likenombres
, and the series will fall back to the defaultSeries #
.
hc_add_series_list()
vs hc_add_series()
I prefer to always use hc_add_series_list()
, even when only add a single series. Adding a single series can be done using hc_add_series()
. For example, I could replicated the last plot by layering one series at a time.
highchart() %>%
hc_chart(backgroundColor = "white") %>%
hc_add_series(
name = 'Hola linea',
color = 'red',
data = c(29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4)
%>%
) hc_add_series(
name = 'Reverse!',
color = 'green',
data = c(54.4, 95.6, 194.1, 216.4, 148.5, 135.6, 176, 144, 129.2, 106.4, 71.5, 29.9)
)
Notice that the construction of hc_add_series()
is basically equivalent to how I built each list object in series
that was then passed to hc_add_series_list()
. I prefer, however, building list objects and saving them to a value (like series
). This makes it easier to reuse the object and also makes for much shorter pipe chains when plotting.
Highcharts API and highcharter
functions
Just throw an hc_
infront of it
Now that we’ve build a basic plot and have some understanding of what a series is, let’s play with some plot options!
The beauty of the highcharter
package is that pratically every Highcharts API call can be quickly translated to highcharter
without needing to look at highcharter
documentation. Specifically, any Highcharts API options can be access by add hc_
infront of the function e.g. hc_xAxis()
calls the xAxis
API option, hc_tooltip()
calls the tooltip
API option, etc.
Here is what I mean. When you go to the Highcharts API Options Reference, there is a menu of list of “main” option calls to the left. It looks like this:
The highcharter
equivalent to each “main” option can be accessed as a function by throwing an hc_
infront (chart
becomes hc_chart()
, plotOptions
becomes hc_plotOptions()
etc).
From there, accessing any “main” option value means using the exact same name as listed in the API. Any level deeper just means contructing a list()
but the API reference names will always be the same.
Let’s work through an example by editing the x-axis looking only at the Highcharts API x-axis options.
If I want to change the min, max, the line width, and some labeling quirks of the x-axis, then I just look at the API options for xAxis
and locate the corresponding values.
In this case, three of these suboptions (min
, max
, lineWidth
) are “unnested” level options, one is a “nested” level option (labels
). What I call an “unnested” level suboption make up the majority of suboptions—any without { ... }
, easily found by the little gray expansion triangle. “Unnested” suboptions can be accessed by using the unnested level API names asis plus a proper value. “Nested” level suboptions—those that are followed by {...}
or just any {
—require list(...)
calls.
In the image above, examples of “unnested” level suboptions (i.e. suboptions with no {...}
; access without a list) are in purple. One need only use the API name and provide a proper value. Examples of “nested” level suboptions (i.e. suboptions with { ... }
; require biulding a list(...)
object) are in red.
Knowing the “main” API option I want to use is xAxis
, I can build the highcharter
equivalent by throwing an hc_
infront. I can then directly call any of the “unnested” level suboptions min
, max
, and lineWidth
since they are not nested { ... }
objects. Using only the “unnested” level calls, the result would be hc_xAxis(min = 1, max = 7, lineWith = 5)
.
Note: As I said before, elements names must exactly match the API names, meaning suboptions are case-sensitive (i.e.
linewidth
\(\ne\)lineWidth
).
But what about nested level calls which require lists? The nested level call I cared about was labels
. Expanding the API main option, the labels
suboptions nests numerous more suboptions. One of them, style
, is another nested suboption—the value starts with { ...
. Again—and hopefully you’re starting to see the pattern!—style
suboption values can be accessed by building a named list()
.
I choose two of the labels
suboptions to change: align
and style
. align
isn’t nested so I can just assign the proper value. The default is "center"
. I change it to "left"
: align = "left"
.
But style
is another nested suboption (valuestarts with {
). But again, not to worry, this just means another list()
. I’ll change the font size, weight, and color style values: style = list(fontSize = "16px", fontWeight = "bold", color = "blue")
. The nested fully constructed labels
suboption is:
= list(align = "left",
labels style = list(
fontSize = "16px",
fontWeight = "bold",
color = "blue"
))
I can then add this labels
as aother hc_xAxis()
suboption:
hc_xAxis(min = 1,
max = 7,
lineWith = 5,
labels = list(align = "left",
style = list(
fontSize = "16px",
fontWeight = "bold",
color = "blue"
)))
Throwing this all together, I can adjust the x-axis of the plot above by adding my fully constructed hc_xAxis
function to the pipe chain.
highchart() %>%
hc_chart(backgroundColor = "white") %>%
hc_add_series_list(series) %>%
hc_xAxis(min = 1,
max = 7,
lineWith = 5,
labels = list(align = "left",
style = list(
fontSize = "16px",
fontWeight = "bold",
color = "blue"
)))
The corresponding code in Highcharts
can be see here.
Use hchart()
with Tidy Data
One of the most convinient function for plotting is the hchart()
function. But I would only recommend the use of this function if one has a Tidy Dataframe structured in a “long” format with a time-key-value or key-value structure, similar to a dataframe that would be used in ggplot
. I’ll show you what I mean.
Here is some code from an example in highcharter
that builds a graph by extracting variables from citytemp
and adding them as a series.
data(citytemp)
<- highchart() %>%
hc1 hc_chart(backgroundColor = "white") %>%
hc_xAxis(categories = citytemp$month) %>%
hc_add_series(name = "Tokyo", data = citytemp$tokyo) %>%
hc_add_series(name = "London", data = citytemp$london)
hc1
citytemp
is in a “wide” format. But this data isn’t tidy. In these data, there are three variables: month
, city
, temperature
. In this case, month
is time, city
is a key, and temperature
is a value. I want to reshape the data such that each row of data is a single observation for the temprature of one city at one point in time. Reshaping the data to a “long” format with a tidy time-key-value structure will allow us to plot virtually the same plot but in one line using hchart()
.
<- citytemp %>%
citytemp2 ::gather(key = city, value = temperature, tokyo, london) tidyr
I can now use the hchart()
function to plot these data. How the data splits into separate series is via the group
variable in the hchart()
function. Notice that the mapping of variables uses the function highcharter::hcaes()
, which was inspired by the ggplot2
function ggplot2::aes()
and has the same syntax.
hchart(citytemp2, type = 'line', hcaes(y = temperature, group = city, x = month)) %>%
hc_chart(backgroundColor = "white")
If I just wanted to print data for tokyo and london, I can just filter the data prior to using hchart()
.
::: {.cell}
<- citytemp2 %>%
citytemp2 ::filter(city %in% c('tokyo', 'london')) # filter to just tokyo and london
dplyr
<- hchart(citytemp2, type = 'line', hcaes(y = temperature, group = city, x = month))
hc2 %>%
hc2 hc_chart(backgroundColor = "white")
::: {.cell-output-display}