How to immediately draw a graph and then, by clicking on the button, draw points on it?

#- Dear Friends! Please help me

#- I want to display plot immediately, and then after pressing button, draw points on plot.
#- my problems:
#- first plot is displayed immediately, but does not draw a min point
#- second plot is NOT displayed immediately, only after click button, and I see min point

library(shiny)

#------------------ function for investigation
mfun <- function(x) {
  return((x + 3)^2 * sin(x - 2))
}

#------------------ function for searching min value f(x)
findMin <- function(mini, maxi, f) {
  x <- runif(min = mini, max = maxi, n = 1e+7)
  return(x[which.min(f(x))])
}

#-------------------------
ui <- fluidPage(
   #-- set range for plot
   numericInput(inputId = 'xmin',  label = 'x left',  value = 0),
   numericInput(inputId = 'xmax',  label = 'x right', value = 15),

   #-- button for calc min value
   actionButton(inputId = 'btMin', label = 'find min f(x)'),

   #--- results
   textOutput(outputId = 'xlocmin'),
   textOutput(outputId = 'ylocmin'),
  
   #-- first plot is displayed immediately, but does not draw a min point
   plotOutput(outputId = 'plot_1',  width = '100%',  height = '300px'),

   #-- second plot is NOT displayed immediately, only after press button!
   plotOutput(outputId = 'plot_2',  width = '100%',  height = '300px')
)

#=================================
server <- function(input, output) {
  
  #----- define points for plot() 
  xgr <- reactive({seq(
      from = input$xmin,
      to = input$xmax,
      by = (input$xmax - input$xmin) / 200)
  })

  #----- calc xlocmin in range input$xmin and input$xmax for our function
  xlocmin <- eventReactive(eventExpr = input$btMin,
      {findMin(mini = input$xmin, maxi = input$xmax, f = mfun)})
  
  #-------- calc mfun(xlocmin)
  ylocmin <- reactive({round(mfun(xlocmin()), 3)})

  #------- make output for coordinates point with min value
  output$xlocmin <- renderText({paste0('x_min = ', round(xlocmin(), 3))})
  output$ylocmin <- renderText({paste0('f(x_min) = ', round(ylocmin(), 3))})

  #--------- draw plot without min point
  output$plot_1 <- renderPlot(expr = {
    plot(x = xgr(),
         y = mfun(xgr()),
         type = 'l',
         lwd = 3,
         col = 'red')    
    }, res = 70)
  
  #--------- draw plot with min point
  output$plot_2 <- renderPlot(expr = {
    plot(x = xgr(),
         y = mfun(xgr()),
         type = 'l',
         lwd = 3,
         col = 'red')

    points(x = xlocmin(),
           y = ylocmin(),
           type = 'p',
           pch = 21,
           col = 'blue',
           bg = 'yellow',
           cex = 2)
    },  res = 70)  
  }

shinyApp(ui = ui, server = server)

Hi,

Welcome to the RStudio community!

Thanks for generating a nice reproducible example! This really helps.
I have come up with a solution, though I feel it is a bit of a hack :slight_smile:

library(shiny)

#------------------ function for investigation
mfun <- function(x) {
  return((x + 3)^2 * sin(x - 2))
}

#------------------ function for searching min value f(x)
findMin <- function(mini, maxi, f) {
  x <- runif(min = mini, max = maxi, n = 1e+7)
  return(x[which.min(f(x))])
}

#-------------------------
ui <- fluidPage(
  #-- set range for plot
  numericInput(inputId = 'xmin',  label = 'x left',  value = 0),
  numericInput(inputId = 'xmax',  label = 'x right', value = 15),
  
  #-- button for calc min value
  actionButton(inputId = 'btMin', label = 'find min f(x)'),
  
  #--- results
  textOutput(outputId = 'xlocmin'),
  textOutput(outputId = 'ylocmin'),
  
  #-- first plot is displayed immediately, but does not draw a min point
  plotOutput(outputId = 'plot_1',  width = '100%',  height = '300px'),

)

#=================================
server <- function(input, output) {
  
  #----- define points for plot() 
  xgr <- reactive({seq(
    from = input$xmin,
    to = input$xmax,
    by = (input$xmax - input$xmin) / 200)
  })
  
  #----- calc xlocmin in range input$xmin and input$xmax for our function
  xlocmin <- eventReactive(eventExpr = input$btMin,
                           {findMin(mini = input$xmin, maxi = input$xmax, f = mfun)})
  
  #-------- calc mfun(xlocmin)
  ylocmin <- reactive({round(mfun(xlocmin()), 3)})
  
  #------- make output for coordinates point with min value
  output$xlocmin <- renderText({paste0('x_min = ', round(xlocmin(), 3))})
  output$ylocmin <- renderText({paste0('f(x_min) = ', round(ylocmin(), 3))})
  
  nclicks = reactiveVal(0)
  
  output$plot_1 <- renderPlot(expr = {
   
    plot(x = xgr(),
         y = mfun(xgr()),
         type = 'l',
         lwd = 3,
         col = 'red')
    
    if(input$btMin > isolate({nclicks()})){
      points(x = xlocmin(),
             y = ylocmin(),
             type = 'p',
             pch = 21,
             col = 'blue',
             bg = 'yellow',
             cex = 2)
      nclicks(input$btMin)
    }

  }, res = 70)
  

}

shinyApp(ui = ui, server = server)

I used the fact that when you click a button, the variable actually is a counter on how many times the button has been clicked. This ensures that the min value is only plotted once the button has been clicked, and if you generate a new plot, the min will not be shown until the button is clicked again.

A variable nclicks() keeps track on how many times the button was clicked, and the isolate() function is used to not trigger the reactive environment when it's updated. So if the button is clicked, it values will be 1 higher than the nclicks(). At that time the min is drawn, but the nclicks() is updated too. If the plot now changes (by using the input boxes) the min will not be plotted until the button is clicked again.

Hope this helps,
PJ

Great!
Thank You very much! Now I undestand shiny better! ))

And I have prepared another question with an example, please see it...

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.