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)
})
Related
I'm writing a shiny app, which runs a function over a set of parameters, so I figured I could use multiple cores.
For some reason it can't feed in the variables to the cluster, I get an error: "var_mean" not found. I've tried isolate but that didn't seem to help.
The code below is a very simple example which reproduces the behaviour.
Thanks for any help.
library(shiny)
library(parallel)
ui <- fluidPage(
# Application title
titlePanel("Test parallel in Shiny app"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
numericInput("n","N",value = 100),
numericInput("mean","Mean",value = 1000),
checkboxInput("parallel","Parallel?",value=FALSE)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
server <- function(input, output) {
simulate <- reactive({
mean = input$mean
sim = rnorm(input$n,mean=mean,sd = 100)
return(sim)
})
p_simulate<-reactive({
cl = makeCluster(detectCores()-1)
var_mean = input$mean
clusterExport(cl,varlist="var_mean")
sim = parSapply(cl,
1:input$n,
function(x) rnorm(1,mean=var_mean,sd = 100)
)
stopCluster(cl)
sim
})
output$distPlot <- renderPlot({
if(input$parallel){
x = p_simulate()
} else x = simulate()
# draw the histogram with the specified number of bins
hist(x)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Your "clusterExport" command is missing the "envir" flag:
clusterExport(cl,varlist="var_mean", envir = environment())
That got it running for me.
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
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)
I'm creating Shiny app and I want to use checkboxGroupInput in order to print out multiple plots. However, I want to print out plots only for the elements of checkboxGroupInput that were checked. There is a similar example in Shiny gallery to create UI elements in a loop that uses lapply. Here is a simplified version of that example to show what I want to do:
#server.R
library(shiny)
library(ggplot2)
shinyServer(function(input, output, session) {
numberInput <- reactive({
input$checkbox
})
lapply(1:10, function(i) {
output[[paste0('b', i)]] <- renderPlot({
qplot(x = rnorm(100, mean = as.numeric(numberInput()[i]))) +
ggtitle(paste("This plot was plotted with", numberInput()[i], "option"))
})
})
})
#ui.R
library(shiny)
shinyUI(fluidPage(
title = 'lapply example',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkbox", "Checkbox",
choices = sample(1:10, 5))
),
mainPanel(
lapply(1:10, function(i) {
plotOutput(paste0('b', i))
})
)
)
))
This works, but obviously when Shiny tries to extract numberInput()[i] where i is bigger than number of currently checked elements, there is nothing to extract and instead of a plot there is an error. Therefore I need to somehow tell lapply to iterate only n number of times where n is length(input$checkbox).
I tried to use length(input$checkbox) directly, tried putting that element in the numberInput() reactive statement and returning it as the list, I tried to use reactiveValues() in a following way:
v <- reactiveValues(n = length(input$checkbox))
lapply(1:isolate(v$n), function(i) {
However, in all of those instances Shiny complains about lack of active reactive context.
So, what am I missing? How can I use length of input in lapply outside of reactive context?
I've generally had more luck using this approach (only because it's easier for me to wrap my head around it), but the idea is to render your plots into a UI on the server and then render the UI in ui.R
#server.R
library(shiny)
library(ggplot2)
server <- shinyServer(function(input, output, session) {
output$checks <- renderText(input$checkbox)
output$plots <- renderUI({
plot_output_list <-
lapply(input$checkbox,
function(i){
plotOutput(paste0("plot", i))
})
do.call(tagList, plot_output_list)
})
observe({
for (i in input$checkbox) {
local({
local_i <- i
output[[paste0("plot", local_i)]] <-
renderPlot({
qplot(x = rnorm(100, mean = as.numeric(local_i))) +
ggtitle(paste("This plot was plotted with", local_i, "option"))
})
})
}
})
})
#ui.R
library(shiny)
ui <- shinyUI(fluidPage(
title = 'lapply example',
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkbox", "Checkbox",
choices = sample(1:10, 5))
),
mainPanel(
verbatimTextOutput("checks"),
uiOutput('plots')
)
)
))
shinyApp(ui = ui, server = server)
Minimal working example
Say I want to have a custom version of renderDataTable, which I shall name myRenderDataTable and works by wrapping around renderDataTable:
library(shiny)
runApp(list(
ui = basicPage(
actionButton("button", "Increase input"),
tabsetPanel(
tabPanel("table1", shiny::dataTableOutput("table1")),
tabPanel("table2", shiny::dataTableOutput("table2")),
tabPanel("table3", shiny::dataTableOutput("table3"))
)
),
server = function(input, output) {
myRenderDataTable <- function(a) {
renderDataTable(
data.frame(x = a, y = a^2, z = a^3),
options = list(bPaginate = as.logical(a %% 2))
)
}
output$table1 <- myRenderDataTable(input$button)
output$table2 <- myRenderDataTable(input$button + 1)
output$table3 <- myRenderDataTable(input$button + 2)
}
))
Issue
Unfortunately, it appears that myRenderDataTable is not reactive like renderDataTable. Clicking the Increase input button should cause the table values to change, but doesn't.
So what's going wrong?
Attempt: Passing calls to reactive:
Doing output$table1 <- reactive(myRenderDataTable(input$button))) leads to:
Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)?
Error : evaluation nested too deeply: infinite recursion / options(expressions=)?
Attempt: Passing calls to observe:
Doing observe(output$table1 <- myRenderDataTable(input$button)) had no effect on the issue
The problem is that input$button is evaluated "eagerly" - i.e. input$button + 1 evaluates to 2 to the first time it's run and then never changes again. You can make it evaluate every time input$button changes by explicitly making it a reactive:
library(shiny)
runApp(list(
ui = basicPage(
actionButton("button", "Increase input"),
tabsetPanel(
tabPanel("table1", shiny::dataTableOutput("table1")),
tabPanel("table2", shiny::dataTableOutput("table2")),
tabPanel("table3", shiny::dataTableOutput("table3"))
)
),
server = function(input, output) {
myRenderDataTable <- function(a) {
renderDataTable(
data.frame(x = a(), y = a()^2, z = a()^3),
options = list(bPaginate = as.logical(a() %% 2))
)
}
output$table1 <- myRenderDataTable(reactive(input$button))
output$table2 <- myRenderDataTable(reactive(input$button + 1))
output$table3 <- myRenderDataTable(reactive(input$button + 2))
}
))
I think you're underestimating how much magic goes in in the render* functions. From looking at this example, I don't think you want a custom renderDataTable function, I think you want a custom function to build a table, which you can then pass to the built in renderDataTable. I think this does what you want, the wrapping is just in the opposite order (ie, a custom funciton inside a reactive expression):
library(shiny)
runApp(list(
ui = basicPage(
actionButton("button", "Increase input"),
tabsetPanel(
tabPanel("table1", dataTableOutput("table1")),
tabPanel("table2", dataTableOutput("table2")),
tabPanel("table3", dataTableOutput("table3"))
)
),
server = function(input, output) {
myDataTable <- function(a) {
data.frame(x = a, y = a^2, z = a^3)
}
output$table1 <- renderDataTable(myDataTable(input$button))
output$table2 <- renderDataTable(myDataTable(input$button + 1))
output$table3 <- renderDataTable(myDataTable(input$button + 2))
}
))