Asynchronous Programming in R with the Future Package - r

I am new to asynchronous programming in R with the Future Package so needed some help. I am trying to build a simple application with rshiny which supports asynchronous programming. So my code as a histogram plot, a slider, a simple text print and read.csv function to read a large CSV file. So my plan is before my read.csv function runs in the background using the future package in R, I would like to have control over my other application.
But my code waits for the CSV file to read. Any help will be appreciated. The code sample is below.
library(promises)
library(future)
library(shinydashboard)
library(shiny)
library(tidyverse)
plan(multiprocess)
#UI parts
ui <- dashboardBody(fluidRow(box(tableOutput("input1")),
box(textOutput("input2"))),
fluidRow(box(
sliderInput(
inputId = "bins",
label = "Number of bins:",
min = 1,
max = 5,
value = 2
)
),
box(plotOutput(outputId = "distPlot"))),
fluidRow(box(
sliderInput(
"slider2",
label = h3("Slider Range"),
min = 0,
max = 100,
value = c(40, 60)
)
),
box(verbatimTextOutput("range"))))
#server part
server <- function(input, output, session) {
output$input1 <- renderTable({
promise <- future((read.csv("data/sample_large.csv")))
promise %...>% head() %...>% print()
})
output$input2 <- renderText({
print("hello")
})
output$distPlot <- renderPlot({
dist <- rnorm(input$bins)
hist(dist)
})
output$range <- renderPrint({
input$slider2
})
}
shinyApp(ui = dashboardPage(dashboardHeader(),
dashboardSidebar(),
ui),
server = server)

The behaviour you're experiencing where the rest of the UI is not loading until the promise is evaluated is expected behaviour. It is explained in the promises package as part of what they call the 'shiny flush cycle' and is described in more detail here and here.
Only after all of the outputs have completed executing are they sent back to Shiny to update the UI. You may expect/prefer outputs to be rendered as soon as they are ready but unfortunately that's not how Shiny operates.
As noted in the second link you can 'trick' shiny into thinking all outputs are executed and then use a reactive value to trigger the final update once the promise has evaluated:
#server part
server <- function(input, output, session) {
data <- reactiveVal()
# Return NULL from this operation so Shiny 'thinks' the output is evaluated
observe({
data(NULL)
future({read.csv("data/sample_large.csv")}) %...>%
data() #Assign to data
NULL
})
# When data() is updated as a side effect of our promise the table will be updated
output$input1 <- renderTable({
req(data()) %>%
head(5) %>%
print()
})
# in the mean time all outputs will be judged to be complete so can be rendered
output$input2 <- renderText({
print("hello")
})
output$distPlot <- renderPlot({
dist <- rnorm(input$bins)
hist(dist)
})
output$range <- renderPrint({
input$slider2
})
}
shinyApp(ui = dashboardPage(dashboardHeader(),
dashboardSidebar(),
ui),
server = server)

Related

Stop shiny renderTable from dimming when updating data

I'm working on a shiny app that streams data and am updating the UI via renderTable every second. When the app renders the table dims between each update which is annoying from a visual perspective. Is there a way to disable this behavior?
output$table_state <- renderTable({
invalidateLater(1000)
get_table_state()
})
If get_table_state() performs a long computation, you can try to execute it outside renderTable(). Notice the use of observe here.
Example app
library(shiny)
library(tidyverse)
long_calculation <- function() {
Sys.sleep(1)
iris
}
ui <- fluidPage(
fluidRow(
column(width = 6,
tableOutput('table_slow')),
column(width = 6, tableOutput('table2')))
)
server <- function(input, output, session) {
df <- reactiveValues(x = NULL)
output$table_slow <- renderTable({
invalidateLater(1000)
long_calculation()
})
iris_no_dim <- observe({
invalidateLater(1000)
df$x <- long_calculation()})
output$table2 <- renderTable({
df$x
})
}
shinyApp(ui, server)

Startup warning with reactive input in shiny module

I am currently modularizing a Shiny app in different modules following the {golem} framework. For simplicity, let's say I have 3 main shiny modules:
mod_faith_plot: generates a scatterplot of a given dataset (I'll use faitfhul).
mod_points_select: decouples a dropdown menu to select how many points will be plotted. UI inputs have this dedicated module as I wanted to place the selector in the sidebarPanel instead of mainPanel (alongside the plot).
mod_data: provides a reactive dataframe depending on the n_points argument.
This modules talk to each other in the server function.
Now, when I start my app with a simple head(., n_points()) in mod_data I get the following warning:
Warning: Error in checkHT: invalid 'n' - must contain at least one non-missing element, got none.
The input in mod_points_select is clearly NULL before the selected_points argument gets assigned, is there a less hacky and more elegant way to avoid the warning at startup than my if condition?
library(shiny)
library(dplyr)
library(ggplot2)
# [Module] Plot faithful data -------------------------------------------------------
mod_faith_plot_ui <- function(id){
ns <- NS(id)
tagList(
plotOutput(ns("faith_plot"))
)
}
mod_faith_plot_server <- function(input, output, session, data){
ns <- session$ns
output$faith_plot <- renderPlot({
data() %>%
ggplot(aes(eruptions, waiting)) +
geom_point()
})
}
# [Module] Module for n_points dropdown ---------------------------------------------
mod_points_select_ui <- function(id){
ns <- NS(id)
uiOutput(ns("select_points"))
}
mod_points_select_server <- function(input, output, session){
ns <- session$ns
output$select_points <- renderUI({
selectInput(
ns("n_points"),
label = "Select how many points",
choices = seq(0, 200, by = 10),
selected = 50
)
})
reactive({input$n_points})
}
# [Module] Get filtered data -----------------------------------------------------------------
mod_data_server <- function(input, output, session, n_points){
ns <- session$ns
data <- reactive({
faithful %>%
# If condition used to avoid warnings at startup - switch lines to get warning
# head(., n_points())
head(., if(is.null(n_points())) { TRUE } else {n_points()})
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
mod_points_select_ui(id = "selected_points")
),
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("plot", mod_faith_plot_ui(id = "faith_plot"))
)
)
)
)
server <- function(input, output, session) {
data <- callModule(mod_data_server, id = "data", n_points = selected_points)
selected_points <- callModule(mod_points_select_server, id = "selected_points")
callModule(mod_faith_plot_server, id = "faith_plot", data = data)
}
shinyApp(ui, server)
You can use req() to ensure values are available:
data <- reactive({
req(n_points())
faithful %>%
head(., n_points())
})
When values are not available the call is silently canceled

Reactive function not being found within app

I have a fairly involved app. When I call a particular eventReactive function, let's call it function A, within a reactive expression, I get an error that function A cannot be found.
I'm unable to reproduce the exact app because it is proprietary, but I did create a dummy app that simulates the setup I have. I realize that there must be some difference between the dummy app and what I actually have, but I can't figure it out. The function in question is there, so I fundamentally don't understand why it's not being found.
library(shiny)
ui <- fluidPage(
sliderInput(inputId = "num",
label = "Choose a number",
value = 25, min = 1, max = 100),
actionButton(inputId = "action",
label = "Update"),
plotOutput("hist"),
verbatimTextOutput("stats")
)
server <- function(input, output) {
data <- eventReactive(input$action, {
input$num*2
})
data2 <- reactive({
data()*2
})
output$stats <- renderPrint({
data2()
})
}
shinyApp(ui = ui, server = server)

Access data created from reactive function to define reactiveValues in Shiny

I'm exploring the possibilities with interactive ggplot2 in shiny. Inspired by this I created a shiny app that exclude points from a dataset and plots the data where the excluded points are of a different color.
app.R
library(shiny)
library(ggplot2)
server<-function(input, output) {
data <- reactive({
set.seed(10)
df=data.frame(x=rnorm(100),y=rnorm(100))
df
})
vals<-reactiveValues(keeprows=rep(TRUE, 100))
output$plot1 <- renderPlot({
df=data()
keep=df[vals$keeprows, ,drop=FALSE]
exclude=df[!vals$keeprows, ,drop=FALSE]
plot=ggplot(data=keep,aes(x,y))+geom_point()+theme_bw()+
geom_point(data=exclude,fill=NA,col="black",alpha=0.75,shape=21)
plot
})
observeEvent(input$plot1_click,{
df=data()
res <- nearPoints(df, input$plot1_click, allRows = TRUE,threshold=5)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
}
ui <- fluidPage(
titlePanel("Reactive test"),
mainPanel(
plotOutput("plot1",click="plot1_click")
)
)
shinyApp(ui = ui, server = server)
This works perfectly, but now I want to be able to define vals with:
vals<-reactiveValues(keeprows=rep(TRUE,nrow(CustomDataInput))
In the case of my example, I tried accessing number of rows from the data created in data():
vals<-reactiveValues(keeprows=rep(TRUE,nrow(data()))
This gives me an error because I tried to access a reactive variable in a non-reactive environment. Is there a way to access the data created in a reactive function to define reactiveValues?
Thank you for your time!
The error pretty much addresses the problem. The correct way to do this is as follows.
library(shiny)
library(ggplot2)
server<-function(input, output) {
vals <- reactiveValues()
data <- reactive({
set.seed(10)
df=data.frame(x=rnorm(100),y=rnorm(100))
vals$keeprows = rep(TRUE,nrow(df))
df
})
#vals<-reactiveValues(keeprows=rep(TRUE,100))
output$plot1 <- renderPlot({
df=data()
keep=df[vals$keeprows, ,drop=FALSE]
exclude=df[!vals$keeprows, ,drop=FALSE]
plot=ggplot(data=keep,aes(x,y))+geom_point()+theme_bw()+
geom_point(data=exclude,fill=NA,col="black",alpha=0.75,shape=21)
plot
})
observeEvent(input$plot1_click,{
df=data()
res <- nearPoints(df, input$plot1_click, allRows = TRUE,threshold=5)
vals$keeprows <- xor(vals$keeprows, res$selected_)
})
}
ui <- fluidPage(
titlePanel("Reactive test"),
mainPanel(
plotOutput("plot1",click="plot1_click")
)
)
shinyApp(ui = ui, server = server)
Declare the vals variable before hand and use that in reactive() function to send variables to vals as shown above. You should be fine.

Measure execution speed inside of Shiny

I am working on developing a Shiny app.
I am interested in clocking the time it takes to execute certain chunks of code (such as a ggplot, etc).
For some reason it appears that using the usual clocking methods don't work within reactive calls, for example:
output$R1_C1 <- renderPlot({
beginning <- Sys.time()
<lots of code here>
end <- Sys.time()
print(end - beginning)
R complains and gives me
Error in (structure(function (input, output) :
object 'beginning' not found
Has anyone found a successful way to time execution speed inside of reactive calls in Shiny?
This works on my system:
library(shiny)
runApp(list(
ui = bootstrapPage(
numericInput('n', 'Number of obs', 100),
plotOutput('plot')
),
server = function(input, output) {
output$plot <- renderPlot({
beginning <- Sys.time()
h <- hist(runif(input$n))
end <- Sys.time()
print(end - beginning)
h
})
}
))
In a similar way, you can use the tictoc package as shown below. The reason I prefer this over the base R solution is because you can easily use multiple tic/toc tags to measure sub-routines within either a reactive or a rendering expression.
library(shiny)
library(tictoc)
runApp(list(
ui = bootstrapPage(
numericInput('n', 'Number of obs', 100),
plotOutput('plot')
),
server = function(input, output) {
output$plot <- renderPlot({
tic("execution time - Histogram")
hist(runif(input$n))
toc()
tic("execution time - Print")
print("this is a second task within 'renderPlot' ")
toc()
})
}
))
The profvis package might be useful. Example:
library(shiny)
library(profvis)
profvis({
sApp <- shinyApp(
ui = fluidPage(
numericInput('n', 'Number of obs', 100, min = 1, max = 200),
plotOutput('plot')
),
server = function(input, output) {
dfTable <- reactive({
as.data.frame(matrix(rnorm(10 * input$n, mean = 5), ncol = input$n))
})
vMeans <- reactive({
apply(dfTable(), 2, mean)
})
output$plot <- renderPlot({
hist(vMeans())
})
}
)
runApp(sApp)
})

Resources