R Shiny: Adding to plot via a loop
You can use reactiveTimer
to do that. I have modified the server part of your code. In the code below I have set the timer for two seconds so that the plot updates every two seconds.
server <- function(input, output) {
autoInvalidate <- reactiveTimer(2000)
plot1 <- NULL
output$plot1 <- renderPlot({
plot1 <<- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
plot1 <<- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
plot1
})
observeEvent(input$button,{
output$plot1 <- renderPlot({
autoInvalidate()
data$sampled <- "red"
sample.rows <- sample(data$ID, 20, replace = F)
data$sampled[sample.rows] <- "green"
plot1 <<- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)
sample.mean.x <- mean(data$x[sample.rows])
plot1 <<- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")
plot1
})
})
}
[EDIT]:
As you wanted the loop to be run only 20 times I have modified the code with the help of the answer in this link so that the reactive timer is run only till the count is 20. Here is the code that you need to add from the link:
invalidateLaterNew <- function (millis, session = getDefaultReactiveDomain(), update = TRUE)
{
if(update){
ctx <- shiny:::.getReactiveEnvironment()$currentContext()
shiny:::timerCallbacks$schedule(millis, function() {
if (!is.null(session) && session$isClosed()) {
return(invisible())
}
ctx$invalidate()
})
invisible()
}
}
unlockBinding("invalidateLater", as.environment("package:shiny"))
assign("invalidateLater", invalidateLaterNew, "package:shiny")
Here is the server code for it:
server <- function(input, output, session) {
count = 0
plot1 <- NULL
output$plot1 <- renderPlot({
plot1 <<- ggplot(data, aes(x=x, y=y)) + geom_point(colour="red") + theme_bw()
plot1 <<- plot1 + geom_vline(xintercept = mean(data$x), size=1.1, colour="red")
plot1
})
observeEvent(input$button,{
count <<- 0
output$plot1 <- renderPlot({
count <<- count+1
invalidateLater(1500, session, count < 20)
data$sampled <- "red"
sample.rows <- sample(data$ID, 20, replace = F)
data$sampled[sample.rows] <- "green"
plot1 <<- plot1 + geom_point(x=data$x, y=data$y, colour=data$sampled, size=2)
sample.mean.x <- mean(data$x[sample.rows])
plot1 <<- plot1 + geom_vline(xintercept = sample.mean.x, colour="green")
plot1
})
})
}
Force shiny to render plot in loop
You could nest an observer
into an observeEvent
to make it work. Based on Jeff Allen's code from the SO topic you linked.
Crucial part:
observeEvent(input$run, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
Full code:
library(shiny)
server <- function(input, output, session) {
rv <- reactiveValues(i = 0)
maxIter <- 3
output$myplot <- renderPlot( {
if(rv$i > 0) {
x <- seq_len(rv$i * 100)
y <- (x + 1)^2 - 1 # this will do for now
plot(x, y, main = sprintf("Round %i", rv$i), type = "l")
} else {
plot(1:1, main = "Placeholder")
}
})
observeEvent(input$run, {
rv$i <- 0
observe({
isolate({
rv$i <- rv$i + 1
})
if (isolate(rv$i) < maxIter){
invalidateLater(2000, session)
}
})
})
}
ui <- fluidPage(
actionButton("run", "START"),
plotOutput("myplot")
)
shinyApp(ui = ui, server = server)
update goes into a loop shiny
this should work:
library(shiny)
ymax <- 100
ymin <- 0
ui <- fluidPage(
sidebarPanel(
h3("See"),
numericInput("yinter", "Vertical interval (m)",
min = 0, max = ymax, value = 50, step = 0.5),
numericInput("movepercent", "Scroll interval (%)",
min = 0, max = 100, value = 15, step = 5),
uiOutput("inputs"),
actionButton("up","",icon("arrow-up"),
width = "100%"),
actionButton("down","",icon("arrow-down"),
width = "100%",""),
width=2
),
sidebarPanel(
plotOutput("plot1",height = 800)
)
)
server <- function(input, output, clientData, session) {
ival <- reactiveVal(0)
observeEvent(input$up, {
newval <- ival() + input$yinter*(input$movepercent/100)
ival(newval)
})
observeEvent(input$down, {
newval <- ival() - input$yinter*(input$movepercent/100)
ival(newval)
})
observeEvent(input$heightSlider, {
if(input$heightNumeric != input$heightSlider){
ival(input$heightSlider)
}
})
observeEvent(input$heightNumeric, {
if(input$heightNumeric != input$heightSlider){
ival(input$heightNumeric)
}
})
output$inputs <- renderUI({
newval <- ival()
tagList(
numericInput("heightNumeric", "Height (m)",
min = ymin, max = ymax, value = newval, step = 1),
sliderInput("heightSlider","Height (m)",min = ymin, max = ymax,
value = newval ,step=0.01)
)
})
output$plot1 <- renderPlot({
plot(seq(from=0,to=1,by=0.0001),seq(from=0,to=100,by=0.01),
type="l",ylim=c(ival() - input$yinter/2,
ival() + input$yinter/2))
})
}
shinyApp(ui = ui, server = server)
Plotting graphs in Shiny R; using a repeat loop to plot data for automated analysis
You should take a look at the invalidateLater
or reactiveTimer
functions.
I'm using invalidateLater
in the following example (NOTE: the function takes milliseconds as first argument).
You also shouldn't put any outputs inside an observer, make a reactiveVal
/ reactiveValues
object and fill that at every new interval. You can use that object anywhere in the app then.
I also changed the observeEvent
to a normal observe
, since otherwise it would only trigger when the Button is clicked. Now, the observer triggers, when the Button is clicked, the interval slider is changed and when the interval has passed.
library(shiny)
library(ggplot2)
ui<-fluidPage(
titlePanel('Minimal example'),
tabsetPanel(
tabPanel("Example",
sidebarPanel(width = 4,
h5("The default interval for the analysis refresh is 5 minutes. If you wish to change this, please do so in the box below:"),
numericInput("intervaltime","Input refresh interval:",5),
br(),
h5("Press 'Run Analysis' button below to start automated analysis"),
actionButton("automatedanalysis", "Run Analysis")),
mainPanel(
h4("An example plot"),
plotOutput("example_plot", width = "100%"),
h4("Some text with updated system time"),
textOutput("example_text")
)
)))
server<-function(input,output,session){
rv <- reactiveVal(NULL)
observe({
interval = input$intervaltime*1000
invalidateLater(interval, session)
req(input$automatedanalysis)
print("Fill ReactiveVal")
mpg$hwy <- mpg$hwy * runif(nrow(mpg))
rv(mpg)
})
output$example_plot<-renderPlot({
req(rv())
currenttime<-Sys.time()
print("Plot Code")
ggplot(rv(), aes(displ, hwy, colour = class)) +
geom_point()+ggtitle(paste0("graph made at: ",currenttime))
})
output$example_text<-renderText({
print(paste0("The current system time is: ", Sys.time())) #a check to know that it is working
})
}
shinyApp(ui, server)
R Shiny: how to prevent duplicate plot update with nested selectors?
You can use isolate
to avoid dependency of your histogram on input$category
:
output$histogram <- renderPlot({
cat(paste("hist:", "category =", isolate(input$category),
"value =",input$value, "\n"), file = stderr())
hist(data[data[, isolate(input$category)] == input$value, "height"])
})
Your histogram will no longer be updated when the category changes. Changing the category also changes the value in your app, and the histogram will update when that value is changed.
More info on isolate
here.
Shiny app ploting every few seconds after button press
Well, I guess I'm an Idiot and I found a similar problem discussed here (ofcourse 30 minutes after posting this) and I was able to adapt the code. Hope this helps someone in the future.
library(shiny)
ui <- fluidPage(
shinyjs::useShinyjs(),
tags$style(type="text/css",
".recalculating {opacity: 1.0;}"),
mainPanel(
actionButton("Start","START", width='100%'),
plotOutput("A")
)
)
server <- function(input, output, session) {
par(bg = 'black')
maxIter <- 5
vals <- reactiveValues( A = matrix(nrow=5,ncol =5,sample(3,size =25,replace=T)),counter = 1)
observeEvent(input$Start, {
vals$counter <- 1
shinyjs::disable("Start")
output$A <- renderPlot({
Sys.sleep(0.5)
image(vals$A,xaxt='n', ann=FALSE,yaxt='n',bty="n",asp=1)
})
})
observeEvent(input$Start, {
observe({
isolate({
vals$A <- matrix(nrow=5,ncol =5,sample(3,size =25,replace=T))
vals$counter <- vals$counter + 1 #for loop
})
if (isolate(vals$counter) <= maxIter)
invalidateLater(0, session)
})
})
observe({vals$counter
if (vals$counter > maxIter)
shinyjs::enable("Start")
})
}
shinyApp(ui = ui, server = server)
Related Topics
How to Define Fill Colours in Ggplot Histogram
How to Change the Default Directory in Rstudio (Or R)
Update Plot Within Observer Loop in Shiny Application
How to Convert Unix Timestamp (Milliseconds) and Timezone in R
R Specify Function Environment
Highlight a Line in Ggplot with Multiple Lines
R Windows Os Choose.Dir() File Chooser Won't Open at Working Directory
Does White Space Slow Down Processing
How to Prep Transaction Data into Basket for Arules
Adding a Simple Lm Trend Line to a Ggplot Boxplot
Write.Csv() a List of Unequally Sized Data.Frames
The Representation of an Empty Argument in a "Call"
Print a List of Dynamically-Sized Plots in Knitr
Why Are the Colors Wrong on This Ggplot