4.4 htmlWidgets
4.4.1 Overview of htmlWidgets
JavaScript has a number of libraries that can be used to create interactive graphics for documents viewed in a web browser. There are now several R packages that allow you to create plots built on these JavaScript libraries from within R.
While these packages draw on JavaScript libraries, they do not require you to export your data from R or to write JavaScript code. However, because the output runs on JavaScript, the resulting plots will not be interactive if included in static output like pdf or Word documents that you create using R Markdown. The output will be interactive if viewed in the “Viewer” panel of RStudio or if included in a HTML document created with R Markdown, and they can be incorporated into Shiny web applications.
There is a website with much more on these htmlWidgets
at http://www.htmlwidgets.org. Some of the packages availabe to help you create interactive graphics from R using JavaScript graphics libraries are:
leaflet
: Mappingdygraphs
: Time seriesplotly
: A variety of plots, including mapsrbokeh
: A variety of plots, including mapsnetworkD3
: Network datad3heatmap
: HeatmapsDT
: Data tablesDiagrammeR
: Diagrams and flowcharts
The leaflet
and plotly
packages are two of the most useful and developed packages in this collection of htmlWidgets. In this section, we will overview what you can make with these two packages.
I> If you are interested in learning all the details about the JavaScript on which these htmlWidgets are built, you may find the short book Getting Started with D3 by Mike Dewar interesting.
4.4.2 plotly
package
W> This section on the plotly
package requires the use of a web browser to see results. Therefore, we recommend that you go to the web version of this book to view this particular section and to interact with the graphics examples.
The plotly
package in R wraps the JavaScript plotly.js
package, an open source library for creating interactive graphs. The plotly.js
JavaScript library is built on d3.js
(Data-Driven Documents), which is a key driver in interactive web-based data graphics today. The package allows the plotting of a variety of interactive plots; one of the most interesting feature is it’s ability to plot 3-D point and surface plots that can be rotated to view them from different angles. You can find out more about the plotly.js
JavaScript library at plotly’s website.
Like some of the other packages we’ve looked at (e.g., ggmap
), the plotly
R package allows you to draw on functionality external to R, but while work entirely within R. Some of the functions in this package extend the ggplot2
code you’ve learned.
There are two main ways of creating plots using the R plotly
package:
- Use one of the package’s functions to create a customized interactive graphic:
plot_ly
: Workhorse ofplotly
, renders most non-map types of graphsplot_geo
,plot_mapbax
: Specific functions for creatingplotly
maps
- Create a
ggplot
object and then convert it to aplotly
object using theggplotly
function.
When using the first method, most graphics other than maps will be created using the plot_ly
function. For example, if you want to plot an interactive scatterplot of time versus shots for the World Cup 2010 data (which we have created as a static plot earlier in this section), you can do so with the following code:
library(faraway)
data(worldcup)
library(plotly)
plot_ly(worldcup, type = "scatter",
x = ~ Time, y = ~ Shots, color = ~ Position)
If you view this plot in a format where it is interactive, you can see that labels pop up for each point as you pass the cursor over it. Further, there are some buttons at the top of the graph that allow interactive actions like zooming, panning, and selection of a subset of the graph.
This code specifies the dataframe with the data to plot, what type of plot to create, and mappings of variables to aesthetics. In this case, we want to show Time on the x-axis and Shots on the y-axis, so we specify those for the x
and y
parameters. Further, we want to show player position with color, so we map the Position
column to color.
This code uses a ~
syntax for mapping aesthetics that is a bit different from the ggplot syntax we’ve presented earlier. This syntax is necessary in the plotly
call to let R know that these variables can be found as columns in the dataframe passed to the function. If you would like to use a constant value for an aesthetic, you must specify that the argument should be used “as-is,” using the I()
function. For example, to plot all points in blue, you could run:
plot_ly(worldcup, type = "scatter",
x = ~ Time, y = ~ Shots, color = I("blue"))
I> While you usually won’t use ~
syntax like this when using ggplot2
in interactive coding, you will use it to avoid non-standard evaluation when using ggplot2
code in functions you write for a package. See the section on non-standard evaluation earlier in the book for more on this concept.
By default, the pop-ups will show the mapped aesthetics when you move the cursor over each point. However, you can change this default to show something different when the viewer scrolls over each point. For example, the plot we created above for the World Cup data maps player time to the x aesthetic, shots to the y aesthetic, and color to the player’s position. Therefore, by default these three values will be shown for a point if you move the cursor over the point. However, you might prefer to show each player’s name, which is contained in the rownames of the worldcup
data. You can do this by using dplyr
tools to move the rownames to a column named Name
and then mapping that column to the text aesthetic and specifying that aesthetic to the hoverinfo
parameter:
%>%
worldcup mutate(Name = rownames(worldcup)) %>%
plot_ly(x = ~ Time, y = ~ Shots, color = ~ Position) %>%
add_markers(text = ~ Name, hoverinfo = "text")
You can use the paste
function to create a more customized text label. Use HTML tags for any formatting. For example, to show both the player’s name and team in a more attractive format, you could run:
%>%
worldcup mutate(Name = rownames(worldcup)) %>%
plot_ly(x = ~ Time, y = ~ Shots, color = ~ Position) %>%
add_markers(text = ~ paste("<b>Name:</b> ", Name, "<br />",
"<b>Team:</b> ", Team),
hoverinfo = "text")
If you aren’t familiar with HTML syntax, you may find it helpful to use a HTML cheatsheet like this one.
Just like with ggplot2
, the mappings you need depend on the type of plot you are creating. For example, scatterplots (type = "scatter"
) need x
and y
defined, while a surface plot (type = "surface"
) can be created with a single vector of elevation, using a mapping to the z aesthetic.
The plotly
package is designed so you can pipe data into plot_ly
and add elements by piping into add_*
functions (this idea is similar to adding elements to a ggplot
object with +
). For example, you could create the same scatterplot we just created by piping the World Cup data into plotly
, and then piping that output to add_markers
, without needing to specify that the type of plot should be a scatterplot as we did in the last code chunk:
%>%
worldcup plot_ly(x = ~ Time, y = ~ Shots, color = ~ Position) %>%
add_markers()
The add_*
functions for plotly include:
add_markers
add_lines
add_paths
add_polygons
add_segments
add_histogram
If you pipe to the rangeslider
function, it allows the viewer to zoom in on part of the x range. This functionality can be particularly nice for time series. For example, you can read in data on the maximum winds for Hurricane Floyd at different points along its track. You can pipe the result of reading in the csv directly into the plot_ly
call. To show a time series of wind speeds, map the time stamp to the x aesthetic and the wind to the y aesthetic. You can then add a line and range slider:
read_csv("data/floyd_track.csv") %>%
plot_ly(x = ~ datetime, y = ~ max_wind) %>%
add_lines() %>%
rangeslider()
Notice that, in the output, you can change the range of data plotted in the top graph by interactively adjusting the window shown in the lower plot.
You can make a 3-D scatterplot with plot_ly
by mapping a variable to the z
variable. For example, to plot a scatter plot of time, shots, and passes in the World Cup 2010 data, you can run (note that size
is set with a constant value to make the points larger):
%>%
worldcup plot_ly(x = ~ Time, y = ~ Shots, z = ~ Passes,
color = ~ Position, size = I(3)) %>%
add_markers()
Again, if you move the cursor over the points in the scatterplot, you can see the value of the point. Further, the tool bar above the plot includes buttons that allow you to rotate the plot and look at it from different angles.
Similarly, you can create 3-D surface plots with the plot_ly
function. In this case, if you have a matrix of data regularly spaced on x- and y-dimensions, with the cell values in the matrix giving values of a third variable, you can create a surface map with plot_ly
by mapping the matrix values to the z aesthetic. The helpfile for plot_ly
includes an example using the volcano
data that comes with R. This data is in a matrix format, and each value gives the elevation for a particular pair of x- and y-coordinates for a volcano in New Zealand.
class(volcano)
1] "matrix" "array"
[1:4, 1:4]
volcano[1] [,2] [,3] [,4]
[,1,] 100 100 101 101
[2,] 101 101 102 102
[3,] 102 102 103 103
[4,] 103 103 104 104 [
You can use the following code to create a 3-D surface plot of this data.
plot_ly(z = ~ volcano, type = "surface")
The other way to create a plotly
graph is to first create a ggplot
object and then transform it into an interactive graphic using the ggplotly
function.
Earlier in this subsection, we used plot_ly
to create an interactive scatterplot with the World Cup. We could have created the same plot by first creating a ggplot object with the scatterplot and then passing it to the ggplotly
function:
<- worldcup %>%
worldcup_scatter ggplot(aes(x = Time, y = Shots, color = Position)) +
geom_point()
ggplotly(worldcup_scatter)
W> If you get an error when you try this code, make sure you have the latest versions of ggplot2
and plotly
installed. It may be necessary for you to install the development version of plotly
directly from GitHub, which you can do using devtools::install_github("ropensci/plotly")
.
If you would like to find out more about what you can do with the plotly
package, the creator of the package has written a bookdown book on the package that you can read here.
4.4.3 Leaflet
Leaflet is a JavaScript library that you can use to create very attractive interactive maps. You will recognize the output, as maps created using Leaflet are now very common on websites. You can find out more about the JavaScript version here: http://leafletjs.com. The leaflet
package allows you to create these maps from within R. As with other htmlWidgets, you can explore these maps in the “Viewer” pane of RStudio and also add them to HTML R Markdown output and Shiny web applications.
For the examples in these section, we’ll use the data on fatal accidents and census tracts in Denver, Colorado. This data is contained in the denver_tracts
and driver_data
datasets created in an earlier subsection of the book. If you need to, you can reload those using the following code (replace the filepath in the load
call with the filepath to where you have saved this example data on your own computer):
library(tigris)
<- tracts(state = "CO", county = 31, cb = TRUE,
denver_tracts class = "sp")
in proj4string(obj): CRS object has comment, which is lost in output
Warning load("data/fars_colorado.RData")
<- driver_data %>%
denver_fars filter(county == 31 & longitud < -104.5)
To start creating a leaflet map in R, you need to initialize a leaflet object (this is similar to how you initialize a ggplot object when creating plots with ggplot2
). You do this with the leaflet
function. If you just run leaflet()
without adding any elements, however, you just get a blank leaflet area:
library(leaflet)
leaflet()
In leaflet
, the map background is composed of map tiles, which you can pull from a number of different sources. To get a background for your leaflet map, you’ll need to add tiles to the object created by leaflet
. If you don’t add any elements other than tiles, the leaflet map will zoom out to show the world:
leaflet() %>%
addTiles()
Once you have a leaflet object and map tiles, you’ll add other elements to show your data. This is similar to adding geoms to a ggplot object.
A common element you’ll want to add are points showing locations. You can add points using markers (these will give the map “pins” you may be familiar with from Google maps) or circle markers. You add these elements by piping the current leaflet object into an addMarkers
or addCircleMarkers
function. These functions can input either a dataframe of data or a spatial object (SpatialPoints
, SpatialLines
, etc.).
For example, to add markers for all the fatal accidents from the Denver dataset, you can call (note: this data is by driver, so there will be a point for every car involved in each accident):
leaflet() %>%
addTiles() %>%
addMarkers(data = denver_fars, lng = ~ longitud, lat = ~ latitude)
In the call to addMarkers
, the lng
and lat
parameters tell R which columns contain data on longitude and latitude for each point. These parameters are not needed if you are using a spatial object (e.g., SpatialPointsDataFrame
). Further, R will try to guess which columns show longitude and latitude in a regular dataframe if you do not specify these parameters.
To use circles for your markers instead of pins, use addCircleMarkers
. You can adjust the circle size with the radius
parameter.
leaflet() %>%
addTiles() %>%
addCircleMarkers(data = denver_fars, radius = 2,
lng = ~ longitud, lat = ~ latitude)
If you have a lot of overlapping data, you may prefer to use the clusterOptions
argument when adding markers. When using this option, markers are shown as clusters that group together when you zoom out but split up when you zoom in, so they can be useful when you have very dense points you would like to map, as in this example.
leaflet() %>%
addTiles() %>%
addMarkers(data = denver_fars,
lng = ~ longitud, lat = ~ latitude,
clusterOptions = markerClusterOptions())
The background map comes from the map tiles you add to the leaflet object. For the background, the default is to use map tiles from OpenStreetMap. However, you can use different tiles by changing the source of the tiles. To do this, use the addProviderTiles
function in place of the addTiles
function and specify the provider of the tiles you would like to use. To figure out what you would like to use, you can see previews of provider choices here: http://leaflet-extras.github.io/leaflet-providers/preview/index.html.
For example, to use Stamen watercolor map tiles, you can call:
leaflet() %>%
addProviderTiles("Stamen.Watercolor") %>%
addCircleMarkers(data = denver_fars, radius = 2,
lng = ~ longitud, lat = ~ latitude)
Similarly, to use OpenTopoMap tiles, you can all:
leaflet() %>%
addProviderTiles("Thunderforest.TransportDark") %>%
addCircleMarkers(data = denver_fars, radius = 2, color = I("red"),
lng = ~ longitud, lat = ~ latitude)
NOTE: To use provider tiles you may need to register a separate API key. Make sure to check the documentation before using provider tiles.
You can also add pop-ups that show information about a point when a user clicks on the point. To do this, use the popup
option in the function in the function where you add the element to the leaflet object. The popup
parameter requires a character vector, so if you want to show something currently in a different class vector, wrap it in paste
. For example, to add popups giving the age of the driver for the map of accidents, you can run:
leaflet() %>%
addTiles() %>%
addCircleMarkers(data = denver_fars, radius = 2,
lng = ~ longitud, lat = ~ latitude,
popup = ~ paste(age))
You can build nicely formatted popups by adding HTML tags into the character string for the pop-up. For example, to make it clearer to viewers that the pop-up is showing age, you could use paste
and some HTML formatting to create the character string for the popup
parameter.
leaflet() %>%
addTiles() %>%
addCircleMarkers(data = denver_fars, radius = 2,
lng = ~ longitud, lat = ~ latitude,
popup = ~ paste("<b>Driver age:</b>", age))
If you are going to make more complex pop-ups, you might want to create a column with the pop-up strings before passing the data into the leaflet call. For example, you could create pop-ups that show driver age, the date and time of the accident, and blood alcohol content if that data is available:
<- denver_fars %>%
denver_fars mutate(popup_info = paste("<b>Driver age:</b>", age, "<br />",
"<b>Date:</b>", format(date, "%Y-%m-%d"), "<br />",
"<b>Time:</b>", format(date, "%H:%M"), "<br />"),
popup_info = ifelse(!is.na(alc_res),
paste(popup_info,
"<b>Blood alcohol</b>", alc_res, "<br />"),
popup_info))
%>%
denver_fars leaflet() %>%
addTiles() %>%
addCircleMarkers(radius = 2, lng = ~ longitud, lat = ~ latitude,
popup = ~ popup_info)
In the popups, you can use HTML to format things like color, typeface, and size. You can also add links.
To use color to show a value, you need to do a few things. First, you need to the the colorFactor
function (or another in its family) to create a function for mapping from values to colors. Then, you need to use this within the call to add the markers. For example, the drunk_dr
column in the denver_fars
data gives the number of drunk drivers involved in an accident. You can use the following code to show that value using color in the leaflet map:
library(viridis)
<- colorFactor(viridis(5), denver_fars$drunk_dr)
pal leaflet() %>%
addTiles() %>%
addCircleMarkers(data = denver_fars, radius = 2,
lng = ~ longitud, lat = ~ latitude,
popup = ~ popup_info,
color = ~ pal(drunk_dr))
The colorFactor
function (and related functions) actually creates a new function, which is why its syntax in this call is a bit different than the syntax used to set other parameters. Note that in this code we are using the viridis
function from the viridis
package within the pal
call to use a viridis color palette for the points.
Once you have mapped a variable to color, you can add a legend to explain the mapping. You can do that with the addLegend
function, which must include values for the color palette and values for each point from this color palette.
library(viridis)
<- colorFactor(viridis(5), denver_fars$drunk_dr)
pal leaflet() %>%
addTiles() %>%
addCircleMarkers(data = denver_fars, radius = 2,
lng = ~ longitud, lat = ~ latitude,
popup = ~ popup_info,
color = ~ pal(drunk_dr)) %>%
addLegend(pal = pal, values = denver_fars$drunk_dr)
You can add polygons to leaflet objects with the addPolygons
function. For example, you can use the following code to add the census tract boundaries for Denver to a leaflet object:
leaflet() %>%
addTiles() %>%
addPolygons(data = denver_tracts)
You can add popups for polygons, as well:
leaflet() %>%
addTiles() %>%
addPolygons(data = denver_tracts,
popup = paste0("Tract ID: ", denver_tracts@data$NAME))
Note that, because the denver_tracts
object is a spatial object, we’ve used @data
to pull a value from the spatial objects attribute dataframe to use in the pop-ups, but we do not need to specify lat
or lng
in the addPolygons
call.
You can overlay multiple elements on a leaflet map. For example, you add elements to show both accidents and tracts by adding accidents using markers and adding census tracts using polygons:
leaflet() %>%
addTiles() %>%
addPolygons(data = denver_tracts,
popup = paste0("Tract ID: ", denver_tracts@data$NAME),
color = "#000000", fillColor = "969696",
weight = 2) %>%
addCircleMarkers(data = denver_fars, lat = ~ latitude,
lng = ~ longitud, radius = 2,
popup = ~ popup_info, opacity = 0.9,
color = ~ pal(drunk_dr)) %>%
addLegend(pal = pal, values = denver_fars$drunk_dr, opacity = 0.9)
You can allow the user to pick which layers to show on the graph by adding addLayersControls
. When using this function, add group
specifications to each of your map layers, and then specify which to include as overlays in the overlayGroups
parameter of addLayersControl
. For example, this code adds layer control to the map of Denver accidents:
leaflet() %>%
addTiles() %>%
addPolygons(data = denver_tracts,
popup = paste0("Tract ID: ", denver_tracts@data$NAME),
color = "#000000", fillColor = "969696",
weight = 2, group = "tracts") %>%
addCircleMarkers(data = denver_fars, lat = ~ latitude,
lng = ~ longitud, radius = 2,
popup = ~ popup_info, opacity = 0.9,
color = ~ pal(drunk_dr),
group = "accidents") %>%
addLegend(pal = pal, values = denver_fars$drunk_dr, opacity = 0.9) %>%
addLayersControl(baseGroups = c("base map"),
overlayGroups = c("tracts", "accidents"))
To find out more about using the R leaflet
package, including tips for including leaflet maps in R Shiny applications, see http://rstudio.github.io/leaflet/.
4.4.4 Creating your own widget
If you find a JavaScript visualization library and would like to create bindings to R, you can create your own package for a new htmlWidget.
There is advice on creating your own widget for R available at http://www.htmlwidgets.org/develop_intro.html.