Setting a minimum range in a shiny app slider - r

When using a range slider in a shiny app, can you require a minimum range of selected values? I am using the sliderTextInput() function in the shinyWidgets package, but think this is general to range sliders. Toy example code:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
ui <- fluidPage(
plotOutput("plot"),
shinyWidgets::sliderTextInput("range","Input Size:",
choices=c(1,25,50,100),
selected=c(25,50), grid = T)
)
server <- function(input, output) {
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)
The issue I am trying to avoid is the one below, where both ends of a slider are set to the same value, which results in a single point being plotted--I'd like to require a range be selected.

You can update the values if the are the same:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
library(shinyWidgets)
ui <- fluidPage(
plotOutput("plot"),
sliderTextInput("range","Input Size:",choices=sliderchoice,selected=c(25,50), grid = T)
)
server <- function(input, output,session) {
observeEvent(input$range,{
if(input$range[1] == input$range[2]){
updateSliderTextInput(session,"range",selected = c((input$range[1]-1),input$range[2]))
}
})
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)

Related

How to efficiently subset a dataframe in R Shiny?

In the below example code I reactively subset the mtcars dataframe inside the renderPlot() function. However, in my larger App with many render functions in the server section I am having to repeat the same rv$x[1:input$samples], etc., over and over in many places. How would I apply this subsetting instead "at the top", into the rv <- reactiveValues(...) function itself or equivalent "master function"? I tried subsetting inside the reactiveValues() and got the message "Warning: Error in : Can't access reactive value 'samples' outside of reactive consumer. Do you need to wrap inside reactive() or observer()?" I assumed incorrectly that the reactiveValues() function is a "reactive consumer".
If someone can answer this basic understanding question, please explain the logic for correctly subsetting "at the top" because I am getting very embarrassed by my repeated questions about Shiny reactivity.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
output$p <- renderPlot({plot(rv$x[1:input$samples],rv$y[1:input$samples])})
}
shinyApp(ui, server)
There are multiple ways you can handle this.
Here is one way to create new subset reactive values inside observe.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
observe({
rv$x_sub <- rv$x[1:input$samples]
rv$y_sub <- rv$y[1:input$samples]
})
output$p <- renderPlot({plot(rv$x_sub,rv$y_sub)})
}
shinyApp(ui, server)
I'd use reactiveValues only if you need them to be modified in different places.
reactive is shiny's basic solution for this:
library(shiny)
library(datasets)
ui <- fluidPage(
sliderInput(
'samples',
'Nbr of samples:',
min = 2,
max = 32,
value = 16
),
plotOutput("p")
)
server <- function(input, output, session) {
reactive_mtcars <- reactive({mtcars[1:input$samples,]})
output$p <- renderPlot({
plot(reactive_mtcars()$mpg, reactive_mtcars()$wt)
})
}
shinyApp(ui, server)

Restrict slider input min and max values from overlapping (range > 0) [duplicate]

When using a range slider in a shiny app, can you require a minimum range of selected values? I am using the sliderTextInput() function in the shinyWidgets package, but think this is general to range sliders. Toy example code:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
ui <- fluidPage(
plotOutput("plot"),
shinyWidgets::sliderTextInput("range","Input Size:",
choices=c(1,25,50,100),
selected=c(25,50), grid = T)
)
server <- function(input, output) {
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)
The issue I am trying to avoid is the one below, where both ends of a slider are set to the same value, which results in a single point being plotted--I'd like to require a range be selected.
You can update the values if the are the same:
testx=1:150
testy=1:150
library(shiny) # also requires shinyWidgets package be installed
library(shinyWidgets)
ui <- fluidPage(
plotOutput("plot"),
sliderTextInput("range","Input Size:",choices=sliderchoice,selected=c(25,50), grid = T)
)
server <- function(input, output,session) {
observeEvent(input$range,{
if(input$range[1] == input$range[2]){
updateSliderTextInput(session,"range",selected = c((input$range[1]-1),input$range[2]))
}
})
output$plot <- renderPlot({
plot(testx[input$range[1]:input$range[2]],testy[input$range[1]:input$range[2]],
xlim=c(0,150),ylim=c(0,150))
})
}
shinyApp(ui, server)

Is there a way to avoid the flickering between rendering of the plots using shiny recalculate

Is there a way to avoid the flickering between rendering of the plots using shiny recalculate ?
The plots become grey-on-invalidation mechanism (flashing) the moment they are invalidated (which in our case means when values$a has changed), so their current display is not up to date, but they also have not finished recalculating for the new value of values$a. We can speed up recalculation (by parallelizing code), but never completely get rid of it. So removing this mechanism actually removes information from the end user, as you now don't have any way to know if the plot is still up to date or if it's recalculating for the next value... I understand this may be annoying, but I just want to make sure you understand why it's happening.
Appreciate your time for reading this and will be glad if you could suggest some solution.
library("shiny")
library("parallel")
library("pryr")
ui <- basicPage(
plotOutput('plot1')
,plotOutput('plot2')
,plotOutput('plot3')
,plotOutput('plot4')
,plotOutput('plot5')
,plotOutput('plot6')
,plotOutput('plot7')
,plotOutput('plot8')
,plotOutput('plot9')
,plotOutput('plot10')
,plotOutput('plot11')
,plotOutput('plot12')
,plotOutput('plot13')
,plotOutput('plot14')
,plotOutput('plot15')
,plotOutput('plot16')
,plotOutput('plot17')
,plotOutput('plot18')
,plotOutput('plot19')
,plotOutput('plot20')
,plotOutput('plot21')
,plotOutput('plot22')
,plotOutput('plot23')
,plotOutput('plot24')
,plotOutput('plot25')
,plotOutput('plot26')
,plotOutput('plot27')
,plotOutput('plot28')
,plotOutput('plot29')
,plotOutput('plot30')
)
server <- function(input, output) {
values <- reactiveValues(a=1)
observe({
invalidateLater(5000)
doPlot<-rnorm(1)
values$a <- doPlot
print(mem_used())
})
observeEvent(values$a,{
mclapply(1:30,function(i){
output[[paste0("plot",i)]] <- renderPlot({plot(rnorm(50),main=i)})
})
})
}
shinyApp(ui,server)
##################################
library("shiny")
library("parallel")
library("pryr")
ui <- basicPage(
plotOutput('plot1')
,plotOutput('plot2')
,plotOutput('plot3')
,plotOutput('plot4')
,plotOutput('plot5')
,plotOutput('plot6')
,plotOutput('plot7')
,plotOutput('plot8')
,plotOutput('plot9')
,plotOutput('plot10')
,plotOutput('plot11')
,plotOutput('plot12')
,plotOutput('plot13')
,plotOutput('plot14')
,plotOutput('plot15')
,plotOutput('plot16')
,plotOutput('plot17')
,plotOutput('plot18')
,plotOutput('plot19')
,plotOutput('plot20')
,plotOutput('plot21')
,plotOutput('plot22')
,plotOutput('plot23')
,plotOutput('plot24')
,plotOutput('plot25')
,plotOutput('plot26')
,plotOutput('plot27')
,plotOutput('plot28')
,plotOutput('plot29')
,plotOutput('plot30')
)
server <- function(input, output) {
values <- reactiveValues(a=1)
observe({
invalidateLater(5000)
doPlot<-rnorm(1)
values$a <- doPlot
print(mem_used())
})
mclapply(1:30,function(i){
output[[paste0("plot",i)]] <<- renderPlot({values$a
plot(rnorm(50),main=i)
})
})
}
shinyApp(ui,server)
You can change the recalculating opacity via css, e.g. add the following to your UI code:
tags$style(type="text/css",
".recalculating {opacity: 1.0;}"
)
Using your example:
library("shiny")
library("parallel")
library("pryr")
ui <- basicPage(
tags$style(type="text/css",
".recalculating {opacity: 1.0;}"
),
plotOutput('plot1')
,plotOutput('plot2')
,plotOutput('plot3')
,plotOutput('plot4')
,plotOutput('plot5')
,plotOutput('plot6')
,plotOutput('plot7')
,plotOutput('plot8')
,plotOutput('plot9')
,plotOutput('plot10')
,plotOutput('plot11')
,plotOutput('plot12')
,plotOutput('plot13')
,plotOutput('plot14')
,plotOutput('plot15')
,plotOutput('plot16')
,plotOutput('plot17')
,plotOutput('plot18')
,plotOutput('plot19')
,plotOutput('plot20')
,plotOutput('plot21')
,plotOutput('plot22')
,plotOutput('plot23')
,plotOutput('plot24')
,plotOutput('plot25')
,plotOutput('plot26')
,plotOutput('plot27')
,plotOutput('plot28')
,plotOutput('plot29')
,plotOutput('plot30')
)
server <- function(input, output) {
values <- reactiveValues(a=1)
observe({
invalidateLater(5000)
doPlot<-rnorm(1)
values$a <- doPlot
print(mem_used())
})
observeEvent(values$a,{
mclapply(1:30,function(i){
output[[paste0("plot",i)]] <- renderPlot({plot(rnorm(50),main=i)})
})
})
}
shinyApp(ui,server)

conditionalPanel in R/shiny

Quick question on conditionalPanel for shiny/R.
Using a slightly modified code example from RStudio, consider the following simple shiny app:
n <- 200
# Define the UI
ui <- bootstrapPage(
numericInput('n', 'Number of obs', n),
conditionalPanel(condition = "input.n > 20",
plotOutput('plot') ),
HTML("Bottom")
)
# Define the server code
server <- function(input, output) {
output$plot <- renderPlot({
if (input$n > 50) hist(runif(input$n)) else return(NULL)
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
My objective is to hide the graph and move up the HTML text to avoid a gap. Now, you can see that if the entered value is below 20, the graph is hidden and the text "Bottom" is moved up accordingly. However, if the entered value is larger than 20, but smaller than 50, the chart function returns NULL, and while no chart is shown, the text "Bottom" is not moving up.
Question is: is there a way I can set a conditionalPanel such that it appears/is hidden based on whether or not a plot function returns NULL? The reason I'm asking is because the trigger a bit complex (among other things it depends on the selection of input files, and thus needs to change if a different file is loaded), and I'd like to avoid having to code it on the ui.R file.
Any suggestions welcome,
Philipp
Hi you can create a condition for conditionalPanel in the server like this :
n <- 200
library("shiny")
# Define the UI
ui <- bootstrapPage(
numericInput('n', 'Number of obs', n),
conditionalPanel(condition = "output.cond == true", # here use the condition defined in the server
plotOutput('plot') ),
HTML("Bottom")
)
# Define the server code
server <- function(input, output, session) {
output$plot <- renderPlot({
if (input$n > 50) hist(runif(input$n)) else return(NULL)
})
# create a condition you use in the ui
output$cond <- reactive({
input$n > 50
})
outputOptions(output, "cond", suspendWhenHidden = FALSE)
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
Don't forget to add the session in your server function and the outputOptions call somewhere in that function.

Adding more than one graph to mainPanel in Shiny (R)

I am trying to have multiple html outputs in my shiny App but it seems like it can only show one at a time.
My UI is:
# ui.R
shinyUI(
mainPanel(
tableOutput("view"),
plotOutput("view2")
))
And my server is:
# server.R
library(googleVis)
library(RMySQL)
shinyServer(function(input, output) {
datasetInput <- reactive({
"try2" = subset(try1, idCampaign == input$inputId)
})
output$view <- renderGvis({
gvisTable(datasetInput(),options=list(width=1000, height=270, col='blue'))
})
output$view2 <- renderGvis({
gvisScatterChart(datasetInput2())
})
})
in the output to view2 you use datasetInput2() , this should be datasetInput(). Here datasetInput() just represents a dynamic version of a dataframe, you can use it in as many functions as you want, there is no need to index it.
alternatively i think you can use the tabsetPanel to divide your main page into certain parts and assign output objects to each of your tabPanel.

Resources