class: bottom, left background-image: url(adv-plotly-for-r.svg) background-size: contain --- ## Texas housing prices ```r library(dplyr) tx <- txhousing %>% select(city, year, month, median) %>% filter(city %in% c("Galveston", "Midland", "Odessa", "South Padre Island")) tx #> # 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 ``` --- ## Highlighting in small multiples ```r * TX <- SharedData$new(tx, ~year) p <- ggplot(TX, aes(month, median, group = year)) + geom_line() + facet_wrap(~city, ncol = 2) (gg <- ggplotly(p, tooltip = "year")) ``` <iframe src="../day1/08-small-multiples.html" width="100%" height="420" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- ## Query missing values by city <iframe src="01-pipeline.html" width="100%" height="500" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- class: bottom, left background-image: url(pipeline.svg) background-size: contain ## The 'data pipeline' --- ```r library(plotly) library(crosstalk) sd <- SharedData$new(txhousing, ~city, "Select a city") base <- plot_ly(sd, color = I("black"), height = 400) %>% group_by(city) p1 <- base %>% summarise(miss = sum(is.na(median))) %>% filter(miss > 0) %>% arrange(miss) %>% add_bars(x = ~miss, y = ~factor(city, levels = city), hoverinfo = "x+y") %>% layout( barmode = "overlay", xaxis = list(title = "Number of months missing"), yaxis = list(title = "") ) p2 <- base %>% add_lines(x = ~date, y = ~median, alpha = 0.3) %>% layout(xaxis = list(title = "")) subplot(p1, p2, titleX = TRUE, widths = c(0.3, 0.7)) %>% layout(margin = list(l = 120)) %>% hide_legend() %>% highlight(dynamic = TRUE, persistent = TRUE, selectize = TRUE) ``` --- class: inverse, middle, bottom background-image: url(https://i.imgur.com/H9vOG9f.gif) background-size: contain # Britney: So what? I can do this with shiny. --- class: inverse, bottom, left background-image: url(https://i.imgur.com/UDwYyGP.gif) background-size: contain --- class: middle, bottom background-image: url(pipeline.svg) background-size: contain # Where is the pipeline? --- background-image: url(server-client.svg) background-size: contain --- class: middle, center background-image: url(server-client.svg) background-size: contain # Standalone web pages are **much** easier to share, deploy, maintain. --- background-image: url(crosstalk.svg) background-size: contain ## The general model .footnote[ ### Links are specified in R, but the "updating logic" is JavaScript -- no server required! ] --- class: inverse, center background-image: url(../your-turn.jpeg) background-size: contain ## Your turn (1) Modify the ['pipeline' demo](https://github.com/ropensci/plotly/blob/master/demo/crosstalk-highlight-pipeline.R) so that markers (instead of bars) encode # of missing values (like it does in the diagram). (2) Highlight cities by *brushing* marker(s) (hint: linked brushing isn't be enabled by default, see `help(highlight)`) --- ## Aggregating selections <iframe src="02-binned-target-b.html" width="100%" height="400" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- ```r # These examples demonstrate ways to display binned/aggregated selections library(crosstalk) library(plotly) d <- SharedData$new(mtcars) sp <- plot_ly(d, x = ~mpg, y = ~disp) %>% add_markers(color = I("black")) # hist/box/violin are all 'statistical trace types' meaning # they compute aggregations on the fly 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", selected = attrs_selected(showlegend = FALSE) ) ``` --- ## Aggregating selections (continued) <iframe src="02-binned-target-a.html" width="100%" height="550" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- ```r # These examples demonstrate ways to display binned/aggregated selections library(crosstalk) library(plotly) d <- SharedData$new(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, x = ~class, color = ~class) subplot(dots, boxs, titleX = TRUE, titleY = TRUE) %>% subplot(bars, nrows = 2, titleX = TRUE, titleY = TRUE) %>% layout( title = "Click and drag on scatterplot", barmode = "overlay", showlegend = FALSE ) %>% highlight("plotly_selected") ``` --- ## Aggregating selections (continued) <iframe src="02-binned-target-c.html" width="100%" height="500" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- ```r # These examples demonstrate ways to display binned/aggregated selections library(crosstalk) library(plotly) tx <- SharedData$new(txhousing, ~city) p1 <- ggplot(tx, aes(date, median, group = city)) + geom_line() + xlab(NULL) 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, persistent = TRUE, selected = attrs_selected(opacity = 0.3) ) ``` --- class: middle background-image: url(https://i.imgur.com/jQXFzZw.gif) background-size: contain ## Selections inherit animation frames! .footnote[ See <https://plotly-book.cpsievert.me/linking-animated-views.html> ] --- class: inverse background-image: url(../your-turn.jpeg) background-size: contain <h2 align="center"> Your turn </h2> Check out one or two more demos that come with the package ```r demo(package = "plotly") ``` .footnote[ **PS** * Once you have a topic, say "ternary", you can run it like this ```r demo("ternary", package = "plotly") ``` * If you'd like to see/edit the source code, do something like ```r file.edit(system.file("demo", "ternary.R", package = "plotly")) ``` ] --- class: inverse, center, middle # You can only go so far without shiny... --- class: bottom background-image: url(https://i.imgur.com/T7GSpv9.gif) background-size: contain ### https://github.com/cpsievert/zikar --- class: bottom background-image: url(https://i.imgur.com/csIUJX0.gif) background-size: contain ### https://github.com/cpsievert/apps --- class: center, middle background-image: url(https://i.imgur.com/tlRv98y.gif) background-size: contain # ...but we can combine powers --- class: bottom background-image: url(https://i.imgur.com/MGbR5AI.gif) background-size: contain ### https://github.com/cpsievert/bcviz --- class: center, middle # I promise... We will get to shiny, but we haven't covered *filter* events yet! --- class: inverse, middle, center # Filter vs highlight The `highlight()` function sets options for *highlight events*. *Highlight* events **dim the opacity** of existing marks. *Filter* events **completely removes** existing marks and rescales axes.<sup>1</sup> At least currently, *filter* events must be fired from **crosstalk** widgets. .footnote[ [1]: when using `ggplotly()`, you need to specify `dynamicTicks = TRUE` ] --- ## Crosstalk's filtering widgets ```r tx <- SharedData$new(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 ``` <iframe src="03-filter-widgets.html" width="100%" height="420" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- ## Filtering ```r bscols( widths = c(4, 8), widgets, plot_ly(tx, x = ~date, y = ~median, showlegend = FALSE) %>% add_lines(color = ~city, colors = "black") ) ``` <iframe src="04-filter.html" width="100%" height="420" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- ## Talk to other crosstalk-enabled widgets ```r library(leaflet) sd <- SharedData$new(quakes) p <- plot_ly(sd, x = ~depth, y = ~mag) %>% add_markers(alpha = 0.5) %>% highlight("plotly_selected") map <- leaflet(sd) %>% addTiles() %>% addCircles() bscols(p, map) ``` <iframe src="05-leaflet.html" width="100%" height="420" scrolling="no" seamless="seamless" frameBorder="0"> </iframe> --- class: inverse, center background-image: url(../your-turn.jpeg) background-size: contain ## Your turn Add some filter widgets to the earthquakes example. Use `htmltools::save_html()` to save the result Recreate the `leaflet()` map with `plot_mapbox()` .footnote[ Full solution is in `your-turn/01.R` file ] --- class: center, middle, inverse ## Expectations vs reality .pull-left[ <img src="https://i.imgur.com/fZIenVE.jpg" height = "500" /> ] .pull-right[ <br /> <br /> <br /> <br /> **plotly** has advanced support for *highlight* events (e.g., `persistent`, `dynamic`, `selectize`) Other [**crosstalk**-enabled htmlwidgets](https://rstudio.github.io/crosstalk/) likely won't respect (non-default) `highlight()` options. However, *filter* events should generally be supported. ] --- class: center, middle # Hello <img src="https://www.rstudio.com/wp-content/uploads/2014/04/shiny.png" width=100 /> 👋 ```r shiny::runApp("~/day2/shiny/01", display.mode = "showcase") ``` --- class: center, middle # Accessing plotly user events ```r shiny::runApp("~/day2/shiny/02", display.mode = "showcase") ``` --- class: inverse, center background-image: url(../your-turn.jpeg) background-size: contain ## Your turn Program an app to populate a bar chart reflecting the selection, sort of like this (using `cars` data): <div align="center" > <img src="events-plotly-linked.gif" width="600" height="500" /> </div> .footnote[ [Full solution](https://github.com/cpsievert/apps/blob/master/shiny/apps/plotlyLinkedBrush/app.R) [Partial solution, <br /> without shiny](your-turn/02.R) ] --- class: center, middle # Targetting events (only if we have time) ```r shiny::runApp("~/day2/shiny/03", display.mode = "showcase") ``` .footnote[ See also <https://plotly-book.cpsievert.me/linking-views-with-shiny.html#targeting-views> ] --- class: center, middle # plotly proxies By default, **shiny updates require a full redraw**, but proxies allows us to leverage [the plotly.js API](https://plot.ly/javascript/plotlyjs-function-reference/) to modify/update graphs more efficiently ```r shiny::runApp("~/day2/shiny/04", display.mode = "showcase") ``` --- class: center, middle # Streaming data ```r shiny::runApp("~/day2/shiny/05", display.mode = "showcase") ``` .footnote[ Inspired by <https://plot.ly/r/streaming/> ] --- class: inverse background-image: url(../your-turn.jpeg) background-size: contain <h2 align="center"> Your turn </h2> Open the last example ```r file.edit("day2/shiny/05/app.R") ``` Try to do the following: 1. Add `sliderInput()` for controlling the streaming interval and the number of points added in each update. 2. Add another (streaming) trace .footnote[ ### This is the last your turn, and I have a 6pm flight to catch, so ask me any other questions now! ] --- background-image: url(plotly.svg) background-size: 100px background-position: 90% 8% class: center, middle # Thanks! Resources for more learning: https://plot.ly/r/ <br /> https://plotly-book.cpsievert.me <br /> https://talks.cpsievert.me <br /> https://github.com/cpsievert/phd-thesis <br /> https://github.com/cpsievert/pedestrians <br /> https://github.com/cpsievert/bcviz <br /> https://github.com/cpsievert/apps <br /> Reach out to me Web: <http://cpsievert.me/> <br /> Twitter: [@cpsievert](https://twitter.com/cpsievert) <br /> GitHub: [@cpsievert](https://github.com/cpsievert) <br /> Email: <cpsievert1@gmail.com> --- background-image: url(plotly.svg) background-size: contain # Ask me anything!! .footnote[ Want something to do? Some ideas: 1. Read about the new api interface -- `help(api)`. <br /> 2. Read about [JavaScript customization](https://cpsievert.github.io/plotly_book/custom-behavior-via-javascript.html) ]