3

I have a shiny app where I can read a file with a particular format and the app plots the data and then I can interactively annotate the plot. I recently updated the app so that it can consider all of the files in a directory in a list and then with the use of a 'next' button I can flick between the files and annotate them one by one:

filed=list.files()
library(shiny)
ui <- fluidPage(
  actionButton("nex","next"),
  ......
  column(6,plotOutput("plot", click = "plot_click1")),
  ......
  column(1,actionButton("submit1","add to list")),
  column(6,actionButton("write results to file","write"))
)

server <- function(input, output) {
  value= reactiveVal(1)

  observeEvent(input$back, {
    newValue <- value() - 1     # newValue <- rv$value - 1
    value(newValue)             # rv$value <- newValue
  })

  observeEvent(input$nex, {
    newValue <- value() + 1     # newValue <- rv$value + 1
    value(newValue)             # rv$value <- newValue
  })

  df <- reactive({
    f=paste(filed[value()])
    df=read.table(f,header=F)
    df
  })      

  click_saved1 <- reactiveValues(singleclick = NULL)
  observeEvent(eventExpr = input$plot_click1, handlerExpr = { click_saved1$singleclick <- input$plot_click1 })

  rv=reactive({
    if(input$nex){
    m=data.frame(x=0,y=0)
    }else{
      m=data.frame(x=0,y=0)
      }
    })
  observeEvent(input$submit1, {
    if (input$submit1 > 0) {
      rv$m <- rbind(rv$m,unlist(click_saved1$singleclick))
    }
  })
  output$plot<- renderPlot({
    df=df()
    rv=rv()
    x<-df$distance
    y<-df$frequency
    s=ceemdan(y, ensemble_size = 1000)
    par(mar=c(5,5,5,5))
    plot(x,y)
    points(rv$m$x[-1],rv$m$y[-1], pch=16, col="red", cex=1)
  })

  output$lin=renderPlot({
    rv=rv()
    x<-df$distance
    y<-df$frequency
    m=as.data.frame(cbind(x=seq(1,nrow(rv$m)-1),y=rv$m$x[-1]))
    fit=lm(y~x, data=m)
    plot(m$x,m$y)
    abline(fit)
    legend("topleft", bty="n", legend=paste("NRL:",round(coef(fit)[-1], digits = 2), "Error:",round(summary(fit)$coefficients[-1 , 2]), digits=2),cex=1.2)
  })
  observeEvent(eventExpr = input$write, handlerExpr = { 
    df=df()
    rv=rv()
    x<-df$distance
    y<-df$frequency

    write.table(paste(filed[value(),],round(coef(fit)[-1], digits = 2), 
                      round(summary(fit)$coefficients[-1 , 2]),sep='\t'),'NRLs.txt',append = T,quote=F,col.names=F,row.names=F)
  })
}
shinyApp(ui, server)

The plot annotation involves clicking the plot at points that I find interesting and these points' locations are given to a dataframe reactively:

ui.R

 column(1,actionButton("submit1","add to list")),

server.R

 rv=reactiveValues(m=data.frame(x=0,y=0))
 observeEvent(input$submit1, {
   if (input$submit1 > 0) {
       rv$m <- rbind(rv$m,unlist(click_saved1$singleclick))
     }
   })

My problem however: is that when I click the 'next' button to move to the next file that needs annotation, the reactive data frame does not reset to data.frame(x=0,y=0). This is needed as each file is different. Hence I tried to rectify this with:

   rv=reactiveValues({
     input$nex
     m=data.frame(x=0,y=0)})

This causes the app to immediately crash and returns the error:

Error in .getReactiveEnvironment()$currentContext() : 
  Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)

I also tried:

rv=reactive({
    if(input$nex){
    m=data.frame(x=0,y=0)
    }else{
      m=data.frame(x=0,y=0)
      }
    })
 observeEvent(input$submit1, {
   if (input$submit1 > 0) {
       rv$m <- rbind(rv$m,unlist(click_saved1$singleclick))
     }
   })

And then I specified that 'rv=rv()' in each of the server sections of code.

In this case the plots render but when I select a point on the plot and click the 'add to list' button the app crashes with the error:

Warning: Error in seq.default: 'to' must be of length 1

This is a technical error where I use the seq command to create another data frame in a portion of code- 'm=as.data.frame(cbind(x=seq(1,nrow(rv$m)),y=rv$m$x))'

However this piece of code works fine when I return to normal and the reactive dataframe 'rv' is not dependent on the next button...

I think that adding points to the dataframe via the 'add to list' button is conflicting with the rv object's dependency on the 'next' button.

Can anyone help?

doyle
  • 41
  • 1
  • 6

1 Answers1

1

If you need for a reactive dataframe/ object to be reset each time that you click a button, one can achieve this as follows:

  rv=reactiveValues(m=data.frame(x=0,y=0))

  observe({ if (input$button == 0) 
    return()
    rv$m <- data.frame(x=0,y=0)
  })

And so each time the plot is clicked on and the 'add to list' button is clicked- the data frame is given a new row. And when next is clicked the data frame is reset.

ui.R

  plotOutput("plot", click = "plot_click1")

server.R

  click_saved1 <- reactiveValues(singleclick = NULL)
  observeEvent(eventExpr = input$plot_click1, handlerExpr = { click_saved1$singleclick <- input$plot_click1 })

  rv=reactiveValues(m=data.frame(x=0,y=0))

  observeEvent(input$submit1, {
    if (input$submit1 > 0) {
      rv$m <- rbind(rv$m,unlist(click_saved1$singleclick))
    }
  })

  observe({ if (input$button == 0) 
    return()
    rv$m <- data.frame(x=0,y=0)
  })
doyle
  • 41
  • 1
  • 6