This question already has an answer here:
Can I let Shiny wait for a longer time for numericInput before updating?
(1 answer)
Closed 5 years ago.
In my app I've got an output depending on many inputs. Whenever one of the inputs is changed shiny refreshes the output which takes some long time. The problem occurs when I want to change more than one input, because I have to wait X times to get correct output. Is there a way to break the refreshing of the reactive/output if another input was changed?
In this simple example:
output$distPlot depends on input$bins and input$col. Every change in inputs takes 3 seconds to refresh a histogram, so when I want to change both of them I have to wait 6 seconds. What I want to do is break existing refreshing if another input change was made.
ui
library(shiny)
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
selectInput("col","Color",
choices = c("green","red","blue"),selected = "green")
),
mainPanel(
plotOutput("distPlot")
)
)
))
server
library(shiny)
shinyServer(function(input, output) {
output$distPlot <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = input$col, border = 'white')
Sys.sleep(3)
})
})
P.S. submitButton is not an option in my case, I'm looking for an option to reset/break rendering
Thanks to HubertL I found my answer. I had to create a reactive list with all my dependent inputs, and then use debounce on it, this way plot will change only once (if the time of changing the inputs will be less than 3000 mls in this example).
server:
library(shiny)
library(dplyr)
shinyServer(function(input, output,session) {
inputs_change<-reactive({
list(input$bins,input$col)
}) %>% debounce(3000)
output$distPlot <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = inputs_change()[[1]] + 1)
hist(x, breaks = bins, col = inputs_change()[[2]], border = 'white')
Sys.sleep(3)
})
})
Related
I can see how to use actionButton to delay an output, but I haven't seen an example relevant to what I am trying to do, which is delay the start of a defined function that is called within another output.
Simplified for the MRE, let's say I have an output to create the mean of a data set. I have three ways to calculate the mean. One of those ways takes a long time though (simulated here by Method 2). Here is the way it is structured now.
How can I get algo(x) to wait until the button is pressed, then start the calculation and return the value?
library(shiny)
# Define UI
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
radioButtons(inputId = "calc_t",label = "Select Calculation",choices = c("Method 1"=1,"Method 2 (long)"=2,"Method 3"=3)),
actionButton(inputId = "go_algo",label = "Start Algo")
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
textOutput("analyze")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white',
xlab = 'Waiting time to next eruption (in mins)',
main = 'Histogram of waiting times')
})
output$analyze <- renderText({
calc_type<-input$calc_t
x <- faithful[, 2]
if(calc_type==1){
output<-paste("Mean 1 = ",mean(x))
} else if (calc_type==2){
output<-paste("Mean 2 = ",algo(x))
} else if(calc_type==3){
output<-paste("Mean 3 = ",sum(x)/length(x))
}
})
algo<-function(x){
mean_x<-mean(x)
#stuff that would take a long time
output<-mean_x+100
return(output)
}
}
# Run the application
shinyApp(ui = ui, server = server)
I would suggest using an observeEvent for the action button for the function that needs to wait for the button. For this observeEvent a req is required to limit the button to work only for this choice. Then you can use another observeEvent for the the other choices and again limit what is allowed to run without a button click with req.
Here's the updated server code:
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white',
xlab = 'Waiting time to next eruption (in mins)',
main = 'Histogram of waiting times')
})
observeEvent(input$calc_t, {
req(input$calc_t!=2)
output$analyze <- renderText({
calc_type<-input$calc_t
x <- faithful[, 2]
if(calc_type==1){
output<-paste("Mean 1 = ",mean(x))
} else if(calc_type==3){
output<-paste("Mean 3 = ",sum(x)/length(x))
}
})
})
observeEvent(input$go_algo, {
req(input$calc_t==2)
output$analyze <- renderText({
isolate(calc_type<-input$calc_t)
x <- faithful[, 2]
output<-paste("Mean 2 = ",algo(x))
})
})
algo<-function(x){
mean_x<-mean(x)
#stuff that would take a long time
output<-mean_x+100
return(output)
}
}
I have an output that takes user input to select which of a number of calculations to use and results in 5 numbers. That output pushes out the results of this and other calculations as an HTML table. The individual calculations are not particularly complicated, but user selections choose which of many approaches they are using, so I don't really want to replicate all that code in other outputs that are going to use just those 5 numbers.
My thought was to use the double-arrow to make those numbers available to the other outputs (in my case some plots). My goal is to generate graphs from numbers already generated in a different output, however that gets accomplished. I am not attached to the approach below, it is just where I am right now.
I ran into a number of problems just using <<- and tried a lot of things to get it to work. I won't complicate this further with all the things I tried and the problems they created.
The MRE below replicates this by calculating a number in one output that is then to be used in another output. If you enter different numbers of bins, the second output is never triggered to update to the new number. For this MRE I could of course directly use the user input to calculate that number but that is what I am trying to avoid in the real app. I also don't want to use a "Go!" button if I can avoid it since part of the fun is watching how things change in response to your various selections.
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
numericInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
textOutput("binnum")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
a_number<-0
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
a_number<<-bins[2]/5}
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
output$binnum<-renderText({
a_number
})
}
# Run the application
shinyApp(ui = ui, server = server)
Could you just treat bins and a_number as reactive?
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
numericInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
textOutput("binnum")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
# a_number<-0
# generate bins based on input$bins from ui.R
data(faithful)
x <- faithful[, 2]
bins <- reactive({
seq(min(x), max(x), length.out = input$bins + 1)
})
a_number <- reactive({
req(bins())
-bins()[2]/5
})
output$distPlot <- renderPlot({
# draw the histogram with the specified number of bins
hist(x, breaks = bins(), col = 'darkgray', border = 'white', xlab = paste0("a = ", -bins()[2]/5))
})
output$binnum<-renderText({
a_number()
})
}
# Run the application
shinyApp(ui = ui, server = server)
Is it possible for multiple users of the same app to make changes to the same set of reactive values?
This question (handling multiple users simulaneously in an R Shiny app) suggests that multiple users in different sessions can make changes to the same value (by declaring it outside of server() and using <<- instead of <- ) But that is for just plain old values/variables. Is this possible for reactive values?
Ideally, I would like a change made by user A to be immediately reflected in some output viewed by user B.
Here's a minimal working example based on RStudio's default one-file Shiny app:
library(shiny)
slidervalue <- 30
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = slidervalue)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
textOutput('txt')
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
observe({
slidervalue <<- input$bins
})
reactive_slidervalue <- reactivePoll(100, session,
checkFunc = function() { slidervalue },
valueFunc = function() { slidervalue }
)
output$txt <- renderText(reactive_slidervalue())
observe({
updateSliderInput(session, 'bins', value = reactive_slidervalue())
})
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = reactive_slidervalue() + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)
Basically, I am using a global variable (as you and and the post suggested), and then hooked it back into server by using the reactivePoll function to make the external dependency reactive.
Im creating shiny app. for calculating risk score where the user will upload input file and select the input such as ethnic groups, type of calculating score and diseases. After all of the input are selected and file is uploaded, my App. will be run when user click at action button and the output such as graph and dataframe will be shown
Im using observeEvent to control my App for triggering unnecessarily( mulitple handleExpr with one eventExpr), and this is my shorten version of code. Im sorry for my code that is not reproducible.
observeEvent(input$action,{
isolate(system2("bash_script/plink.sh",args = c(input$file$datapath,input$type,input$sum_stat,input$Disease,input$Ethnic,input$Ref)))
output$table_score <- renderDataTable({
percentile <- read.csv("../output/score_percentile.csv",header = T, sep = "\t")
}, selection = "single")
output$table_variant <- renderDataTable({
varaints_in_sample <- fread("../output/summary.csv", header = T, drop = 1)
})
#Plot Graph
output$plot <- renderPlot({
s <- input$table_score_cell_clicked
plot("../output/score_percentile_plot.csv",s,"analysis")
})
})
my problem is that when Im running app for the first time, everything is controllable. However, if I want to select new input. for example im changing input disease from heart disease to another disease. my App. will be triggered unnecessarily although I did NOT click at action button.
So, Is there any way to use observeEvent with one evenExpr for mulitple handleExpr
Thanks everyone for your help!
I think, this is simplified example of your problem. The solution is to put all your input$... inside isolate().
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
actionButton('action', 'Click')
),
mainPanel(
plotOutput("distPlot")
)
)
)
server <- function(input, output) {
output$distPlot <- renderPlot({
req(input$action)
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = isolate(input$bins) + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
shinyApp(ui = ui, server = server)
I have a Shiny app with a slider input and I would like to set the maximum possible value for the slider based on a maximum value in the dataset uploaded by the user. The max distance will change based on the dataset uploaded.
Here is a minimum working example of what I am trying to do. Below I just hardcode the number for the maxdistance, but in my code it is calculated:
library(shiny)
ui <- fluidPage(
sliderInput("range_one", "Core:", min = 0, max = textOutput("maxdistance"), value = c(0,0))
)
server <- function(input,output) {
output$maxdistance <- renderText({
maxdistance <- 250
return(maxdistance)
})
}
shinyApp(ui=ui,server=server)
I get the following error:
Error in max - min : non-numeric argument to binary operator
Which makes sense because I as asking for a text output, so how do I get this output as a numeric value for use in the sliderInput() function?
Here is an example.
library(shiny)
ui <- shinyUI(fluidPage(
titlePanel("Example"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
actionButton("change", "Change slider max value")
),
mainPanel(
plotOutput("distPlot")
)
)
))
server <- shinyServer(function(input, output, session) {
observeEvent(input$change, {
max = sample(50:100, 1)
updateSliderInput(session, "bins", max=max)
})
output$distPlot <- renderPlot({
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
})
shinyApp(ui = ui, server = server)
Change as follows, it will work:
sliderInput("range_one", "Core:",
min = 0, max = as.integer(textOutput("maxdistance")),
value = c(0,0))
Here is the code I am using on the server side to achieve the desired result of my original question, without the need for an action button:
observe({
infile <- input$file # user input file upload
if(!is.null(infile)) {
processed <- processed() # list of processed elements returned from earlier reactive({}) function with my app
processed_data <- processed$processed_data # get the processed data from the list and save as data frame
maxdistance <- max(processed_data$distance) # calculate the max distance from the processed data
updateSliderInput(session, "range_one", max=maxdistance) # update the slider called "range_one" based on the maxdistance
}
})
This allows the app to use the default maximum slider value until a file is uploaded. Once the user uploads a file, the data is processed and the slider is updated.