Draw ggplot on reactive value change in the background, on nonfocused tabs

不羁岁月 提交于 2019-12-24 01:18:53

问题


Objective

Increase UX fluidity by decreasing wait time.

Problem

I am building a data exploration dashboard. There are two tabs: 1) (default tab) a tab showing a datatable, and 2) a tab showing ggplots. A user can explore the data by selecting options from the sidebar (e.g. choose how many points you want to display). The table reactively updates upon a change, but the ggplot on the other tab doesn't - until the tab is selected, only then is the ggplot drawn.

This is annoying as the data was already specified but process doesn't start until the person clicks to the plot tab... thus wasting time on large datasets.

What I tried

I tried looking over SO and google for hints of this problem but I must not be searching correctly as this is seemingly an easy problem. I've thought about implementing a function that switches to every nonfocused tab for a split second after a value is specified to force a draw but this is UX non-friendly and incredibly hacky for such a small problem.

MCVE

# Load libraries
library(shiny)
library(shinydashboard)
library(DT)
library(ggplot2)
library(ggthemes)

# Set seed to arbitrary value for reproducibility
set.seed(1)

# Create random dataset of 100K points
test.df <- data.frame(a = 1:100000, b = rnorm(n = 100000, mean= 2, sd=1))

# Create ui
ui <- dashboardPage(dashboardHeader(),
                    dashboardSidebar(
                      sidebarMenu(
                        id = "tabs",
                        # Tab to show table
                        menuItem("Tab 1", tabName = "item1"),
                        # Tab to show plot
                        menuItem("Tab 1", tabName = "item2"),
                        numericInput("numeric", "Length of data", value = 1)
                    )),
                    dashboardBody(
                      tabItems(
                        tabItem("item1", dataTableOutput("table")),
                        tabItem("item2", plotOutput("plot"))
                      )
                    )
                  )

server <- function(input, output) {

  # Get first n rows
  test.df.sample <- reactive({
    head(test.df, n=input$numeric)
  })

  # Render table
  output$table <- DT::renderDataTable({
    datatable(test.df.sample())
  })

  # Render ggplot
  output$plot <-  renderPlot({
    ggplot(test.df.sample()) +
      geom_point(aes(x=a, y=b)) +
      theme_tufte()
  })
}

shinyApp(ui = ui, server = server)

Progress update

Update 1 - Posting date + 2

Under the pressure of time, I decided to implement the hacky method of making a updateTabItems() call to instantaneously switch between tabs. This method works as it triggers shiny to update each tabItem content but leaves undesired visual effects (e.g. I'm using googleway to plot a map, but it zooms out at max level, so you see a strip where the world map is repeated 4 times, if it gets updated in this hacky method)


回答1:


I think you could use the option suspendWhenHidden from the outputOptions to get the behavior you are looking for. However, that on its own does not suffice, since for this to work, the user will have to have initialized the plot once by going to the second tab. We can solve this by setting the height and width of the plot to non-zero values, see here.

Notice that now the table only renders as soon as both the table and the plot are done rendering, so that takes a little longer. So the user has to wait once for 60 seconds instead of waiting for 10 seconds six times. The advantage of the new behavior is that at least the user can get a cup of coffee and knows that everything is ready to be viewed when he gets back. I hope this was your desired behavior. If your goal was to render everything in sequence - I am not sure this is possible with Shiny I am afraid...

A working example is given below, hope this helps!

# Load libraries
library(shiny)
library(shinydashboard)
library(DT)
library(ggplot2)
library(ggthemes)

# Set seed to arbitrary value for reproducibility
set.seed(1)

# Create random dataset of 100K points
test.df <- data.frame(a = 1:100000, b = rnorm(n = 100000, mean= 2, sd=1))

# Create ui
ui <- dashboardPage(dashboardHeader(),
                    dashboardSidebar(
                      sidebarMenu(
                        id = "tabs",
                        # Tab to show table
                        menuItem("Tab 1", tabName = "item1"),
                        # Tab to show plot
                        menuItem("Tab 1", tabName = "item2"),
                        numericInput("numeric", "Length of data", value = 1)
                      )),
                    dashboardBody(
                      tabItems(
                        tabItem("item1", dataTableOutput("table")),
                        tabItem("item2", plotOutput("plot"))
                      )
                    )
)

server <- function(input, output) {

  # Get first n rows
  test.df.sample <- reactive({
    head(test.df, n=input$numeric)
  })

  # Render table
  output$table <- DT::renderDataTable({
    datatable(test.df.sample())
  })

  # Render ggplot
  output$plot <-  renderPlot({
    ggplot(test.df.sample()) +
      geom_point(aes(x=a, y=b))
    }, height = 450, width = 800)
  outputOptions(output, "plot", suspendWhenHidden = FALSE)
}

shinyApp(ui = ui, server = server)


来源:https://stackoverflow.com/questions/50710222/draw-ggplot-on-reactive-value-change-in-the-background-on-nonfocused-tabs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!