Shiny app with crosstalk and different interactive plots and tables

Viewed 70

I want to build a shiny application that allows the user to interact with different plots and tables that are linked. To be precise, plot1 shows the raw data as a scatter plot, plot2 shows the data in an aggregated barplot, and finally table1 shows the data aggregated by another variable.

For example using ggplot2::mpg, I want hwy vs cty in plot1; plot2 shows the average hwy by manufacturer; and table1 shows the average hwy by drv. The important bit is that when the user selects drv == "r" in the table, plot1 and plot2 should be reactive to that. Similarly, if a range is selected in plot1, then plot2 and table1 should show the values for the filtered data only, similarly, if a group is excluded by clicking on the legend in plot2, eg drv != "r", then that should be applied to the other data as well.

Using basic shiny I would create a reactive dataset which is filtered by the selection of each plot/table, but this feels a little bit too complicated for the task at hand.

This seems to be the perfect example for crosstalk but I am not able to get it to work. For a simple example, I was using echarts4r for the interactivity.

MWE

An MWE looks like this:

library(dplyr)     # for aggregating the data
library(ggplot2)   # for the mpg dataset
library(shiny)     # ...
library(crosstalk) # ...
library(reactable) # interactive tables 
library(echarts4r) # interactive charts

# 1. Create the shared datasets =====
sd_raw <- SharedData$new(mpg, group = "mpg")

sd_by_man <- as_tibble(mpg) |> 
  group_by(manufacturer, drv) |> 
  summarise(n = n(), mean_hwy = mean(hwy)) |> 
  SharedData$new(group = "mpg")

sd_by_drv <- as_tibble(mpg) |> 
  group_by(drv) |> 
  summarise(n = n(), mean_hwy = mean(hwy)) |> 
  SharedData$new(group = "mpg")


# 2. Define shiny UI ====
ui <- fluidPage(
  fluidRow(
    column(4, echarts4rOutput("plot1")),
    column(4, echarts4rOutput("plot2")),
    column(4, reactableOutput("table1"))
  )
)

# 3. Define shiny Server ====
server <- function(input, output, session) {
  output$plot1 <- renderEcharts4r({
    # apparently echarts4r and group_by do not play well with sd_raw, but need
    # the $data() element
    sd_raw$data() |> 
      group_by(drv) |> 
      e_charts(hwy) |> 
      e_scatter(cty, ) |> 
      e_tooltip() |> 
      e_brush()
  })
  output$plot2 <- renderEcharts4r({
    sd_by_man$data() |> 
      group_by(drv) |> 
      e_charts(manufacturer, stack = "drv") |> 
      e_bar(mean_hwy) |> 
      e_tooltip() |> 
      e_brush()
  })
  output$table1 <- renderReactable({
    reactable(
      sd_by_drv$data(),
      selection = "multiple",
      onClick = "select",
      rowStyle = list(cursor = "pointer"),
      minRows = 10
    )
  })
}

shinyApp(ui, server)

Which results in an app like this

example app

Note that the app looks correct, but for example

  • selecting a drv in the table does not change the plots, or
  • de-selecting a drv does not change the other outputs, or
  • brushing an area on the first plot also does not change the other outputs.

Any idea how to get this interactivity to work? Can this be done using crosstalk or do I need to resort back to using basic shiny reactivity (which of course would make the app a lot more complicated...)

0 Answers
Related