Time: 2 minutes
PhD in statistics at Iowa State with Heike Hofmann & Di Cook (Dec 2016)
CEO of Sievert Consulting LLC (Jan 2017 - Present)
Expert at Library of Congress (June 2018 - Present)
I ❤️ interactive data visualization
Identify structure that otherwise goes missing (Tukey 1972).
Search for information quickly without fully specified questions1 (Unwin & Hofmann, 2000)
Diagnose, compare, and understand models (Wickham, Cook, & Hofmann 2015).
[1]: Worried about inference? See visual (Majumder et al 2013) and post-selection (Berk et al 2013) inference frameworks.
...via htmlwidgets and htmltools
Graphical (database) queries
Respond to plotly sliders, buttons, and dropdowns via plotly.js functions
Custom JavaScript via htmlwidgets::onRender()
Graphical (database) queries
Respond to plotly sliders, buttons, and dropdowns via plotly.js functions
Custom JavaScript via htmlwidgets::onRender()
library(plotly)p <- ggplot(txhousing) + geom_line(aes(date, median, group = city))ggplotly(p)
library(plotly)p <- ggplot(txhousing) + geom_line(aes(date, median, group = city, text = city))ggplotly(p, tooltip = "text")
library(plotly)tx <- highlight_key(txhousing, ~city)p <- ggplot(tx) + geom_line(aes(date, median, group = city, text = city))gg <- ggplotly(p, tooltip = "text")highlight(gg, on = "plotly_click")
gg <- ggplotly(p, tooltip = "text")highlight(gg, on = "plotly_hover", selectize = TRUE, dynamic = TRUE)
SELECT * FROM table WHERE city == "South Padre Island"
(A) Think of a question you'd like to ask of your data via a (linked) interactive graphic (bonus: draw it)!
(B) Study the code for generating this visual. How does it work?
Time: 5 minutes
tx <- txhousing %>% select(city, year, month, median) %>% filter(city %in% c("Galveston", "Midland", "Odessa", "South Padre Island"))
#> # A tibble: 748 x 4#> city year month median#> <chr> <int> <int> <dbl>#> 1 Galveston 2000 1 95000#> 2 Galveston 2000 2 100000#> 3 Galveston 2000 3 98300#> 4 Galveston 2000 4 111100#> 5 Galveston 2000 5 89200#> 6 Galveston 2000 6 108600#> 7 Galveston 2000 7 99000#> 8 Galveston 2000 8 96200#> 9 Galveston 2000 9 104000#> 10 Galveston 2000 10 118800#> # ... with 738 more rows
library(ggplot2)ggplot(tx, aes(month, median, group = year)) + geom_line() + facet_wrap(~city, ncol = 2)
Run the following R code to generate the soccer visualization:
demo("crosstalk-highlight-epl-2", package = "plotly")
plotly_json()
. What does this return?Time: 3 minutes
plotly_json()
returns the underlying JSON of any plotly graph -- nice way to learn how ggplotly()
maps to plotly.js!
schema()
returns the official plotly.js figure reference tied to the R package
ggplotly()
! plot_ly()
: 'flexible' interface to plotly.jsplot_mapbox()
: plot_ly()
wrapper/shortcut for scattermapboxplot_geo()
: plot_ly()
wrapper/shortcut for scattergeoadd_markers()
, add_bars()
, etc (see help(add_trace)
for full list)style()
to modify traces of an existing plotly graphlayout()
to add/modify to a layout componentapi_create()
api_download_plot()
/api_download_grid()
api()
subplot( plot_ly(diamonds, x = ~cut, y = ~clarity), plot_ly(diamonds, x = ~cut, color = ~clarity), nrows = 2, shareX = TRUE)
Use plotly_json()
to study how the R code (on the last slide) maps to JSON.
NOTE: We don't have time to cover plot_ly()
in depth..learn more in the plotly cookbook chapter of the plotly for R book
Time: 3 minutes
library(plotly)d <- highlight_key(mpg)dots <- plot_ly(d, color = ~class, x = ~displ, y = ~cyl)boxs <- plot_ly(d, color = ~class, x = ~class, y = ~cty) %>% add_boxplot()bars <- plot_ly(d, colors = "Set1", x = ~class, color = ~class)subplot(dots, boxs, titleX = TRUE, titleY = TRUE) %>% subplot(bars, nrows = 2, titleX = TRUE, titleY = TRUE) %>% layout( barmode = "overlay", showlegend = FALSE ) %>% highlight("plotly_selected")
library(plotly)d <- highlight_key(mtcars)sp <- plot_ly(d, x = ~mpg, y = ~disp) %>% add_markers(color = I("black"))# 'statistical trace types'hist <- plot_ly(d, x = ~factor(cyl)) %>% add_histogram(color = I("black"))box <- plot_ly(d, y = ~disp, color = I("black")) %>% add_boxplot(name = " ")violin <- plot_ly(d, y = ~disp, color = I("black")) %>% add_trace(type = "violin", name = " ")subplot(sp, box, violin, shareY = TRUE, titleX = TRUE, titleY = TRUE) %>% subplot(hist, widths = c(.75, .25), titleX = TRUE, titleY = TRUE) %>% layout( barmode = "overlay", title = "Click and drag scatterplot", showlegend = FALSE ) %>% highlight("plotly_selected")
library(plotly)tx <- highlight_key(txhousing, ~city)p1 <- ggplot(tx, aes(date, median, group = city)) + geom_line()gg1 <- ggplotly(p1, tooltip = c("city", "date", "median"))p2 <- plot_ly(tx, x = ~median, color = I("black")) %>% add_histogram(histnorm = "probability density")subplot(gg1, p2, titleX = TRUE, titleY = TRUE) %>% layout(barmode = "overlay") %>% highlight(dynamic = TRUE, selected = attrs_selected(opacity = 0.3))
library(leaflet)sd <- highlight_key(quakes)p <- plot_ly(sd, x = ~depth, y = ~mag) %>% add_markers(alpha = 0.5) %>% highlight("plotly_selected")map <- leaflet(sd) %>% addTiles() %>% addCircles()crosstalk::bscols(p, map)
library(plotly)data(trails, package = "mapview")tsd <- highlight_key(trails)crosstalk::bscols( plot_mapbox(tsd, text = ~FKN, hoverinfo = "text"), DT::datatable(tsd))
plotly has advanced support for highlight events (e.g., persistent
, dynamic
, selectize
)
Other crosstalk-enabled htmlwidgets likely won't respect (non-default) highlight()
options.
However, filter events should generally be supported.
Highlight events dim the opacity of existing marks.
Filter events completely removes existing marks and rescales axes.
At least currently, filter events must be fired from crosstalk widgets.
library(crosstalk)tx <- highlight_key(txhousing)widgets <- bscols( widths = c(12, 12, 12), filter_select("city", "Cities", tx, ~city), filter_slider("sales", "Sales", tx, ~sales), filter_checkbox("year", "Years", tx, ~year, inline = TRUE))widgets
bscols( widths = c(4, 8), widgets, plot_ly(tx, x = ~date, y = ~median, showlegend = FALSE) %>% add_lines(color = ~city, colors = "black"))
htmlwidgets::saveWidget()
to save a plotly graph (e.g. plot_ly()
). What's the size of the HTML file it creates?htmltools::save_html()
to save the plotly+leaflet example. What's the size of the HTML file it creates?saveWidget()
and save_html()
? When is one preferred to the other?Time: 5 minutes
Graphical (database) queries
Respond to plotly sliders, buttons, and dropdowns via plotly.js functions
Custom JavaScript via htmlwidgets::onRender()
styles <- schema()$layout$layoutAttributes$mapbox$style$valuesstyle_buttons <- lapply(styles, function(s) { list(label = s, method = "relayout", args = list("mapbox.style", s))})storms <- sf::st_read(system.file("shape/storms_xyz.shp", package = "sf"), quiet = TRUE)plot_mapbox(storms, color = I("red")) %>% layout( title = "Changing the base layer", updatemenus = list(list(y = 0.8, buttons = style_buttons)) )
Graphical (database) queries
Respond to plotly sliders, buttons, and dropdowns via plotly.js functions
Custom JavaScript via htmlwidgets::onRender()
The customdata
attribute provides a way to attach "meta-data" to visual attributes that you can access with JavaScript
plot_ly(mtcars, x = ~wt, y = ~mpg) %>% add_markers(customdata = ~paste0("http://google.com/#q=", rownames(mtcars))) %>% htmlwidgets::onRender("function(el, x) { el.on('plotly_click', function(d) { var url = d.points[0].customdata; window.open(url); }); }")
In the RStudio cloud project, open the 'customdata.R' script:
file.edit("~/customdata.R")
shiny::runApp("~/tutorials/20180711/shiny/01", display.mode = "showcase")
shiny::runApp("~/tutorials/20180711/shiny/02", display.mode = "showcase")
Modify the last app to use plot_ly()
instead of ggplotly()
Add output blocks that print out data from the following events:
"plotly_hover"
"plotly_click"
"plotly_relayout"
Time: 5 minutes
shiny::runApp("~/tutorials/20180711/shiny/03", display.mode = "showcase")
By default, shiny updates require a full redraw, but proxies allows us to leverage the plotly.js API to modify/update graphs more efficiently
shiny::runApp("~/tutorials/20180711/shiny/04", display.mode = "showcase")
shiny::runApp("~/tutorials/20180711/shiny/05", display.mode = "showcase")
Inspired by https://plot.ly/r/streaming/
Open the last example
file.edit("~/tutorials/20180711/shiny/05/app.R")
Modify it to do the following:
Add sliderInput()
for controlling the streaming interval.
Add a widgets to:
Time: 10 minutes
Hint: some of these shiny example apps will be helpful (e.g. proxy_restyle_economics
) -- https://github.com/ropensci/plotly/tree/master/inst/examples/shiny
Resources for more learning:
https://plotly-book.cpsievert.me
https://blog.cpsievert.me
https://talks.cpsievert.me
https://github.com/cpsievert/apps
https://github.com/cpsievert/pedestrians
https://github.com/cpsievert/bcviz
https://github.com/cpsievert/phd-thesis
Find me online:
Web: http://cpsievert.me/
Twitter: @cpsievert
GitHub: @cpsievert
Email: cpsievert1@gmail.com
Time: 2 minutes
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |