--- title: "Animated graphics in R" author: Abhijit Dasgupta, PhD abstract: Animated graphics in R, with mouseovers --- ```{r setup, include=FALSE, child = here::here('slides/templates/setup.Rmd')} ``` ```{r setup1, include=FALSE} pacman::p_load(htmlwidgets) pacman::p_load(plotly) pacman::p_load(widgetframe) theme_set(theme_classic()+theme(axis.text = element_text(size=14), axis.title = element_text(size=16), legend.text = element_text(size=14), legend.title = element_text(size=16))) Sys.setenv("RSTUDIO_PANDOC" = '/usr/bin/') ``` layout: true
BIOF 339: Practical R
--- class: middle,inverse # Dynamic graphics --- ## Dynamic graphics in R There are several package in R that provide dynamic graphics meant to be consumed on the web. Many of these are wrappers around well-known Javascript libraries like D3.js, leaflet.js and others These packages have mostly come under the umbrella of of the `htmlwidgets` package, which allows these HTML-based graphics to be displayed through R and R Markdown --- ## Dynamic graphics in R There are several broad categories of dynamic graphs General purpose: ```{r, echo=FALSE } tribble( ~Package, ~Description, 'r2d3' , 'Interface for D3.js, requires D3.js code', 'plotly', 'Interface with plot.ly, direct conversion from ggplot2', 'highcharter', 'Using the Highcharts.js package', 'dygraphs', 'For time series or longitudinal data' ) %>% knitr::kable() %>% kable_styling() ``` Maps: ```{r } tribble( ~Package, ~Description, "leaflet", "Maps using OpenStreetMaps") %>% kable() %>% kable_styling() ``` --- ## Dynamic graphics in R Networks: ```{r } tribble(~Package, ~Description, 'networkD3', 'Dynamic network visualizations using D3', 'visNetwork', 'Interface to the vis.js pacman::p_load') %>% kable() %>% kable_styling() ``` --- class: middle, inverse # Plotly https://plotly.com/graphing-libraries --- ## Plotly Plotly is a company that developed the `plotly.js` graphing pacman::p_load, as well as packages for R and Python. For the R package, it developed a turnkey method to convert `ggplot2` graphics into interactive graphs. .pull-left[ ```{r g1, echo=TRUE, fig.height=3} plt <- ggplot(penguins, aes(bill_length_mm, body_mass_g, color = species))+ geom_point() plt ``` ] .pull-right[ ```{r , echo=TRUE, fig.height=4} ggplotly(plt) ``` ] --- ## Plotly You can do some customization on the tooltips (what shows up when you put your mouse over a point). ```{r , fig.height=5, fig.width=12, echo=TRUE} p <- ggplot(data = diamonds, aes(x = cut, fill = clarity))+ geom_bar(position = 'dodge') ggplotly(p, tooltip = c('cut')) #<< ``` --- ## Plotly You can do linked plots in plotly, so interactions in one plot are reflected in a second plot. This is called _brushing_. The key here is to use `highlight_key`, which allows a data frame to be shared between multiple plots at the same time. ```{r brush, echo=T, fig.height=4, fig.width=9} d <- highlight_key(penguins) plt1 <- ggplot(d, aes(x = bill_length_mm, y = body_mass_g))+geom_point() plt2 <- ggplot(d, aes(x = bill_length_mm, y = flipper_length_mm))+geom_point() subplot(plt1, plt2) %>% layout(title = "Click and drag to select points") %>% highlight("plotly_selected") ``` --- ## Plotly You can also do brushing over multiple plots drawn on the same dataset .left-column70[ ```{r brush2, echo=TRUE, fig.height=5} highlight_key(iris) %>% GGally::ggpairs(aes(colour = Species), columns = 1:4) %>% ggplotly(tooltip = c("x", "y", "colour")) %>% highlight("plotly_selected") ``` ] .right-column70[ **GGally** is a **ggplot2** extension that provides additional composite plot types like the pairs plot we use here. ] --- class: middle, inverse # Dygraphs https://rstudio.github.io/dygraphs --- ## Dygraphs The *dygraphs* package wraps the `dygraphs` Javascript pacman::p_load, which is build to chart time-series data. ```{r dyg1, echo=TRUE, fig.height=6} pacman::p_load(dygraphs) dygraph(nhtemp, main = "New Haven temperatures") %>% dyRangeSelector(dateWindow = c("1920-01-01","1960-01-01")) ``` ------------------------------------------------------------------------------- This package is built for linked charts, in the form of line charts + annotations + controls --- ## Dygraphs We can also create multiple linked time series .left-column30[ ```{r dyg2, echo=TRUE, eval=FALSE} dygraph(ldeaths, main = "All", group = "lung-deaths") #<< dygraph(mdeaths, main = "Male", group = "lung-deaths") #<< dygraph(fdeaths, main = "Female", group = "lung-deaths") #<< ``` You could name the group anything you like, as long as it's the same across the plots. ] .right-column30[ ```{r dyg22, echo=FALSE, eval=TRUE, fig.height=2, ref.label='dyg2'} ``` ] --- ## Dygraphs You could also annotate dygraphs ```{r ,echo=TRUE, fig.height=5} dygraph(presidents, main = "Quarterly Presidential Approval Ratings") %>% dyAxis("y", valueRange = c(0, 100)) %>% dyEvent("1950-6-30", "Korea", labelLoc = "bottom") %>% dyEvent("1965-2-09", "Vietnam", labelLoc = "bottom") ``` --- class: middle, inverse # Highcharter https://jkunst.com/highcharter/index.html --- ## Highcharter + The **highcharter** package provides a rich set of plotting functions for dynamic graphics + The R package is, in spirit, similar to **ggplot2** + It uses *mappings*, *aesthetics* and *geometries* (called *types*) + Customization can be added using additional functions in a pipe + Very rich set of chart types --- ## Highcharter .pull-left[ ```{r hc1, echo=T, eval=FALSE} pacman::p_load(highcharter) hchart(object=penguins, type="scatter", mapping = hcaes(x = body_mass_g, y = flipper_length_mm , group = species)) %>% hc_legend(align='right', verticalAlign='top') ``` See how the elements are similar to what you'd put in a **ggplot2** pipe, except they are in a single function Options can be added with pipes ] .pull-right[ ```{r , echo=F, eval=T, fig.height=4, ref.label='hc1'} ``` ] --- ## Highcharter Unlike **ggplot2**, the object to be plotted doesn't have to be a data frame .pull-left[ ```{r hc2, echo=T, eval=F} hc1=hchart(diamonds$cut, type='column') hc1 ``` ] .pull-right[ ```{r ,ref.label='hc2', eval=T} ``` ] --- ## Highcharter We can make some more complex plots using **highcharter** .pull-left[ ```{r , eval=FALSE, echo=TRUE} pacman::p_load(survival) pacman::p_load(widgetframe) data(lung) lung <- dplyr::mutate(lung, sex = ifelse(sex == 1, "Male", "Female")) fit <- survfit(Surv(time, status) ~ sex, data = lung) hchart(fit, ranges=TRUE) ``` ] .pull-right[ ```{r , eval=TRUE, echo=FALSE} pacman::p_load(survival) pacman::p_load(widgetframe) data(lung) lung <- dplyr::mutate(lung,sex = ifelse(sex == 1, "Male", "Female")) fit <- survfit(Surv(time, status) ~ sex, data = lung) hc <- hchart(fit, ranges=TRUE) frameWidget(hc) ``` ] --- ## Highcharter .pull-left[ ```{r, echo=TRUE, eval=FALSE} pacman::p_load(highcharter) mtcars2 <- mtcars[1:20, ] x <- dist(mtcars2) hchart(x) ``` ] .pull-right[ ```{r, echo=FALSE, eval=TRUE} pacman::p_load(highcharter) mtcars2 <- mtcars[1:20, ] x <- dist(mtcars2) frameWidget(hchart(x)) ``` ] --- ## Highcharter ### Licensing issues ### Highcharter has a dependency on Highcharts, a commercial JavaScript charting pacman::p_load. Highcharts offers both a commercial license as well as a free non-commercial license. Please review the licensing options and terms before using this software, as the highcharter license neither provides nor implies a license for Highcharts. Highcharts (http://highcharts.com) is a Highsoft product which is not free for commercial and Governmental use. --- class: middle, inverse # rbokeh http://hafen.github.io/rbokeh/ --- ## rbokeh **rbokeh** wraps the **bokeh** plotting pacman::p_load in Python It uses a layering concept similar to **ggplot2** --- .pull-left[ ```{r rb1, echo=TRUE, eval=TRUE} pacman::p_load(rbokeh) p <- figure(width=400,height=400) %>% ly_points(bill_length_mm, body_mass_g, data=penguins, color=species, glyph = species, hover=list(bill_length_mm,body_mass_g)) p ``` ] .pull-right[ ```{r, echo=TRUE } z <- lm(dist ~ speed, data = cars) p <- figure(width = 400, height = 400) %>% ly_points(cars, hover = cars) %>% ly_lines(lowess(cars), legend = "lowess") %>% ly_abline(z, type = 2, legend = "lm") p ``` ] --- class: middle, inverse # Maps --- ## Maps We've already seen maps with **leaflet** that allow data to be overlayed over cartographic maps Using the packages introduced here, we also have mapping capabilities. See the respective websites for details .pull-left[ **rbokeh** ```{r , echo=FALSE} pacman::p_load(maps) data(world.cities) caps <- subset(world.cities, capital == 1) caps$population <- prettyNum(caps$pop, big.mark = ",") figure(width = 400, height = 250, padding_factor = 0) %>% ly_map("world", col = "gray") %>% ly_points(long, lat, data = caps, size = 5, hover = c(name, country.etc, population)) ``` ] .pull-right[ **highcharter** ```{r , echo=FALSE} data <- tibble( country = c("PT", "IE", "GB", "IS", "NO", "SE", "DK", "DE", "NL", "BE", "LU", "ES", "FR", "PL", "CZ", "AT", "CH", "LI", "SK", "HU", "SI", "IT", "SM", "HR", "BA", "YF", "ME", "AL", "MK", "FI", "EE", "LV", "LT", "BY", "UA", "MD", "RO", "BG", "GR", "TR", "CY", "RU"), tz = c(rep("UTC", 4), rep("UTC + 1",25), rep("UCT + 2",12), "UTC + 3") ) # auxiliar variable data <- data %>% mutate(value = cumsum(!duplicated(tz))) # now we'll create the dataClasses dta_clss <- data %>% mutate(value = cumsum(!duplicated(tz))) %>% group_by(tz) %>% summarise(value = unique(value)) %>% arrange(value) %>% rename(name = tz, from = value) %>% mutate(to = from + 1) %>% list_parse() hcmap( map = "custom/europe", data = data, joinBy = c("iso-a2","country"), name = "Time zone", value = "value", tooltip = list(pointFormat = "{point.name} {point.tz}"), dataLabels = list(enabled = TRUE, format = "{point.country}") ) %>% hc_colorAxis( dataClassColor = "category", dataClasses = dta_clss ) %>% hc_title(text = "Europe Time Zones") %>% hc_size(width=400, height=250) ``` ] --- class: middle, inverse # Using D3.js in R --- ## r2d3 [D3.js](https://d3js.org) is a state-of-the-art Javascript pacman::p_load for data-driven graphics It is widely used in data journalism, including the New York Times and the Guardian However, it controls each aspect of a chart and so can require rather long JS scripts --- ## r2d3 If you know D3.js, you can use it pretty easily embed them in R using the **r2d3** package ```{r echo=TRUE, eval=TRUE} pacman::p_load(r2d3) r2d3(data = read_csv('../data/flare.csv'), d3_version = 4, script = 'bubbles.js') ``` --- ## r2d3 ```{r echo=TRUE} r2d3(data = jsonlite::read_json('../data/miserables.json'), d3_version = 4, script = 'forcegraph.js') ``` --- ## networkD3 You can also draw networks slightly more easily using the **networkD3** package. .pull-left[ ```{r nd3, echo=T, eval=F} pacman::p_load(networkD3) lemis <- jsonlite::fromJSON('../data/miserables.json') lemis$links <- lemis$links %>% mutate(ID = 1:n()) %>% gather(location, nm, source, target) %>% mutate(nm = as.integer(as.factor(nm))-1) %>% spread(location, nm) forceNetwork(Links = lemis$links, Nodes = lemis$nodes, Source='source', Target='target', Value = 'value', NodeID = 'id', Group = 'group', opacity=0.7, zoom = TRUE, fontSize = 20) ``` ] .pull-right[ ```{r , echo=FALSE, eval=TRUE, ref.label="nd3"} ``` ] --- ## Crosstalk **Crosstalk** is a package that allows multiple HTML widgets to share data and interact together. Not all packages in the **htmlwidgets** family are **crosstalk**-compatible. ```{r , echo=TRUE, eval=FALSE} load('data/exdata.rda') pacman::p_load(leaflet); pacman::p_load(crosstalk); pacman::p_load(d3scatter) gpx1 <- gpx %>% mutate(Minutes = as.numeric(difftime(Time, min(Time), units = 'mins'))) %>% filter(Minutes <= 50) %>% left_join(run_data) shared_gpx1 <- SharedData$new(gpx1) bscols( leaflet(data = shared_gpx1, width="100%", height=450) %>% addTiles() %>% addCircleMarkers(~Longitude, ~Latitude, radius=1, color='blue'), list( d3scatter(shared_gpx1, ~Minutes, ~ HR, width="100%", height=450) ) ) bscols( filter_slider("Minutes","Time", shared_gpx1, ~Minutes, width="100%") ) ``` .center[`leaflet` + `d3scatter` + `crosstalk`] --- ## Linked graphs --- ## Conclusions + There are many many resources today to create interactive graphics + For many of them, there are wrappers in R + You can explore the respective packages for many more details about the different kinds of charts and customizations that are possible.