4.4.1 Overview of htmlWidgets
There is a website with much more on these
dygraphs: Time series
plotly: A variety of plots, including maps
rbokeh: A variety of plots, including maps
networkD3: Network data
DT: Data tables
DiagrammeR: Diagrams and flowcharts
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.
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.
plotly.js package, an open source library for creating interactive graphs. The
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
Like some of the other packages we’ve looked at (e.g.,
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
- Use one of the package’s functions to create a customized interactive graphic:
plot_ly: Workhorse of
plotly, renders most non-map types of graphs
plot_mapbax: Specific functions for creating
- Create a
ggplotobject and then convert it to a
plotlyobject using the
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
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
%>% 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
y defined, while a surface plot (
type = "surface") can be created with a single vector of elevation, using a mapping to the z aesthetic.
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()
add_* functions for plotly include:
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 [,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
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
<- 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
plotly installed. It may be necessary for you to install the development version of
plotly directly from GitHub, which you can do using
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.
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
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:
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
addCircleMarkers function. These functions can input either a dataframe of data or a spatial object (
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
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
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
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))
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
lng in the
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"))