Reactive Loop in R Shiny - r

I've been stuck on this problem for hours. I tried to create a simpler version of my actual use case to see if I can debug the problem but still having trouble.
My shiny app is below. It takes an input from user, runs it through some calculations until it meets a threshold, then collects the value in a list. It does this for several iterations (10 here in the example), then plots a histogram of these values.
The specific error I'm getting says
Warning: Error in : evaluation nested too deeply: infinite recursion / options(expressions=)?
I think that my error is related to the way I'm using x.new, but I can't figure out how to code it differently.
# install.packages('shiny')
library(shiny)
ui <- fluidPage(
headerPanel('Title Here'),
sidebarPanel(
numericInput('x.qty', 'Pick a number', 3, 1, 10)),
mainPanel(plotOutput('valueplot'))
)
server <- function(input, output, session) {
results <- reactive({
x.list <- list()
for (i in 1:10){
x.new <- reactive({input$x.qty * sample(1:10, 1)})
repeat{
if (x.new() > 20){
x.list <- c(x.list, x.new())
results <- x.list
break
} # end if loop
x.new <- reactive({x.new() * sample(1:10, 1)})
} # end repeat
} # end for loop
results
})
output$valueplot <- renderPlot({hist(as.numeric(results()))})
}
shinyApp(ui = ui, server = server)

I would suggest to modify your server function in the following way:
server <- function(input, output, session) {
results <- reactive({
x.list <- list()
for (i in 1:10){
x.new <- input$x.qty * sample(1:10, 1)
repeat{
if (x.new > 20){
x.list <- c(x.list, x.new)
results <- x.list
break
} # end if loop
x.new <- x.new * sample(1:10, 1)
} # end repeat
} # end for loop
results
})
output$valueplot <- renderPlot({hist(as.numeric(results()))})
}
This way you avoid the infinite recursion provoked by
x.new <- reactive({x.new() * sample(1:10, 1)})

Does this work as you want? Each for loop iteration, x.new is calculated using x.qty then in the repeat loop x.new recursively multiplies itself by a random number until it is greater than 20.
library(shiny)
ui <- fluidPage(headerPanel('Title Here'),
sidebarPanel(numericInput('x.qty', 'Pick a number', 3, 1, 10)),
mainPanel(plotOutput('valueplot')))
server <- function(input, output, session) {
x.new <- function(x) {
x * sample(1:10, 1)
}
results <- reactive({
x.list <- list()
for (i in 1:10) {
x.new_draw <- x.new(input$x.qty)
repeat {
if (x.new_draw > 20) {
x.list <- c(x.list, x.new_draw)
break
} # end if loop
x.new_draw <- x.new(x.new_draw)
} # end repeat
} # end for loop
x.list
})
output$valueplot <- renderPlot({
hist(as.numeric(results()))
})
}
shinyApp(ui = ui, server = server)

Related

Loop over data.frame and display result immediately in DT

I have a simple shiny app that holds a dataset as a reactive value.
Once a button is pressed, a function should be applied to each row and the result is added as another variable to that dataset.
The dataset is also shown as a DT.
The result variable should be rendered as soon as the computation for that row is finished.
At the moment, the loop/apply that applies the function to each row finishes and only afterwards the results are displayed.
As the function can run for a long time, I want the DT to be updated as soon as a run is finished, not when all runs finish.
I understand that this means I need to use promises/future so that the main shiny code block spawns new processes which do not block in this case the main thread from updating the values. Correct?
However, I am not able to get it to work.
Here is a small MWE using a simple for loop
library(shiny)
library(DT)
ui <- fluidPage(
actionButton("run", "RUN"),
hr(),
DT::dataTableOutput("table")
)
calc_fun <- function(val) {
Sys.sleep(0.5)
val * 10
}
server <- function(input, output, session) {
set.seed(123)
data_res <- reactiveVal(data.frame(id = 1:10, val = rnorm(10), val10 = NA))
observe({
for (i in seq(nrow(data_res()))) {
print(paste("Looking at row", i))
d <- data_res()
d[i, "val10"] <- calc_fun(val = d[i, "val"])
data_res(d)
}
}) %>% bindEvent(input$run)
# This should be rendered whenever a round in the for-loop has finished
# at the moment it is only run once the loop is finished
output$table <- DT::renderDataTable(data_res())
}
shinyApp(ui, server)
Thanks to #ismirsehregal, I came up with the following solution which uses futures to start the calculation in the background, which in turn write the current status to a file.
Shiny then reactively reads the file and updates the values.
The full MWE looks like this:
library(shiny)
library(DT)
library(future)
library(promises)
library(qs) # for fast file read/write, replace with csv if needed
plan(multisession)
ui <- fluidPage(
actionButton("run", "RUN"),
hr(),
textOutput("prog"),
uiOutput("status"),
hr(),
fluidRow(
column(6,
h2("Current Status"),
DT::dataTableOutput("table")
),
column(6,
h2("Data in File"),
tableOutput("file_data")
)
)
)
calc_fun <- function(val) {
Sys.sleep(runif(1, 0, 2))
val * 10
}
# main function that goes through the rows and starts the calculation
# note that the output is saved to a .qs file to be read in by another reactive
do_something_per_row <- function(df, outfile) {
out <- tibble(id = numeric(0), res = numeric(0))
for (i in seq(nrow(df))) {
v <- df$val[i]
out <- out %>% add_row(id = i, res = calc_fun(v))
qsave(out, outfile)
}
return(out)
}
# create a data frame of tasks
set.seed(123)
N <- 13
tasks_init <- tibble(id = seq(N), val = round(rnorm(N), 2), status = "Open", res = NA)
server <- function(input, output, session) {
# the temporary file to communicate over
outfile <- "temp_progress_watch.qs"
unlink(outfile)
data <- reactiveVal(tasks_init) # holds the current status of the tasks
data_final <- reactiveVal() # holds the results once all tasks are finished
output$prog <- renderText(sprintf("Progress: 0 of %i (0.00%%)", nrow(data())))
output$status <- renderUI(div(style = "color: black;", h3("Not yet started")))
# on the button, start the do_something_per_row function as a future
observeEvent(input$run, {
# if a file exists => the code runs already
if (file.exists(outfile)) return()
print("Starting to Run the code")
output$status <- renderUI(div(style = "color: orange;", h3("Working ...")))
d <- data()
future({do_something_per_row(d, outfile)}, seed = TRUE) %...>% data_final()
print("Done starting the code, runs now in the background! freeing the session for interaction")
# return(NULL) # hide future
})
observe({
req(data_final())
output$status <- renderUI(div(style = "color: green;", h3("Done")))
print("All Done - Results came back from the future!")
})
output$file_data <- renderTable(req(df_done()))
output$table <- DT::renderDataTable({
# no need to fire on every refresh, this is handled automatically later
DT::datatable(isolate(data())) %>%
formatStyle("status", color = styleEqual(c("Open", "Done"), c("white", "black")),
backgroundColor = styleEqual(c("Open", "Done"), c("red", "green")))
})
dt_proxy <- DT::dataTableProxy("table")
# look for changes in the file and load it
df_done <- reactiveFileReader(300, session, outfile, function(f) {
r <- try(qread(f), silent = TRUE)
if (inherits(r, "try-error")) return(NULL)
r
})
observe({
req(df_done())
open_ids <- data() %>% filter(status == "Open") %>% pull(id)
if (!any(df_done()$id %in% open_ids)) return()
print(paste("- new entry found:", paste(intersect(df_done()$id, open_ids), collapse = ", ")))
rr <- data() %>% select(-res) %>% left_join(df_done(), by = "id") %>%
mutate(status = ifelse(is.na(res), "Open", "Done"))
data(rr)
DT::replaceData(dt_proxy, rr)
# replace the progress text
txt <- sprintf("Progress: % 4i of % 4i (%05.2f%%)",
nrow(df_done()), nrow(data()), 100 * (nrow(df_done()) / nrow(data())))
output$prog <- renderText(txt)
})
}
shinyApp(ui, server)
or as a picture:

Pass reactive object as input to function in a shiny app

In the shiny app below I want to pass as input in the initialize_NN() function the layers() which is a reactive object. But when I try to pass it as layers() instead of layers I get an error in my console like unexpected token'layers',expected RPAREN. Any solution to this or any workaround?The initialize_NN() do not expect to give something now but I just want to know how could I pass reactive input into it.
library(shiny)
library(dplyr)
### user inter phase
ui <- fluidPage(
### App title ----
titlePanel(
#title=div(img(src="pics/IRP_NHSc.jpg", width="99%"))
title="Network App"
)
,
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
sliderInput("lay","Adjust layers",min=2,max = 6,value = 2,step=1),
sliderInput("neu","Adjust Neurons",min=10,max = 60,value = 50,step=1)
),
### Main panel for displaying outputs ----
mainPanel(
)
)
)
#### Server
server <- function(input, output, session) {
layers <- reactive({
l<-c(1, rep(input$neu, input$lay), 1)
})
initialize_NN <- function(layers()){
weights <-c()
biases <- c()
num_layers <- length(layers())-1
for (i in seq(num_layers)){
W <- xavier_init(c(layers()[i], layers()[i+1]))
weights <- c(weights, W)
biases <- c(biases, b)
}
return(list(Weights = weights, Biases = biases))
}
obj <- initialize_NN(layers=layers())
weights <- obj$Weights
biases <- obj$Biases
neural_net <- function(x_, weights, biases){
num_layers <- length(weights) + 1
H <- x_
for (l in seq(num_layers-2)){
W <- weights[[l]]
b <- biases[[l]]
H <- tf$nn$tanh(tf$add(tf$matmul(H, W), b))
}
W <- tail(weights, n=1)[1]
b <- tail(biases, n=1)[1]
Y <- tf$add(tf$matmul(H, W), b)
return(Y)
}
}
shinyApp(ui = ui, server = server)
Define function initialize_NN() with "normal" arguments:
initialize_NN <- function(layers){
weights <-c()
biases <- c()
num_layers <- length(layers)-1
for (i in seq(num_layers)){
W <- xavier_init(c(layers[i], layers[i+1]))
weights <- c(weights, W)
biases <- c(biases, b)
}
return(list(Weights = weights, Biases = biases))
}
Then wherever you call the function pass your reactive as the argument layers:
initialize_NN(layers = layers())
Edit 1:
As #jpdugo17 pointed out in the comments, it needs to be done inside a reactive context: whether a reactive(), observe(), observeEvent() etc.
For example:
r_NN <- reactive({
initialize_NN(layers = layers())
})

Shiny - runif function inside reactive loop

hope someone can help with this one:
My code below was returning a dinamic table as I intend, but when I created the for loop to assign individually random numbers to each [k,p] position in the table all the calculations stop and I see an empty UI.
If instead of
rv$MCProbTable[[k,p]] = round(as.numeric(stats::runif(1,0,100)), 3)
I use for example
rv$MCProbTable[[k,p]] = 2
I see "2" allocated in each position in the table, what is close to what I want and shows that everything is working up to the runif application. So is it an issue with the function? Or most likely it is the reactive for loop that isn't set properly?
Thanks and hope this is an easy one!
## LIBRARIES
library(tidyverse)
library(stats)
library(data.table)
library(triangle)
library(base)
library(matrixStats)
library(ggplot2)
library(ggthemes)
library(readxl)
library(httr)
library(writexl)
library(shiny)
ExcelTemplate <- tempfile(fileext = ".xlsx")
GET(url = "https://www.openmontecarlo.com/SampleS.xlsx",write_disk(ExcelTemplate))
defaultRR <- read_xlsx(ExcelTemplate)
## DEFINE UI
ui <- fluidPage(
numericInput("ISvModels", label = h3("Select how many scenarios to run in the model:"),value=10,min=5,max=10000),
hr(),
mainPanel(
fluidRow(
p("vModels"),
textOutput("vModelsText"),
p("vtModels"),
textOutput("vtModelsText"),
p("Scenarios"),
textOutput("SnScenarios"),
h3(strong("ProbTable")),
br(),
dataTableOutput("SMCProbTable"),
hr()
)
)
)
server <- function(input, output, session) {
## CREATE DEFAULT RR AND REACTIVE VARIABLES
rv <- reactiveValues(
vModels = 10,
nScenarios = 10,
vtModelScenarios = NULL,
RRDT = data.frame(),
nRisks = 1,
MCProbTable = data.frame(),
p = 1,
k = 1
)
## PROCESS MODELS INPUT
observe({rv$vModels <- as.numeric(unlist(input$ISvModels))})
output$vModelsText <- renderPrint({unlist(rv$vModels)})
qtModels <- reactive({length(unlist(rv$vModels))})
output$qtModelsText <- renderPrint({unlist(qtModels())})
vtModels <- reactive({paste0("M",1:qtModels()," n = ",rv$vModels," scenarios")})
output$vtModelsText <- renderPrint({unlist(vtModels())})
## RR TABLE
observe({rv$nRisks <- nrow(defaultRR)})
###############################################################################################################
## CALCULATE SCENARIOS - CREATE DATA TABLES AND TEXT VECTORS FOR EACH SCENARIO
## Vectors for SINGLE scenario
observe({rv$nScenarios <- rv$vModels})
observe({rv$vtModelScenarios <- paste0("Scenario ",1:rv$nScenarios,"/",rv$vModels)})
## CREATE PROBABILITY TABLE AND CALCULATE PROBABILITIES
observe({rv$MCProbTable <- data.frame(matrix(nrow=rv$nRisks,ncol=rv$nScenarios))})
observe({rownames(rv$MCProbTable) <- rv$RRDT$ID})
observe({colnames(rv$MCProbTable) <- rv$vtModelScenarios})
observe({
for (p in 1:rv$nScenarios){
for (k in 1:rv$nRisks){
rv$MCProbTable[[k,p]] = round(as.numeric(stats::runif(1,0,100)), 3)
}
}
})
output$SMCProbTable <- renderDataTable({rv$MCProbTable})
}
# Run the app ----
shinyApp(ui = ui, server = server)
In your for loop, use isolate()
for (p in 1:rv$nScenarios){
for (k in 1:rv$nRisks){
isolate(
rv$MCProbTable[k,p] <- round(as.numeric(stats::runif(1,0,100)), 3)
)
}
}

Challenge with dynamically generated, interactive R Shiny plots (mostly functioning code)

Below is the minimum code. It works, but there is a weird problem. Here is what works:
User can select a number of plots (default is 3).
User can click in a plot and have that value represented (partly works).
Steps to reproduce the "partly works":
At launch, click in plot #3, no problem.
Click in plot #2, nothing happens.
Reduce the number of plots from 3 to 2 and then back to 3.
Click in plot #2, now it works.
Click in plot #1, nothing happens.
Reduce the number of plots from 3 to 1 and then back to 3.
Click in plot #1, now it works.
If you reload the app, and start with step 6 above, all plots are interactive as expected.
rm(list=ls())
library(shiny)
#
# Dynamic number of plots: https://stackoverflow.com/questions/26931173/shiny-r-renderplots-on-the-fly
# That can invalidate each other: https://stackoverflow.com/questions/33382525/how-to-invalidate-reactive-observer-using-code
#
ui <- (fluidPage(sidebarLayout(
sidebarPanel(
numericInput("np", "Plots:", min=0, max=10, value=3, step=1)
)
,mainPanel(
fluidRow(uiOutput("plots"))
)
)))
server <- function(input, output, session) {
val <- reactiveValues()
dum <- reactiveValues(v=0)
obs <- list()
### This is the function to break the whole data into different blocks for each page
plotInput <- reactive({
print("Reactive")
np <- input$np
for(i in 1:np) {
cx <- paste0("clk_p",i); dx <- paste0("dbl_p",i); px <- paste0("p",i)
obs[[cx]] <- observeEvent(input[[cx]], {
req(input[[cx]]); val[[px]] <- input[[cx]]$x; dum$v <- dum$v+1; print(paste("Dum",dum$v))
})
obs[[dx]] <- observeEvent(input[[dx]], {
req(input[[dx]]); val[[px]] <- NULL
})
}
return (list(np=np))
})
##### Create divs######
output$plots <- renderUI({
print("Tag plots")
pls <- list()
for(i in 1:plotInput()$np) {
pls[[i]] <- column(4,
plotOutput(paste0("p",i), height=200, width=200
,click=paste0("clk_p",i)
,dblclick=paste0("dbl_p",i))
)
}
tagList(pls)
})
observe({
print("Observe")
lapply(1:plotInput()$np, function(i){
output[[paste("p", i, sep="") ]] <- renderPlot({
print(paste("Plot",dum$v))
x <- val[[paste0("p",i)]]
x <- ifelse(is.null(x),"NA",round(x,2))
par(mar=c(2,2,2,2))
plot(x=runif(20), y=runif(20), main=i, xlim=c(0,1), ylim=c(0,1), pch=21, bg="gray", cex=1.5)
if(is.numeric(x)) abline(v=x, col="blue")
rm(x)
})
})
})
}
shinyApp(ui, server)
Here is a working version of what you're trying to do:
library(shiny)
ui <- fluidPage(
sidebarPanel(
numericInput("num", "Plots:", 3)
),
mainPanel(
uiOutput("plots")
)
)
server <- function(input, output, session) {
obs <- list()
val <- reactiveValues()
observe({
lapply(seq(input$num), function(i){
output[[paste0("plot", i) ]] <- renderPlot({
click_id <- paste0("clk_p",i);
plot(x = runif(20), y = runif(20), main=i)
if (!is.null(val[[click_id]])) {
abline(v = val[[click_id]], col = "blue")
}
})
})
})
observe({
lapply(seq(input$num), function(i){
id <- paste0("clk_p",i);
if (!is.null(obs[[id]])) {
obs[[id]]$destroy()
}
val[[id]] <- NULL
obs[[id]] <<- observeEvent(input[[id]], {
cat('clicked ', id, ' ', input[[id]]$x, '\n')
val[[id]] <- input[[id]]$x
}, ignoreInit = TRUE)
})
})
output$plots <- renderUI({
lapply(seq(input$num), function(i) {
id <- paste0("plot", i)
plotOutput(id, height=200, width=200, click=paste0("clk_p",i))
})
})
}
shinyApp(ui,server)
A few main pointers for anyone who sees this in the future:
The main issue with the original code was that all the observers were registering only for the last ID. This is a bit of an advanced concept and has to do with the way environments in R work and because they were created in a for loop. The fix for this is to use lapply() instead of a for loop to create the observers
Another issue is that obs was overwriting the observers in the list, but the previous observers still exist and can still fire, so I added logic to destroy() existing observers.
One of the most important rules in shiny is to not place side effects inside reactives (plotInput has side effects) so I rewrote the code in a way that avoids that

R Shiny, working with reactive dataframes - NAs

I'm trying to populate a data.frame/matrix based on some user-defined rules. I managed to create a function in R, but am stuck trying to replicate this as a Shiny app [it's my first time using Shiny, and I'm an idiot to start with this one]
This is the crux of the code in regular r-script -
user-inputs are: size (1~3), changes (1~2) and iterations (10~1000)
school_choice_function<- function(changes, size, iterations )
{
######## 1509
##### List of schools
p<-1
j<-1
k<-1
l<-1
s_list<- rep(0,80)
for (i in 1:80) {
if (i <= 26) {
schl<- paste(LETTERS[p],LETTERS[i],sep = "")
s_list[i]<- schl }
if (i>26 & i<=52) {p<- 2
schl<- paste(LETTERS[p],LETTERS[j],sep = "")
s_list[i]<- schl
j=j+1}
if (i>52 & i<=78) {p<- 3
schl<- paste(LETTERS[p],LETTERS[k],sep = "")
s_list[i]<- schl
k=k+1}
if (i>78 ) {p<- 4
schl<- paste(LETTERS[p],LETTERS[l],sep = "")
s_list[i]<- schl
l=l+1}
}
rm(p,i,j,k,l)
########## Applicant Data
a<- c(2011:2015)
c<- 1:size
d<- 1:changes
y<-0
v<-1
w<-10
mat <- matrix(ncol=5, nrow=(iterations*10))
for(pop in 1:iterations){
for (z in v:w)
{
b<- s_list[(1+y):(8+y)]
e<- rep(0,5)
e[1]<- b[1]
g<- sample(d,1)
h<- sample(2:5,g, replace = FALSE)
f1<- rep(0,length(h))
for(j in 1:g){
for(i in 1:length(h))
{
f<- sample(c, 1)
f1[i]<- paste(sample(b,f,replace = FALSE),collapse = ",")
e[h[i]]<- f1[i]
}
}
for(i in c(which(e %in% 0))){
e[i]<- e[i-1]
}
mat[z,]<- e
y<-y+8
}
v<- w+1
w<- w+10
y<-0
}
df<- data.frame(mat,stringsAsFactors = FALSE)
colnames(df)<- c("2011","2012","2013","2014","2015")
return(df)
}
Ignoring the use of worst-practices in coding (I've just learnt to think in terms of loops), I'm using this is a shiny app like this. "s_list/schools" is a character matrix with 80 elements, created before this code.
Just so you get an intuition of what on earth is this - basically it is applicant data over 5 years, who may or many not get assigned to alternatives over time, (based on the rules which comes through in the loops).
The code works in the current form - except the output table is full of NAs.... Any kind of help would be a step up from where I'm at!
ui<- fluidPage(
numericInput(inputId="Changes", label="Changes", value=1, min = 1, max = 3, step = 1),
numericInput(inputId="Size", label="Size", value=2, min = 1, max = 3, step = 1),
numericInput(inputId="Iterations", label="Iterations", value=10, min = 10, max = 1000, step = 10),
tableOutput("dframe")
)
server<- function(input,output) {
Changes<- reactive({input$Changes})
Size<- reactive({input$Size})
Iterations<- reactive({input$Iterations})
schools<- s_list
########## Applicant Data
a<- c(2011:2015)
cc<- reactive(1:(Size()))
d<- reactive(1:(Changes()))
y<-0
v<-1
w<-10
mat <- reactive(matrix(ncol=5, nrow=((input$Iterations)*10)))
pop<- 0
z<- 0
i<- 0
j<- 0
this<- reactive({
for(pop in 1:(Iterations())){
for (z in v:w)
{
b<- schools[(1+y):(8+y)]
e<- rep(0,5)
e[1]<- b[1]
g<- reactive(sample(d(),1))
h<- reactive(sample(2:5,g(), replace = FALSE))
f1<- reactive(rep(0,length(h())))
for(j in 1:g()){
for(i in 1:length(h()))
{
f<- reactive(sample(cc(), 1))
f1()[i]<- reactive(paste(sample(b,f(),replace = FALSE),collapse = ","))
e[h()[i]]<- f1()[i]
}
}
for(i in cc()(which(e %in% 0))){
e[i]<- e[i-1]
}
mat()[z,]<- e
y<-y+8
}
v<- w+1
w<- w+10
y<-0
}
})
df<- reactive(data.frame(mat(),stringsAsFactors = FALSE))
output$dframe <- renderTable({ df() })
}
shinyApp(ui= ui, server = server)
Instead of writing the whole code of the function school_choice_function inside your server why don't you define the function outside your server and just call it from inside your server. Something like this:
server<- function(input,output) {
Changes<- reactive({input$Changes})
Size<- reactive({input$Size})
Iterations<- reactive({input$Iterations})
df<- reactive({
df <- school_choice_function(Changes(), Size(), Iterations())
return(data.frame(df, stringsAsFactors = FALSE))
})
output$dframe <- renderTable({ df() })
}

Resources